Skip to the content.

WWDC 2026 quietly did something that matters more than the Siri headlines: it opened Apple’s Foundation Models framework to almost any language model — including open-source MLX models pulled straight from Hugging Face. Here’s how to use it, with code you can run in the Xcode 27 beta today.


The one announcement that changes how you build AI on Apple platforms

At WWDC 2025, Apple gave Swift developers the Foundation Models framework: a three-line API to call the on-device model behind Apple Intelligence. It was useful but closed — you got Apple’s model, and only Apple’s model.

WWDC 2026 broke that open. The framework now sits behind a LanguageModel protocol, and Apple shipped several conforming backends you can drop in interchangeably: the on-device system model, Private Cloud Compute, a Core AI option for custom weights, and — the one this article is about — an MLXLanguageModel that runs open-source models from the Hugging Face MLX community directly on your Mac’s GPU and Neural Engine.

The punchline: you can point Apple’s LanguageModelSession at mlx-community/some-model and use the exact same Swift code you’d use for Apple’s own model. Tool calling, structured output, streaming — all of it works the same regardless of which brain is behind the session.

This is a big deal for three reasons. You’re no longer capped by Apple’s model quality ceiling. You can pick a model sized for your task instead of taking one size fits all. And you can prototype against a local open model and swap to Apple’s model (or a cloud provider) later without rewriting your app.

Before you start

This is all developer beta software released June 8, 2026, so treat API signatures as provisional — verify against the current docs before shipping. You’ll need:

The MLX backend is provided by Apple’s open-source MLX Swift LM package rather than being part of the system framework, so the first step is adding it as a dependency.

Step 1 — Add the MLX backend to your project

In Xcode: File → Add Package Dependencies, then paste the MLX Swift LM package URL:

https://github.com/ml-explore/mlx-swift-lm

Or declare it in Package.swift if you’re building a Swift package:

// Package.swift
let package = Package(
    name: "MyLocalAIApp",
    platforms: [
        .macOS(.v27), .iOS(.v27)
    ],
    dependencies: [
        .package(url: "https://github.com/ml-explore/mlx-swift-lm", .upToNextMinor(from: "1.0.0"))
    ],
    targets: [
        .target(
            name: "MyLocalAIApp",
            dependencies: [
                .product(name: "MLXFoundationModels", package: "mlx-swift-lm")
            ]
        )
    ]
)

This gives you the MLXFoundationModels module, which provides the MLXLanguageModel type that conforms to the framework’s LanguageModel protocol.

Step 2 — Hello, local model

Here’s the entire “run an open model” flow. Note that nothing here is MLX-specific beyond the one line that constructs the model:

import FoundationModels
import MLXFoundationModels

func askLocalModel() async throws {
    // An open-source MLX model, downloaded and cached from Hugging Face on first use
    let model = MLXLanguageModel(modelID: "mlx-community/Qwen3.5-4B-4bit")

    let session = LanguageModelSession(model: model)
    let response = try await session.respond(to: "Explain unified memory in two sentences.")

    print(response.content)
}

The first run downloads the model weights and caches them locally; subsequent runs start immediately. From here on, the session behaves identically no matter which backend you chose — which is the whole point.

Step 3 — The swap: same code, different brain

This is the part worth internalizing. Every backend conforms to LanguageModel, so switching is a one-line change and everything downstream is untouched:

import FoundationModels
import MLXFoundationModels

// Pick exactly one:

// 1. Apple's on-device model (no download, tightly integrated, smallest)
let model = SystemLanguageModel()

// 2. An open-source MLX model from Hugging Face (you choose size and capability)
// let model = MLXLanguageModel(modelID: "mlx-community/Qwen3.5-4B-4bit")

// 3. Apple's Private Cloud Compute model (larger, still private, no API keys)
// let model = PrivateCloudComputeLanguageModel()

// 4. Custom weights via Core AI
// let model = try await CoreAILanguageModel(resourcesAt: modelURL)

let session = LanguageModelSession(model: model)
let response = try await session.respond(to: "Summarize the plot of Hamlet in three sentences.")
print(response.content)

