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:
- Xcode 27 (beta) with the macOS 27 / iOS 27 SDKs.
- An Apple Silicon Mac. MLX is Apple-Silicon-only, and the models run on the GPU and Neural Engine.
- Enough unified memory for the model you choose — a 4-bit quantized 3–4B model is comfortable on 16 GB; larger models want more.
- Familiarity with Swift concurrency (
async/await), since every call is asynchronous.
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:
- Start with a 4-bit quantized 3–4B model. Something like a
Qwen3.5-4B-4bit-class model is a strong default: fast, capable, and comfortable on 16 GB of unified memory. - Match the model to the task. A small model is plenty for classification, tagging, or extraction. Reach for a larger one only when reasoning quality actually demands it.
- Mind your memory budget. A 4-bit model needs roughly half a gigabyte of memory per billion parameters as a rough mental model, plus headroom for context. On a 16 GB Mac, stay in the 3–8B range; 64 GB opens up much larger models.
- Capabilities vary. Not every open model supports tool calling or guided generation. The framework surfaces this — you can inspect a model’s declared capabilities (tool calling, guided generation, reasoning) and design fallbacks accordingly.
Errors and gotchas
The framework defines a shared LanguageModelError so you can handle failures uniformly across backends. The cases worth catching:
contextSizeExceeded— your transcript or conversation grew past the model’s context window. Trim older entries and retry.unsupportedCapability— you asked for guided generation or tools on a model that doesn’t support them.guardrailViolation— safety guardrails tripped on the prompt or response.rateLimited,timeout,refusal— handle gracefully with a fallback message.
A few practical notes:
- First-run download.
MLXLanguageModelfetches weights on first use; the initial call can take a while and needs network access, even though inference afterward is fully offline. - It’s beta. Signatures like
respond(to:generating:)andstreamResponse(to:)reflect the June 2026 betas and may shift before the fall release. Pin your expectations to the current documentation. - Memory pressure. If a model is too large for available memory, expect slowdowns or failures — drop to a smaller or more aggressively quantized variant.
When to use which backend
A quick decision guide once you’ve internalized the swap:
- System model — default for most apps. Zero download, deeply integrated, free, private. Use it unless you have a specific reason not to.
- MLX model — when you need a capability or quality the system model doesn’t offer, want a specific open model, or are doing research and want full control. The subject of this article.
- Private Cloud Compute — when you need more capability than on-device but still want Apple’s privacy guarantees and no API-key management.
- Core AI — when you have your own fine-tuned weights to ship.
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.