All Articles
CategoryAI
Reading Time
12 min read
Published
2026-10-24
Word Count
3,355words

Grab a coffee — this one is a deep dive!

iOS 27 Foundation Models: Dynamic Profiles and Evaluation

Summary

Apple Foundation Models framework 2026: in-session model switching with Dynamic Profiles, custom provider integration via LanguageModel, multimodal prompting, and prompt evaluation.

  • Dynamic Profiles is a three-layer API (Dynamic Instructions, Profile, DynamicProfile) that lets you change the model/instructions/tools at runtime within a LanguageModelSession.
  • The LanguageModel protocol lets you connect your own model provider to Apple's session/tool-calling/streaming infrastructure via an Executor.
  • Multimodal prompting lets you add images to the prompt through the Attachment struct and get a multimodal response over the same session; if you need OCR or barcodes, you add Vision's OCRTool and BarcodeReaderTool as tools to that same session.
  • The Performance and evaluation guides and the Python Foundation Models SDK let you build these evaluation suites outside Swift too, so you can test profile transitions after GA.
iOS 27 Foundation Models: Dynamic Profiles and Evaluation

Apple's Foundation Models framework moved beyond a single on-device session with iOS 27: you can now switch the model and instruction set at runtime within a session, plug in your own model provider through a protocol, and evaluate your prompts systematically. This post focuses on just three major capabilities added to the framework in 2026 after WWDC26: Dynamic Profiles, multimodal prompting, and the Performance and evaluation tools. We covered the basic session setup, @Generable, and the streaming API in Apple Foundation Models: On-Device LLM Framework; we don't repeat them here — we go straight to production scenarios, real API signatures, and the post-iOS-27-GA test checklist.

💡 Pro Tip: DynamicProfileBuilder enforces the single-active-profile constraint at compile time — use if/else expressions instead of parallel if blocks so the compiler verifies that constraint for you; design the switching logic from the start as an expression that returns a single profile.

Table of Contents

Where the framework landed with iOS 27

The "What's new" section on Foundation Models framework's official reference page lists three new articles (and three WWDC26 sessions). The articles: adding server-side intelligence with Private Cloud Compute, composing dynamic sessions with instructions and profiles, and analyzing images with multimodal prompting. These three headings make up the actual delta on the Foundation Models side of iOS 27 — we cover all three in turn in this post.

iOS 27 reached general availability on September 14, 2026, following the announcements made at WWDC26 (June 8-12, 2026). Since the GA release shipped on that date, the APIs below are available in the stable release and don't require a beta flag.

Why these three take priority

Apple's own ordering isn't arbitrary: Private Cloud Compute expands the context window, Dynamic Profiles allow multiple task modes within the same session, and multimodal prompting removes the text-only limit. Together, the three make one LanguageModelSession reusable across different screens without rebuilding it each time.

Here's how the three articles' titles in the official docs map to the sections of this post:

"What's new" article
Section in this post
Adding server-side intelligence with Private Cloud Compute
On-device vs. Private Cloud Compute
Composing dynamic sessions with instructions and profiles
Switching models/tools within a session via Dynamic Profiles
Analyzing images with multimodal prompting
Multimodal prompting: image analysis with Attachment

Beyond these three, the Custom language model provider and Performance and evaluation sections don't stand as separate items in the "What's new" list, but both are part of the same documentation tree and directly affect production decisions — which is why I cover them as separate headings too.

Language Model protocol: plugging in your own provider

Foundation Models is no longer limited to Apple's own models. The framework offers a lightweight protocol, protocol LanguageModel: Sendable; the docs describe it verbatim as: "Implement this protocol to create a bridge between a model and the framework." On the provider side, an Executor does the heavy lifting — translating framework types into platform-specific requests and streaming results through LanguageModelExecutorGenerationChannel.

swift
1import FoundationModels
2 
3// Framework declaration: protocol LanguageModel: Sendable
4// (requirements per the docs: capabilities + Executor configuration)
5// Once you plug in your provider, only one line changes at the call site:
6let session = LanguageModelSession(model: MyCustomServerLanguageModel())

The most practical part of this API surface: session.respond(to:) stays the same no matter the provider. Apple's own conforming types, SystemLanguageModel (on-device) and PrivateCloudComputeLanguageModel (server-side), conform to the same protocol — so switching between your provider and Apple's models needs no calling-code change, only the model: parameter.

When you should plug in your own provider

If you have an in-house fine-tuned model, a third-party API, or a service that must be hosted in a specific region for regulatory reasons, the LanguageModel protocol connects you to it without giving up Apple's session/tool-calling/streaming infrastructure. The provider is typically packaged as a Swift Package and added to the app.