In practice this means you can develop against a capable open model, then flip to SystemLanguageModel() for the App Store build to avoid bundling weights — or route heavy requests to Private Cloud Compute while keeping light ones on-device. The session code never changes.

Step 4 — Structured output you can actually use

Printing a string is fine for a demo, but real apps need typed data. Foundation Models’ guided generation lets you annotate a Swift type and get it back populated, with the model constrained to produce valid output. Mark a type @Generable, describe its fields with @Guide, and ask the session to generate it:

import FoundationModels
import MLXFoundationModels

@Generable
struct MeetingSummary {
    @Guide(description: "A one-sentence overview of the meeting")
    let overview: String

    @Guide(description: "Key decisions made, as short phrases")
    let decisions: [String]

    @Guide(description: "Action items, each phrased as a task")
    let actionItems: [String]
}

func summarize(_ transcript: String) async throws -> MeetingSummary {
    let model = MLXLanguageModel(modelID: "mlx-community/Qwen3.5-4B-4bit")
    let session = LanguageModelSession(model: model)

    let response = try await session.respond(
        to: "Summarize this meeting transcript:\n\n\(transcript)",
        generating: MeetingSummary.self
    )
    return response.content   // a fully-typed MeetingSummary, not a string to parse
}

No JSON parsing, no regex, no “please respond only in JSON” prompt-wrangling. You get a MeetingSummary instance. (If you read the previous article on local transcription, this is the natural next step: feed the Whisper transcript straight into this function.)

Step 5 — Tool calling

The model can call functions you define, which is how you ground it in live data or let it take actions. You declare a Tool, hand it to the session, and the framework handles the back-and-forth:

import FoundationModels

struct WeatherTool: Tool {
    let name = "getWeather"
    let description = "Get the current temperature for a city."

    @Generable
    struct Arguments {
        @Guide(description: "City name, e.g. 'Cupertino'")
        let city: String
    }

    func call(arguments: Arguments) async throws -> String {
        // Your real implementation would hit a weather service here
        return "It's 21°C and sunny in \(arguments.city)."
    }
}

func askWithTools() async throws {
    let model = MLXLanguageModel(modelID: "mlx-community/Qwen3.5-4B-4bit")
    let session = LanguageModelSession(model: model, tools: [WeatherTool()])

    let response = try await session.respond(to: "Should I bring a jacket in Cupertino today?")
    print(response.content)
}

Whether the model supports tool calling depends on the model — which brings us to capabilities.

Step 6 — Streaming for responsive UIs

For anything user-facing, you want tokens to appear as they’re generated rather than waiting for the full response. Use the streaming variant and update your UI as snapshots arrive:

let session = LanguageModelSession(model: model)
let stream = session.streamResponse(to: "Write a haiku about unified memory.")

for try await partial in stream {
    // `partial` is the response so far — bind it to your SwiftUI view
    print(partial.content)
}

Step 7 — Choosing a model

The mlx-community organization on Hugging Face hosts thousands of pre-converted, quantized models. A few practical guidelines:

Errors and gotchas

The framework defines a shared LanguageModelError so you can handle failures uniformly across backends. The cases worth catching:

A few practical notes:

When to use which backend

A quick decision guide once you’ve internalized the swap:

Wrapping up

The headline from WWDC 2026 wasn’t Siri — for developers, it was that Apple’s AI framework stopped being a walled garden. The LanguageModel protocol turns model choice into a one-line decision, and MLXLanguageModel means the entire open-source ecosystem on Hugging Face is now reachable through the same clean Swift API as Apple Intelligence itself.

The most pragmatic pattern this unlocks: prototype locally with a capable MLX model, then choose your production backend per-feature — on-device for privacy and cost, cloud for the hardest queries — without touching the code that actually does the work.

If you want to go further, the companion piece in this series covers scripting these same models from the terminal with the new fm command-line tool and the mlx_lm CLI.


All APIs reflect the iOS 27 / macOS 27 developer betas as of June 2026 and may change before the public release. Check Apple’s Foundation Models documentation and the WWDC 2026 session “Bring an LLM provider to the Foundation Models framework” for the authoritative reference.