Key Takeaways:
- Foundation Models handles Apple’s on-device language model; Core ML supports custom models.
- Check model availability and handle runtime failures before launch.
- Use structured output, streaming, and Swift concurrency for reliable generation.
- Quantization can reduce memory use but may affect output quality.
- Test latency, memory, privacy, and reliability on real devices.
Quick Answer: Deploy on-device AI in iOS by choosing Foundation Models for Apple’s system language model or Core ML for custom models. Check availability, validate outputs, optimize and profile on real hardware, and provide a fallback when inference is unavailable or the task exceeds local model capabilities.
In AI-powered mobile app development, A model that behaves in a prototype can fall apart on a real iPhone and the reason can be memory pressure, availability checks, older devices, slow responses. All of it lands on you at once and if your feature leans on AI, you need an answer for the day the model simply won’t run.
Apple gives you two routes, the Foundation Models framework wraps Apple’s own on-device language model. Core ML is for models you bring yourself. Pick based on one question “Do you want a managed model, or do you need control over architecture and weights?
Most teams that set out to Deploy On-Device AI Models in iOS Apps find that getting inference running takes an afternoon. Making it dependable takes the rest of the project. That means working across supported devices, keeping user data local, and making sure the output fits what the app needs. Below is how I’d work through it: availability, structured Swift output, model conversion, quantized LLMs, and production failures.
1. Pick the right framework first
The Apple Intelligence Frameworks overlap, but you can’t swap one for another. Match the tool to the task.
| Requirement | Recommended framework |
| Text summarization, rewriting, and generation | Foundation Models |
| Image classification, detection, and prediction | Core ML |
| Natural-language access to app actions | App Intents + Foundation Models |
| Tasks requiring a larger language-model context | Private Cloud Compute, where eligible |
| Custom model architectures and weights | Core ML or a compatible inference runtime |
Foundation Models
The Foundation Models framework in Swift talks to Apple’s on-device language model. It does text generation, structured output, session context, and tool calling. The OS owns the weights, so there’s nothing to bundle and no loader to write. You open a session, give it instructions and input, and read the result.
That suits note summaries, pulling fields out of documents, rewriting text, and generating structured responses. What it won’t do is load arbitrary LLM weights. If your product depends on one specific fine-tuned or quantized model, look at Core ML or another compatible runtime instead.
Core ML
Core ML is the route for custom models. Convert a compatible model, optimize it, and ship it. It covers image recognition, classification, prediction, and compatible language-model inference, and depending on the model and device it can run work on the CPU, GPU, or Apple Neural Engine (ANE). If preprocessing, precision, and runtime behavior need to stay in your hands, this is the one.
2. Check availability before you promise anything
Confirm your Xcode version and deployment target support the APIs you want. Then import the framework:
|
1 2 |
import Foundation import FoundationModels |
Whether the model is usable depends on hardware, OS version, language and region, and whether the user turned Apple Intelligence on and finished the download. You can’t make any of that happen for them. Your app can only ask.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import FoundationModels @available(iOS 26.0, *) struct LocalTextAssistant { func summarize(_ text: String) async throws -> String { let session = LanguageModelSession { "Summarize the supplied text in three concise bullet points." } let response = try await session.respond(to: text) return response.content } } |
Use the result to enable the feature, show setup steps, or fall back to something that doesn’t involve AI.
One trap: availability isn’t permanent. A user can flip a setting while your app is open, and the model disappears. An up-front check won’t save you, so handle errors at the moment of generation too. And build the UI around at least three states: preparing the model, generating, and unable to run.
3. Generate text with Foundation Models
A Language Model Session manages one interaction with the model. You give it instructions for the job and pass in the user’s input.
Say you’re building a notes app and want summaries without shipping note contents to a remote service:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import Foundation import FoundationModels @available(iOS 26.0, *) struct LocalTextAssistant { func summarize(_ text: String) async throws -> String { let session = LanguageModelSession { "Summarize the supplied text in three concise bullet points." } let response = try await session.respond(to: text) return response.content } } |
The instruction is doing the real work here. In production, say how long the output should be, what format it takes, and who it’s for. A summary for a push notification looks nothing like one in a document editor. Narrow prompts also make testing easier, because you can actually tell whether a tweak helped.
Structured Swift output
Free text is fine when a human reads it. It’s a headache when your code needs specific fields to store, filter, or branch on.
Guided generation lets you describe the output as a Swift type:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import FoundationModels @Generable struct SupportSummary { @Guide(description: "The main issue") var issue: String @Guide(description: "A suggested next action") var nextAction: String @Guide(description: "Urgency from 1 to 5") var urgency: Int } @available(iOS 26.0, *) func summarizeTicket(_ text: String) async throws -> SupportSummary { let session = LanguageModelSession() let result = try await session.respond( to: text, generating: SupportSummary.self ) return result.content } |
Good for ticket triage, extracting data from submissions, and organizing meeting notes. But a typed result isn’t a validated result. An urgency of 5 is still just a number the model chose, so check required fields, ranges, and permitted actions against your own rules before saving anything.
4. Streaming and Swift 6 concurrency
Making someone stare at a spinner until the full response lands feels slow. Stream it instead.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@available(iOS 26.0, *) func streamSummary( text: String, update: @escaping @MainActor (String) -> Void ) async throws { let session = LanguageModelSession() let stream = session.streamResponse( to: "Summarize this text: \(text)" ) for try await partial in stream { await update(partial) } } |
Run inference in an async task, push UI changes to the main actor, and never block the main thread with synchronous model calls or heavy preprocessing.
A few things bite teams right before launch. Generation keeps running after the user leaves the screen, so cancel it. Impatient users double-tap, so block duplicate requests. Someone has to own the session, so decide who. Errors shouldn’t crash the interface. And large prompts and transcripts shouldn’t hang around in memory longer than needed.
Swift 6 concurrency makes isolation boundaries explicit, so look hard at actor isolation and Sendable requirements in your model service. Don’t assume a session or a custom inference wrapper tolerates concurrent access unless its API says it does.
Build AI Features That Work on Real Devices
Need help with Foundation Models or Core ML integration? Let’s build reliable, production-ready AI features for your iOS app.
5. Core ML on-device AI integration for custom models
When Apple’s model doesn’t fit, Core ML on-device AI integration lets you ship one your team chose or trained. The order is: compatible model, conversion, validation, optimization, integration.
Converting with coremltools
For a compatible PyTorch model, coremltools conversion looks roughly like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import coremltools as ct import torch model = YourModel().eval() example = torch.randn(1, 3, 224, 224) traced = torch.jit.trace(model, example) mlmodel = ct.convert( traced, inputs=[ ct.TensorType( name="image", shape=example.shape ) ], convert_to="mlprogram" ) mlmodel.save("ImageClassifier.mlpackage") |
It’s a template. Your input names, tensor shapes, preprocessing, and supported ops will differ.
And don’t stop at “it converted.” Run real inputs through the original and the Core ML version and compare the predictions. Silent drift here is more common than people expect.
Loading and running
Add the model to Xcode and Core ML generates an interface from its inputs and outputs.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import CoreML final class ImageClassifier { private let model: ImageClassifierModel init() throws { model = try ImageClassifierModel( configuration: MLModelConfiguration() ) } func predict( image: MLImage ) throws -> ImageClassifierModelOutput { try model.prediction(image: image) } } |
The generated class and prediction signature come from your model, and MLImage is a stand-in for whatever input type or preprocessing pipeline you actually have.
With bigger models, think about using the supported download and compilation APIs rather than stuffing every weight into the app binary. The first install gets smaller. In exchange you own interrupted downloads, storage limits, model versions, and flaky networks.
6. Quantized LLM iOS deployment
Quantization stores weights at lower precision. For an LLM that can mean less memory and faster inference, depending on architecture and hardware.
You pay in quality. Accuracy can slip, some operations may not be supported, and runtime behavior can shift. Smaller isn’t the same as better.
If you’re evaluating a quantized LLM iOS build, track peak memory during loading and generation, time to first token, total generation time, throughput on realistic prompts, battery drain, thermal behavior, and output quality on an evaluation set built for your task. That last one matters most and gets skipped most.
Core ML Tools handles conversion and optimization, quantization included, and the right precision depends on your architecture and the ops your target devices support. The Apple Neural Engine can speed up compatible operations, but not every layer will run there. Profile on actual hardware. Picking a compute configuration doesn’t guarantee ANE execution.
7. On-device vs Private Cloud Compute
On-device wins when you need offline operation, minimal network reliance, or local handling of user data. It’s weaker for long documents and heavier reasoning.
For those cases Apple documents Private Cloud Compute (PCC). Its published comparison gives the on-device model a 4K-token context and PCC a 32K-token context. PCC needs connectivity, has usage limits, and depends on eligibility and the right entitlement.
A selection strategy might look like this:
|
1 2 3 4 5 6 7 |
let model: any LanguageModel if needsLongContext { model = PrivateCloudComputeLanguageModel() } else { model = SystemLanguageModel.default } |
That’s an illustrative pseudocode, follow the initializer and session setup documented for your SDK version, and check entitlement requirements before you design around it.
PCC isn’t a regular third-party inference endpoint, either. Apple’s architecture is built to process requests without retaining user data. You still owe users a clear heads-up that a feature needs the network, and your privacy disclosures should say so.
8. App Intents integration
App Intents exposes your app’s actions to Siri, Shortcuts, and supported Apple Intelligence experiences. You don’t have to build a chat interface for every operation. Define specific actions and let the system call them. A notes app could offer an intent that summarizes a selected note.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import AppIntents struct SummarizeNoteIntent: AppIntent { static var title: LocalizedStringResource = "Summarize Note" @Parameter(title: "Note") var note: NoteEntity func perform() async throws -> some IntentResult & ReturnsValue<String> { let summary = try await NoteAIService().summarize(note.content) return .result(value: summary) } } |
NoteEntity and NoteAIService are yours to write. The intent also needs parameter resolution and authorization for reading the selected note.
Keep the boundary firm. A model can propose an action. Your app decides whether to do it, after checking permissions, user intent, and the operation itself, especially for anything that changes data.
9. Testing something that isn’t deterministic
Ordinary unit tests can’t fully cover on-device AI. Think of the model as a component with quality, performance, and reliability targets you can measure.
| Test area | What to verify |
| Availability | Unsupported device, disabled Apple Intelligence, unavailable model |
| Output quality | Accuracy, relevance, format compliance |
| Performance | Startup latency, time to first token, throughput |
| Memory | Peak usage, memory warnings, repeated sessions |
| Reliability | Cancellation, malformed input, model errors |
| Privacy | Local processing, data retention, logging |
| Compatibility | Supported iOS versions and device classes |
For classification and extraction, keep a fixed evaluation set so you can compare across model versions. For generative features, test representative prompts, edge cases, and your structured-output constraints.
Apple’s Evaluations framework gives you tools for assessing Foundation Models experiences. Re-run your prompts whenever you move to a new SDK or OS version, because the system model’s behavior can change underneath you.
10. Plan for failure
Your core functionality shouldn’t hinge on a model being available. Here’s a workable iOS local AI deployment fallback chain:
- Local Model Available: Run on-device.
- Local Model Unavailable: Offer a non-AI workflow, or explain how to turn on Apple Intelligence.
- Task Too Big For The Local Model: Use PCC when eligible, or a separately configured server provider.
- Network Or Service Failure: Keep the user’s input and offer a retry.
And please don’t quietly upload private content as a fallback. Say where processing will happen, and get consent first if it’s required.
Take Your iOS AI Project Further
Turn your AI deployment requirements into a production-ready iOS experience with expert development support.
Conclusion
Getting a model to run on Apple silicon is the small part when you deploy on-device AI models in iOS apps. Availability, memory ceilings, concurrency, output validation, and what happens when inference fails are what decide whether the feature survives contact with users.
For native language features, start with Foundation Models and test it against your actual task before reaching for anything bigger. When you need a custom architecture or specialized inference, Core ML hands you control over conversion, optimization, and runtime. App Intents can expose either through system experiences, and profiling on real devices tells you if the whole workflow is ready.
The model with the most parameters isn’t the goal. The goal is whatever meets your quality, latency, memory, privacy, and reliability bar on the devices your users actually carry. That’s the standard any iOS app development services partner should hold an AI feature to.



Your email address will not be published. Required fields are marked *