Switching models/tools within a session via Dynamic Profiles

The Composing dynamic sessions with instructions and profiles article defines a three-layer architecture. Here are the layers, in the doc's own words: "Dynamic instructions: Re-evaluate before every model request and supply the instructions and tools needed at that time." "Profiles: Associate dynamic instructions with session-level properties, like which model to use and the reasoning level." "Dynamic profiles: Orchestrates transitions between multiple profiles and maintains one active profile at a time."

So at the bottom layer, the instructions themselves are re-evaluated on every request:

swift
1import FoundationModels
2 
3struct PresentationInstructions: DynamicInstructions {
4 var body: some DynamicInstructions {
5 // Generate the instruction text based on the user's current screen context.
6 Instructions("Help me improve my presentation.")
7 }
8}
9 
10let session = LanguageModelSession(
11 dynamicInstructions: PresentationInstructions()
12)

One layer up, Profile pairs instructions with session-level settings like model choice, temperature, and reasoning level:

swift
1Profile {
2 // Instructions and tools needed for a creative task.
3}
4// Use the PCC model instance created in the outer profile.
5.model(pccModel)
6// Use higher creativity temperature for poetry fans.
7.temperature(likesPoetry ? 0.8 : 0.1)
8// Apply deeper reasoning for astronomy fans.
9.reasoningLevel(likesAstronomy ? .deep : .light)

reasoningLevel accepts values from the ContextOptions.ReasoningLevel enum, and there are four of them: deep, light, moderate, and custom(_:).

At the top, LanguageModelSession.DynamicProfile is a protocol; you write a type conforming to it to manage transitions between multiple profiles, and inside its body you pick profiles with if/else expressions — this is how the compiler verifies the single-active-profile constraint:

swift
1import FoundationModels
2 
3struct PresentationProfile: LanguageModelSession.DynamicProfile {
4 // Create an instance of the server model.
5 var pccModel = PrivateCloudComputeLanguageModel()
6 
7 // The profile's data source.
8 var isEditingImage = true
9 var isEditingAnimation = false
10 
11 // Determine which profile to load based on the current state.
12 var body: some LanguageModelSession.DynamicProfile {
13 if isEditingImage {
14 Profile {
15 Instructions("Help with image-editing steps.")
16 }
17 } else if isEditingAnimation {
18 Profile {
19 Instructions("Improve the animation timing.")
20 }
21 .model(pccModel)
22 .temperature(0.2)
23 .reasoningLevel(.light)
24 } else {
25 Profile {
26 Instructions("Help me improve my presentation.")
27 }
28 .temperature(0.8)
29 }
30 }
31}
32 
33let session = LanguageModelSession(profile: PresentationProfile())

To keep the conversation history at a reasonable length even as the profile changes, you apply the historyTransform modifier on top of the profile:

swift
1Profile {
2 Instructions("Help me generate book ideas.")
3}
4.historyTransform { history in
5 // Apply a local transform before sending the request to the model; this
6 // transform doesn't mutate the transcript's global state.
7 Array(history.suffix(20))
8}

If you want to abstract away repeated behavior across profile transitions, protocol DynamicProfileModifier comes in — its only requirement is defining a type with a func body(content: Self.Content) -> Self.Body signature. The docs describe this as a way to bundle traits like model selection and temperature into a single self-contained modifier. In practice, you can use it to define shared low-temperature + onResponse logging behavior in one place between, say, a "research mode" and a "summarize mode"; the doc's DebugProfileModifier example does exactly this, combining the .temperature(0.0) and .onResponse { ... } traits into a single modifier.

Multimodal prompting: image analysis with Attachment

The Analyzing images with multimodal prompting article defines the Attachment struct for including images alongside text in a prompt. The example in the docs combines a text prompt and two images in the same request, and lets you supply orientation information when needed.

swift
1import CoreGraphics
2import FoundationModels
3 
4func compareImages(imageOne: CGImage, imageTwo: CGImage) async throws -> String {
5 let session = LanguageModelSession()
6 let response = try await session.respond {
7 "Compare these two images by using three bullet points:"
8 
9 Attachment(imageOne)
10 
11 // If the image doesn't already have a rotation applied, transform it
12 // with orientation before sending it to the model.
13 Attachment(imageTwo, orientation: .right)
14 }
15 return response.content
16}

