Stop Locking Into Apple's AI! AnyLanguageModel Exposed
Stop Locking Into Apple's AI! AnyLanguageModel Exposed
What if I told you that a single line of code could liberate your Swift app from Apple's walled garden of AI? That you could go from Cupertino's tightly controlled Foundation Models to running Claude, GPT-4o, or even a privacy-obsessed local LLM on your Mac—without rewriting your entire AI layer?
Here's the painful truth developers are waking up to: Apple's Foundation Models framework is elegant, but it's a cage. macOS 26 or later. iOS 26 minimum. No OpenAI. No Anthropic. No local models that you actually control. You're building on Apple's timeline, with Apple's restrictions, for Apple's ecosystem. And if you've already architected your app around import FoundationModels, you're looking at a massive refactor if you ever want to escape.
What if escaping was as simple as changing one import statement?
Enter AnyLanguageModel, the Swift package that's sending shockwaves through the iOS and macOS developer community. Created by Mattt (yes, that Mattt from NSHipster fame) and now housed under the Hugging Face organization, this isn't just another AI wrapper. It's a drop-in, API-compatible replacement for Apple's entire Foundation Models framework that opens the floodgates to virtually every major language model provider on the planet.
One import change. That's it. Your existing LanguageModelSession, Tool protocols, @Generable macros, and guided generation code? They all work exactly as before. But now, instead of being trapped in Apple's ecosystem, you can point your app at OpenAI's GPT-4o, Anthropic's Claude, Google's Gemini, local MLX models on Apple Silicon, or even self-hosted Ollama instances.
This is the escape hatch developers have been praying for. And it's already production-ready.
What Is AnyLanguageModel?
AnyLanguageModel is a Swift 6.1+ package that provides a mechanically compatible, drop-in replacement for Apple's FoundationModels framework. The genius lies in its architectural philosophy: instead of creating yet another abstraction layer with its own learning curve, it mirrors Apple's APIs precisely—then extends them with pluggable backends.
The origin story matters here. Mattt, renowned for his deep dives into Swift and Apple frameworks through NSHipster, recognized a critical gap. Apple's Foundation Models announcement at WWDC promised on-device intelligence, but with strings attached: latest OS versions only, no third-party model support, and zero flexibility for developers who needed to ship AI features to existing user bases. The community needed a bridge, not a bomb to rebuild everything.
The project gained enough traction that Hugging Face—the epicenter of open-source machine learning—adopted it under their GitHub organization. This isn't a weekend hackathon project. This is infrastructure with serious backing.
Why it's trending now:
- iOS 18 adoption lag: Most users won't hit iOS 26 for years. AnyLanguageModel works on iOS 17+.
- Regulatory pressure: EU developers need alternatives to Apple's locked-down services.
- Privacy awakening: Running local models via MLX or llama.cpp keeps data on-device, period.
- Cost control: Switching between cloud providers based on pricing or performance needs.
- Vendor independence: The AI landscape shifts weekly. Locking into one provider is reckless.
The package supports nine distinct provider backends out of the box, from Apple's own system models to completely self-hosted solutions. And through Swift 6.1's innovative package traits system, you only compile in the backends you actually use—keeping binary sizes lean and build times fast.
Key Features That Make It Irresistible
AnyLanguageModel isn't just about swapping imports. It's about superpowers you never had with Apple's framework alone. Let's dissect what makes this package genuinely transformative for Swift developers.
True API Compatibility
The @Generable macro, @Guide property wrappers, Tool protocol, LanguageModelSession—every single Apple API has a twin here. Your existing code compiles with one import change. This isn't "similar" APIs; this is mechanical compatibility. The diff literally looks like this:
- import FoundationModels
+ import AnyLanguageModel
Nine Provider Backends (and Counting)
| Provider | Type | Local/Cloud | Special Features |
|---|---|---|---|
| Apple Foundation Models | System | Local (device) | Requires macOS/iOS 26 |
| Core ML | Converted models | Local | Apple's ML framework integration |
| MLX | Apple-optimized | Local (Apple Silicon) | KV-cache quantization, GPU memory control |
| llama.cpp (GGUF) | Quantized models | Local | Smallest footprint local inference |
| Ollama | HTTP API | Local/Remote | Easy model management, vision variants |
| OpenAI | Cloud API | Remote | Chat Completions + Responses APIs |
| Open Responses | Multi-provider | Remote | OpenRouter, compatible endpoints |
| Anthropic Claude | Cloud API | Remote | Extended thinking, tool use |
| Google Gemini | Cloud API | Remote | Server-side tools, thinking budget |
Swift 6.1 Package Traits: Compile What You Need
This is architectural elegance. Instead of dragging in every dependency for every backend, you opt-in via traits:
CoreMLtrait → includes Hugging Face TransformersMLXtrait → includes mlx-swift-lmLlamatrait → includes llama.swift
No traits enabled by default means minimal footprint. Your app doesn't bloat with unused ML frameworks.
Guided Generation Everywhere
Apple's @Generable + @Guide system for type-safe LLM outputs? It works across all providers, not just Apple's on-device models. Cloud or local, you get strongly-typed structured generation without string parsing hell.
Image Inputs with Provider Awareness
Multimodal AI isn't an afterthought. Pass images to sessions with automatic capability detection. The package knows which providers support vision and handles graceful degradation.
Tool Calling with Execution Control
Define tools with the Tool protocol, then observe and intercept execution via ToolExecutionDelegate. Want to show a confirmation dialog before your AI sends an email? Return .stop or .provideOutput(...) from your delegate. This is production-grade control that Apple's framework doesn't expose.
Use Cases Where AnyLanguageModel Absolutely Dominates
Theory is cheap. Let's talk about real shipping scenarios where this package changes everything.
1. Shipping AI to Existing User Bases
You've got 500,000 monthly active users on iOS 17. Apple's Foundation Models? Requires iOS 26. That's a three-year adoption cliff before meaningful usage. AnyLanguageModel lets you integrate OpenAI or Claude today, then gracefully migrate users to local MLX models as they upgrade devices. Same codebase. Same APIs. Zero rewrite.
2. Privacy-Critical Applications
Healthcare apps. Legal document analysis. Enterprise knowledge bases. Sending data to OpenAI is a compliance nightmare. With AnyLanguageModel, swap to MLXLanguageModel or LlamaLanguageModel—your data never leaves the device. The Tool protocol works identically, so your AI agent architecture stays intact.
3. Cost Optimization at Scale
You're burning through $12,000/month on OpenAI tokens. Some requests are simple enough for a local Qwen 0.6B model. With AnyLanguageModel, implement intelligent routing: complex reasoning → Claude, simple tasks → local MLX, image analysis → GPT-4o. One unified API, multiple cost tiers. Your CFO will weep with joy.
4. Offline-First and Edge Computing
Field service apps. Aviation EFBs. Maritime navigation. Anywhere connectivity is unreliable. Pre-load a GGUF model via llama.cpp, and your app has fully autonomous AI without network dependency. The same LanguageModelSession code works online or offline—you just initialize a different model.
5. Rapid Provider Migration
OpenAI has an outage. Anthropic changes pricing. Google releases a killer new model. With Apple's framework, you're stuck. With AnyLanguageModel, change one initialization line:
// Before
let model = OpenAILanguageModel(apiKey: key, model: "gpt-4o")
// After (same session code, same tools, same everything)
let model = AnthropicLanguageModel(apiKey: key, model: "claude-sonnet-4")
6. Development and Testing Velocity
Mocking Apple's Foundation Models for unit tests? Painful. With AnyLanguageModel, spin up Ollama locally, point tests at http://localhost:11434, and run your entire AI test suite without network calls or API charges. The ToolExecutionDelegate lets you intercept and verify every tool call precisely.
Step-by-Step Installation & Setup Guide
Ready to break free? Here's your complete escape plan.
Prerequisites
- Swift 6.1+ (required for package traits)
- Xcode 16 recommended (Xcode 26 has a known bug with older deployment targets)
- Deployment targets: iOS 17.0+, macOS 14.0+, visionOS 1.0+, or Linux
Basic Package Installation
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/huggingface/AnyLanguageModel", from: "0.8.0")
]
Enabling Provider Traits
By default, no heavy dependencies are included. Enable only what you need:
// In your Package.swift
dependencies: [
.package(
url: "https://github.com/huggingface/AnyLanguageModel.git",
from: "0.8.0",
traits: ["CoreML", "MLX"] // Enable only these backends
)
]
Critical workaround for SPM bug: Due to Swift Package Manager issue #9286, trait resolution may fail with "exhausted attempts to resolve the dependencies graph." Fix by adding underlying dependencies directly:
dependencies: [
.package(
url: "https://github.com/huggingface/AnyLanguageModel.git",
from: "0.8.0",
traits: ["CoreML", "MLX", "Llama"]
),
// Explicitly declare trait dependencies to bypass SPM resolution bug
.package(url: "https://github.com/huggingface/swift-transformers", from: "1.0.0"), // CoreML
.package(url: "https://github.com/ml-explore/mlx-swift-lm", from: "2.25.5"), // MLX
.package(url: "https://github.com/mattt/llama.swift", from: "2.0.0"), // Llama
]
Xcode Project Setup (The Shim Package Workaround)
Xcode doesn't expose package traits in its UI. The official workaround: create a local shim package.
Step 1: Create local package
mkdir -p Packages/MyAppKit
cd Packages/MyAppKit
swift package init
Step 2: Configure shim Package.swift
// swift-tools-version: 6.1
import PackageDescription
let package = Package(
name: "MyAppKit",
platforms: [
.macOS(.v14),
.iOS(.v17),
.visionOS(.v1),
],
products: [
.library(
name: "MyAppKit",
targets: ["MyAppKit"]
)
],
dependencies: [
.package(
url: "https://github.com/huggingface/AnyLanguageModel",
from: "0.4.0",
traits: ["MLX"] // Your desired traits here
)
],
targets: [
.target(
name: "MyAppKit",
dependencies: [
.product(name: "AnyLanguageModel", package: "AnyLanguageModel")
]
)
]
)
Step 3: Export module
// Sources/MyAppKit/Export.swift
@_exported import AnyLanguageModel
Step 4: Add to Xcode
Project Settings → Package Dependencies → "+" → "Add Local..." → select Packages/MyAppKit.
Now import MyAppKit in your app gives you AnyLanguageModel with MLX enabled.
Pro tip: See chat-ui-swift for a fully working Xcode example with traits.
REAL Code Examples from the Repository
Let's examine actual production code from the AnyLanguageModel repository, with detailed breakdowns of what's happening and why it matters.
Example 1: The One-Line Migration (Basic Tool Calling)
This is the example that broke the internet. Apple's own code, with one import changed:
import AnyLanguageModel // Changed from FoundationModels
// Define a tool with strongly-typed arguments using @Generable macro
struct WeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve the latest weather information for a city"
// @Generable creates Codable conformance and schema generation automatically
@Generable
struct Arguments {
// @Guide adds LLM-facing descriptions for each parameter
@Guide(description: "The city to fetch the weather for")
var city: String
}
// Async throws allows for network calls and error propagation
func call(arguments: Arguments) async throws -> String {
"The weather in \(arguments.city) is sunny and 72°F / 23°C"
}
}
// Initialize Apple's system model (falls back to Apple backend)
let model = SystemLanguageModel.default
// Create session with our tool available for LLM to call
let session = LanguageModelSession(model: model, tools: [WeatherTool()])
// Prompt triggers automatic tool detection and execution
let response = try await session.respond {
Prompt("How's the weather in Cupertino?")
}
print(response.content)
What's happening under the hood: The @Generable macro expands to generate JSON Schema for the LLM, Codable conformance for serialization, and validation logic. When the session processes "How's the weather in Cupertino?", the LLM recognizes a tool call opportunity, generates structured arguments { "city": "Cupertino" }, the framework deserializes to Arguments, invokes call(arguments:), and feeds the result back for final response generation. All automatic.
Example 2: Production-Grade Tool Execution Control
The ToolExecutionDelegate is where AnyLanguageModel surpasses Apple's framework. This actor-based observer pattern gives you surgical control:
actor ToolExecutionObserver: ToolExecutionDelegate {
// Called when LLM generates tool calls but BEFORE execution
func didGenerateToolCalls(
_ toolCalls: [Transcript.ToolCall],
in session: LanguageModelSession
) async {
print("Generated tool calls: \(toolCalls)")
// Log for analytics, show loading UI, etc.
}
// CRITICAL: Decide whether to execute, stop, or override each call
func toolCallDecision(
for toolCall: Transcript.ToolCall,
in session: LanguageModelSession
) async -> ToolExecutionDecision {
// Three options:
// .execute — proceed normally (what we do here)
// .stop — halt entire session after this tool call
// .provideOutput(String) — skip execution, inject fake result
// This is where you'd show a confirmation dialog in UI!
// Or check user permissions, rate limits, safety policies...
.execute
}
// Called AFTER successful execution with actual output
func didExecuteToolCall(
_ toolCall: Transcript.ToolCall,
output: Transcript.ToolOutput,
in session: LanguageModelSession
) async {
print("Executed tool call: \(toolCall)")
// Update UI, log success metrics, trigger side effects
}
}
let session = LanguageModelSession(model: model, tools: [WeatherTool()])
session.toolExecutionDelegate = ToolExecutionObserver() // Wire up observer
The power here is extraordinary. You can implement human-in-the-loop confirmation for destructive operations, audit logging for compliance, circuit breakers for failing tools, or A/B testing different tool implementations. Apple's framework offers nothing comparable.
Example 3: MLX Local Model with Advanced Configuration
Here's where we go deep on Apple Silicon optimization. MLX models run locally with GPU acceleration, and AnyLanguageModel exposes every tuning knob:
// Initialize MLX model with automatic GPU memory management
let model = MLXLanguageModel(
modelId: "mlx-community/Qwen3-0.6B-4bit",
gpuMemory: .automatic // Let MLX optimize VRAM usage
)
let session = LanguageModelSession(model: model)
// Configure generation with provider-specific options
var options = GenerationOptions(temperature: 0.7)
var mlxOptions = MLXLanguageModel.CustomGenerationOptions.default
// KV-cache quantization: massive memory savings for long contexts
mlxOptions.kvCache = .init(
maxSize: 4096, // Maximum sequence length
bits: 4, // 4-bit quantization
groupSize: 64, // Group size for quantization
quantizedStart: 128 // Start quantizing after this token
)
// Deterministic image preprocessing for consistent vision behavior
mlxOptions.userInputProcessing = .resize(to: CGSize(width: 512, height: 512))
// Inject template variables for model-specific chat formats
mlxOptions.additionalContext = [
"user_name": .string("Alice"),
"turn_count": .int(3),
"verbose": .bool(true),
]
// Attach custom options to generation via type-safe subscript
options[custom: MLXLanguageModel.self] = mlxOptions
// Execute with full configuration
let response = try await session.respond(
to: "Summarize this transcript",
options: options
)
Why this matters: The kvCache configuration with 4-bit quantization can reduce memory usage by 8x for long conversations. The userInputProcessing ensures consistent vision model behavior regardless of input image size. And additionalContext lets you customize chat templates without forking the model. This is production systems engineering, not toy demos.
Example 4: Secure Cloud Provider with Environment-Based Keys
For development and testing, environment variables keep credentials out of source control:
let model = OpenAILanguageModel(
apiKey: ProcessInfo.processInfo.environment["OPENAI_API_KEY"]!,
model: "gpt-4o-mini"
)
let session = LanguageModelSession(model: model)
// Multimodal: text + multiple images in one request
let response = try await session.respond(
to: "List the objects you see",
images: [
// Remote image via URL
.init(url: URL(string: "https://example.com/desk.jpg")!),
// Local image from data with explicit MIME type
.init(
data: try Data(contentsOf: URL(fileURLWithPath: "/path/to/closeup.png")),
mimeType: "image/png"
)
]
)
print(response.content)
Security note: The ProcessInfo.processInfo.environment pattern keeps keys out of compiled binaries. For production, the repository documents Bring Your Own Key (Keychain storage) and Proxy Server (OAuth 2.1 tokens) patterns. Never ship API keys in your binary—reverse engineering is trivial.
Advanced Usage & Best Practices
Trait Minimization for Build Performance
Only enable traits you ship. Each trait adds compilation time and binary size. For a cloud-only app, skip CoreML, MLX, and Llama entirely. Your CI will thank you.
Intelligent Model Routing
Build a ModelRouter that selects backends based on task complexity, network availability, and user preferences:
enum ModelRoute {
case local(MLXLanguageModel) // Privacy-critical, offline
case fast(OpenAILanguageModel) // Quick responses, online
case smart(AnthropicLanguageModel) // Complex reasoning
}
Tool Execution Sandboxing
Use ToolExecutionDelegate to implement permission tiers. Dangerous tools (email sending, purchases, data deletion) require explicit user confirmation. Informational tools execute automatically.
Testing Strategy
- Use Ollama with
qwen3for fast local integration tests - Set
CIenvironment variable to skip expensive generation tests in CI - Use
ToolExecutionDelegateto mock tool outputs without network calls
Memory Management for Local Models
MLX's .automatic GPU memory is convenient but can be unpredictable. For consistent behavior, profile your app's memory usage and set explicit limits:
let model = MLXLanguageModel(
modelId: "mlx-community/Llama-3.2-3B-Instruct-4bit",
gpuMemory: .fixed(size: 4 * 1024 * 1024 * 1024) // 4GB hard limit
)
Comparison with Alternatives
| Feature | AnyLanguageModel | Apple's FoundationModels | Direct API SDKs | llama.cpp Swift |
|---|---|---|---|---|
| API compatibility with Apple | ✅ Native | ✅ Native | ❌ None | ❌ None |
| Multiple providers | ✅ 9 backends | ❌ Apple only | ❌ One each | ❌ Local only |
| Drop-in migration | ✅ One import | N/A | ❌ Full rewrite | ❌ Full rewrite |
| Guided generation | ✅ All providers | ✅ Apple only | ❌ Manual parsing | ❌ Manual parsing |
| Tool calling | ✅ All (except llama.cpp) | ✅ Apple only | Varies | ❌ Manual |
| Local model support | ✅ MLX, CoreML, GGUF | ✅ Apple only | ❌ | ✅ Only GGUF |
| Minimum iOS version | iOS 17 | iOS 26 | Varies | Varies |
| Binary size control | ✅ Traits system | System framework | Multiple SDKs bloated | Single purpose |
| Execution observation | ✅ Delegate pattern | ❌ Limited | ❌ None | ❌ None |
The verdict: AnyLanguageModel occupies a unique position. It's the only solution that gives you Apple's elegant API design with complete provider freedom. Direct SDKs lock you into one vendor. Apple's framework locks you into Apple. Raw llama.cpp requires massive boilerplate. AnyLanguageModel is the Goldilocks zone.
FAQ
Q: Will my existing FoundationModels code work without changes?
A: Almost always yes. Change import FoundationModels to import AnyLanguageModel. The APIs are mechanically compatible. Edge cases around very new Apple APIs may need minor adjustments as the package evolves.
Q: Can I mix providers in the same app?
A: Absolutely. Initialize different LanguageModel instances and pass them to separate LanguageModelSession objects. Or build a routing layer that selects providers dynamically.
Q: Is this production-ready for App Store apps?
A: Yes, with proper API key security. Never embed keys in binaries. Use Keychain (BYO model) or proxy servers with OAuth. The repository includes detailed security guidance.
Q: How do I handle the Xcode 26 build bug?
A: Build with Xcode 16 for deployment targets before macOS 26 / iOS 26. This is a known Apple bug tracked in issue #15.
Q: What's the performance overhead versus direct API calls?
A: Negligible. AnyLanguageModel is a thin abstraction layer. The actual network requests or local inference happen in the underlying provider libraries. You're paying for type safety and API consistency, not performance cost.
Q: Can I contribute new providers?
A: The project welcomes contributions. Check good first issues to get started.
Q: Does this work on Linux or Windows?
A: Swift 6.1+ and Linux are supported. Windows support depends on Swift toolchain maturity for the specific backends you enable.
Conclusion
The AI landscape is a battlefield of shifting alliances, pricing wars, and capability explosions. Betting your entire product on a single provider—or a single platform owner like Apple—is strategic malpractice. You need optionality without sacrificing developer experience.
AnyLanguageModel delivers exactly that. One import change liberates you from Apple's timeline. One initialization swap moves you between OpenAI and Claude. One trait enablement brings local, private inference to your users. The @Generable macros, Tool protocols, and LanguageModelSession patterns you've invested in learning? They travel with you across every backend.
This isn't just a technical convenience. It's architectural insurance. When OpenAI has an outage, you failover to Anthropic. When Claude gets too expensive, you test local MLX. When Apple finally ships compelling on-device models, you migrate back with the same code.
The future belongs to developers who build adaptable systems. AnyLanguageModel is your adaptability engine.
Go star the repository. Try the shim package in your Xcode project. Run your first local model. And join the community of developers who refuse to be locked in.
The code is waiting at github.com/huggingface/AnyLanguageModel. Your move.
Found this breakdown valuable? Share it with your iOS team. The best engineering decisions are made when everyone knows the options.
Comments (0)
No comments yet. Be the first to share your thoughts!