The example scenarios given in the docs cover four main use cases: image classification, summarizing a photo of a document, generating an image description for accessibility, and extracting a recipe idea from a photo of a refrigerator. All four work through the same Attachment API. The doc's "Use built-in image analysis tools" section adds: "The Vision framework provides optical character recognition (OCR) and barcode tools that you can add to a session in the Foundation Models framework." So if you need to extract text, you can add OCRTool to the same session, and if you need to read a barcode, you can add BarcodeReaderTool; it's recommended to supply Attachment/label(_:) when using a tool.

What changes in practice

Our earlier foundational post only covered text-only prompts with @Generable output. With multimodal prompting, you can now attach a photo the user just took directly to a prompt over the same LanguageModelSession and get a structured response back (a recipe object, say, or accessibility-description text); for flows needing text extraction or barcode reading, you add the Vision tool to the same session.

Catching regressions with prompt evaluation (Performance and evaluation)

The official name for this topic group in the Foundation Models reference is "Performance and evaluation," and it consists of three articles: Evaluating prompts to measure performance and improve model responses, Evaluating language model responses, and Analyzing the runtime performance of your Foundation Models app. The middle item points you to a separate framework introduced with iOS 27, Evaluations; Apple's Private Cloud Compute guide points to the same tool: "Start with the on-device model and evaluate it with the Evaluations framework." So the Foundation Models side of the guidance tells you what to test, while Evaluations tells you how to measure it with data sets and metrics. A direct quote from the evaluation article: "Prompt evaluation is the practice of thoroughly testing your prompts against real-world scenarios to measure quality, catch failures early, and confidently improve your feature over time."

The practical consequence of this definition: when you switch a profile with Dynamic Profiles (say, a research mode using reasoningLevel(.deep)), you need to re-test the prompts tied to that profile against real-world scenarios — otherwise you won't notice if one profile transition silently breaks another. Apple also supports this workflow on the Python side: the docs reference a Python Foundation Models SDK at github.com/apple/python-apple-fm-sdk, meaning you can set up these test sets outside of Swift too; I prefer wiring it into CI.

On the runtime side, you can inspect token consumption and response times of the prompt/response loop through Instruments — this matters especially once you move to Private Cloud Compute and the context window grows.

On-device vs. Private Cloud Compute

The Adding server-side intelligence with Private Cloud Compute article summarizes the difference between the two models in a table. The table below carries these differences over verbatim:

Feature
SystemLanguageModel
PrivateCloudComputeLanguageModel
Preserves privacy
Yes
Yes
Works offline
Yes
No
Usage limit
Unlimited
Has a daily limit
Reasoning support
Not supported
Supports multiple levels
Context window
4K
32K

The platform requirement is explicit in the docs: "PrivateCloudComputeLanguageModel is available on iOS 27, macOS 27, watchOS 27, and visionOS 27 or later." So this model won't compile without an availability guard:

swift
1func makeSession() -> LanguageModelSession {
2 if #available(iOS 27.0, macOS 27.0, watchOS 27.0, visionOS 27.0, *) {
3 return LanguageModelSession(model: PrivateCloudComputeLanguageModel())
4 }
5 return LanguageModelSession(model: SystemLanguageModel())
6}

Selection criteria

For long document summarization beyond the 4K context window, or multi-step reasoning tasks, switching to Private Cloud Compute makes sense; but since you're subject to a daily usage limit, staying on the on-device SystemLanguageModel for short, high-volume requests (instant suggestions, say) keeps both latency and cost low. Dynamic Profiles lets you switch between the two exactly where needed, within the same session, without the user noticing.

Post-iOS-27-GA adoption and test checklist

Now that iOS 27 is GA (September 14, 2026), here are the items to check before rolling out Dynamic Profiles and Private Cloud Compute in your production app:

  • Don't forget the availability guard: PrivateCloudComputeLanguageModel only exists on iOS 27/macOS 27/watchOS 27/visionOS 27 and later; a SystemLanguageModel fallback is a must for users on older OS versions.
  • Build a separate evaluation set for each profile: each Profile inside a DynamicProfile can carry a different temperature/reasoningLevel combination, so a single evaluation set can't cover all profiles.
  • Bound history growth with historyTransform: if profile transitions are frequent, the context window (especially at the 4K on-device limit) can fill up quickly without a cutoff like Array(history.suffix(20)).
  • Test Attachment-based flows separately: multimodal prompts have a different latency profile than text-only prompts, so they should be measured separately with Instruments.
  • Isolate-test the Executor layer before shipping your own LanguageModel provider: verify that results streamed through LanguageModelExecutorGenerationChannel are correctly streamed independent of the code that calls session.respond(to:).
  • Wire the evaluation set into CI (recommended): the Python Foundation Models SDK referenced in the docs (github.com/apple/python-apple-fm-sdk) lets you set up this suite outside of Swift; I usually add it to the build pipeline and run it automatically on every profile change, so regressions are caught without waiting for manual testing.
  • Don't skip runtime performance measurement: the Analyzing the runtime performance of your Foundation Models app article recommends tracking token consumption and response times through Instruments; keeping this measurement separate for profiles that move to Private Cloud Compute lets you see early how close you are to the daily usage limit.

Rollout order

It's safer to apply the items above in sequence rather than all at once: first the availability guards and fallback, then the evaluation set for each profile, then isolate-testing the Attachment flows and Executor layer, and finally wiring the evaluation set into CI and making runtime measurement continuous. This ordering surfaces early the errors that show up when you skip a step and jump ahead (testing Private Cloud Compute without an availability guard, for example).

GOLDEN TIP

The most valuable insight in this article

This tip holds the article's most important takeaway.

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Reader Reward

To help you gather your checklist in one place while shipping the 2026 additions to Foundation Models, here's a short summary of the APIs and checklist items covered in this post. Review this list before adding a new Dynamic Profile or Attachment flow.

FAQ

What do Dynamic Profiles do in the Apple Foundation Models framework?

Dynamic Profiles is a three-layer API that lets you change the model, instructions, and session settings at runtime, based on your app's state, within a LanguageModelSession: at the bottom, Dynamic Instructions is re-evaluated on every request; in the middle, Profile associates instructions with settings like model choice and reasoning level; and at the top, DynamicProfile manages transitions between multiple profiles and keeps only one profile active at a time.

How does a third-party LLM provider get connected on iOS via the Language Model protocol?

The provider defines a type conforming to protocol LanguageModel: Sendable; the actual translation work is handled by an Executor that converts framework types into platform-specific requests and streams the results through LanguageModelExecutorGenerationChannel. Integration on the caller's side is a single line: LanguageModelSession(model: MyCustomServerLanguageModel()), after which the same session.respond(to:) API surface continues to be used.

How is on-device model output evaluated, and how are regressions caught?

Apple provides three official guides under "Performance and evaluation": Evaluating prompts to measure performance and improve model responses covers systematically testing prompts against real-world scenarios; Evaluating language model responses shows response scoring over a sample set via the Evaluations framework from iOS 27; and Analyzing the runtime performance of your Foundation Models app covers measuring token consumption and response times with Instruments. You can also set these suites up outside Swift with the Python Foundation Models SDK (github.com/apple/python-apple-fm-sdk); I prefer wiring it into CI.

What changed with Foundation Models in iOS 27?

According to the "What's new" section, three things were added: server-side intelligence via Private Cloud Compute (a larger context window and multi-level reasoning), runtime instruction/model switching via Dynamic Profiles, and analyzing images alongside text via multimodal prompting. iOS 27 reached general availability on September 14, 2026, after being announced at WWDC26 (June 8-12, 2026).

How big is the context window difference between SystemLanguageModel and PrivateCloudComputeLanguageModel?

SystemLanguageModel works offline with unlimited usage at a 4K context window, while PrivateCloudComputeLanguageModel offers a 32K context window and multi-level reasoning, but is subject to a daily usage limit and doesn't work offline; it's also only available on iOS 27/macOS 27/watchOS 27/visionOS 27 and later.

Conclusion

iOS 27 took Foundation Models beyond its single-model, single-instruction limits into a platform that switches at runtime, accepts your own provider, and can be evaluated systematically. With Dynamic Profiles you switch model/instructions by task mode within the same session, bring images into a prompt with Attachment, and verify with the Performance and evaluation guides that these changes don't cause regressions.

If you're not yet comfortable with the basic session/@Generable/streaming API, check out Apple Foundation Models: On-Device LLM Framework first. For the on-device pipeline with Core ML, Apple Intelligence and On-Device AI Pipeline with Core ML completes the picture. For a cross-platform comparison, Gemini Nano: iOS/Android Cross-Platform On-Device AI is a good reference. For general on-device ML infrastructure, see On-Device Machine Learning on iOS and On-Device Machine Learning with Core ML.

Sources

Tags

#Apple Foundation Models#iOS 27#Dynamic Profiles#Swift#On-Device AI#Private Cloud Compute#Prompt Evaluation
Muhittin Camdali

Muhittin Çamdalı

Senior Mobile Engineer

Senior Mobile Engineer with 12+ years of experience. I build mobile products with iOS, Flutter and React Native, working full-stack and AI-native.

iOS Development News

Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.

Your subscription starts when you open the link in the confirmation email and press “Confirm my subscription”. The newsletter keeps open/click statistics; you can unsubscribe anytime with one click. Privacy

Share