‹ All articles
Apple
Apple IntelligenceFoundationModelsPCC

Foundation Models, one year later

From a small on-device model to Private Cloud Compute, Dynamic Profiles and Evaluations: what changed, and what I learned along the way.

Carolane Lefebvre11 min
Foundation Models, one year later

A little over a year ago, Foundation Models opened up a small on-device model to developers: free and private. Today, the same API can talk to an Apple server model, to Claude or to Gemini. Keepio was built on that first version, back with iOS 26. I wanted to take stock of everything that has changed since, on the model side and on the tooling side, starting from what I know best: what I went through while integrating all of this into my apps.


At glance

Developers have only had access to the models since iOS 26Apple Foundation Models milestones, June 2024 to September 2026
  1. AFM 1 announced (WWDC24)
  2. iOS 18.1: Apple Intelligence
  3. AFM 2 and framework announced
  4. iOS 26: framework available
  5. iOS 26.4: model updated
  6. AFM 3, PCC and Dynamic Profiles
  7. iOS 27: public release

Apple has been making models since 2024, but we developers have only had access to them since iOS 26. That's where I'm starting from.


First, the models

Two things often get mixed up: the models (which Apple calls Apple Foundation Models, or AFM) and the Foundation Models framework (the Swift API that lets us talk to them). The models have existed since 2024; the framework only arrived with the second generation.

The first generation was announced at WWDC 2024 and reached everyone with iOS 18.1, at the end of October 2024. It powers Apple Intelligence features (Writing Tools, notification summaries…), but we developers couldn't touch it.

The second generation arrived with iOS 26, in September 2025. That's the one that finally opened the door for us: a small model of about 3 billion parameters that runs right on the iPhone. For perspective, that's tiny compared to the models we use in an online chat, but it's free, it works offline, and nothing leaves the device.

The third generation, AFM 3, was announced in June 2026 and shipped with iOS 27 in September. This time it's not one model but a small family, built with Google: two models that run on the device, and others, bigger, that run on Apple's servers. The small local model stays roughly the same size, but it's noticeably better, and you can now show it images.


What we had in 2025, and where it fell short

At launch, the API was deliberately simple. You create a LanguageModelSession, give it instructions ("you are an assistant that…"), send it some text, and it answers.

The really good idea was guided generation. Instead of getting free text back that you then have to parse, you describe a Swift struct with @Generable, and the model fills it in directly:

@Generable
struct SuggestedTags {
    @Guide(.maximumCount(3))
    var tags: [String]
}

let session = LanguageModelSession(instructions: "Suggest tags for this note.")
let result = try await session.respond(to: noteContent, generating: SuggestedTags.self)

That's exactly how tag suggestions work in Keepio. The app's other AI features (the project roadmap, natural-language Quick Add, extracting tasks from a note) all rely on the same principle.

And it was while building them that I ran into the model's limits.

It doesn't think much. When I asked it to rank all the tasks in a project, the result was shaky, and almost everything came out as high priority. But if I gave it a single task to judge, it did fine. So I flipped the logic: the model evaluates each task one at a time, and my Swift code does the ranking.

It sometimes makes things up. In Quick Add, it could come up with a date that appeared nowhere in what the user had typed. So I check that the suggested date really exists in the text, and if not, I run a second attempt.

It has little working memory. A model doesn't read words but tokens, small pieces of words. What it can handle at once is its context: at launch, 4,096 tokens, instructions, input and answer included. That's a few pages, no more. A long note or a big project, and you get an error. The worst part was that there was no way to know in advance whether it would fit.

Add to that guardrails that were sometimes too cautious, blocking perfectly harmless content, and the fact that not everyone has an Apple Intelligence–compatible device: you always had to plan for a version of the app without AI.


Spring 2026: the model changes under our feet

With iOS 26.4, released at the end of March 2026, Apple updated the model without changing generations. It follows instructions better, uses tools better, and its guardrails block fewer things for no reason.

That's good news, but it also means something new for us: the model can change under an app that's already shipped, just because the user updated their iPhone. Apple says it clearly: you need to retest your prompts with every iOS release.

For me, the most useful addition in this release is a quieter one. You can finally count tokens before sending anything, with tokenCount(for:), and know the context size with contextSize. No more discovering the limit by hitting it: you can trim or summarize a note that's too long before sending it.

iOS 27: we can finally choose our model

This is the biggest change of the year. Until now, the framework talked to a single model, the one on the iPhone. With iOS 27, it becomes a kind of universal plug: you keep exactly the same code (instructions, @Generable types, tools), and you just choose which model sits behind it.

// The iPhone's model
let session = LanguageModelSession(model: SystemLanguageModel())

// Apple's model on its servers: only one line changes
let session = LanguageModelSession(model: PrivateCloudComputeLanguageModel())

You can plug in three kinds of models:

  • the local model, still free and offline;

  • the Private Cloud Compute model (PCC), bigger, running on Apple's servers;

  • models from other companies, like Claude or Gemini, through Swift packages provided by Anthropic and Google. There, it's their regular API, with a key and a bill.


Private Cloud Compute, or PCC

Private Cloud Compute is the set of servers Apple already uses for Apple Intelligence requests that are too heavy for the iPhone, with the promise that nothing is stored there. Since iOS 27, our apps can use them too, with no API key or account to create.

The PCC model has two big advantages: a context eight times larger than the local model's, and above all the ability to reason before answering. You can choose how much it thinks, from light to deep, in exchange for a slower answer.

On the other hand, it needs a network connection, each user has a daily request quota, and you have to ask Apple for access. It's free for small developers, as long as all their apps combined stay under 2 million downloads.

What it looks like (e.g. in Caffio)

I tried PCC in Caffio, my coffee tracking app, before Keepio. I use it for just one thing: analyzing consumption over the week or the month, where there's a lot of data to cross-reference. Everything else stays on the iPhone.

Three important remarks when implementing PCC:

  1. Please, you always need a plan B. If PCC isn't available (no network, quota reached), you need a fallback to the local model with a shorter prompt, and as a last resort to hand-written text. The screen is never empty or spinning into infinity.

  2. Show the quota, don't let it surprise! Rather than an alert when it's too late, the app shows when you're getting close to the limit. Apple even provides the button that offers to raise your quota through iCloud+.

  3. Not mandatory but highly recommended: let your user stay in control. A "100% on this iPhone" setting turns PCC off completely. And I never send the most personal data, like sleep in Caffio.


Dynamic Profiles and the other new tools

In 2025, each AI feature in Keepio created its own session, asked its question, and that was it. iOS 27 lets you go further: build multi-step flows where the model changes roles along the way.

Dynamic Profiles

Picture a conversation with an assistant in Keepio. At first, you dump everything you have to do on it: the small local model is enough for that. Then you ask it to organize it all into a plan: now you'd want the PCC model, which can reason.

Before, you needed two separate sessions, and the second one knew nothing about the first. A DynamicProfile fixes that: you describe several "profiles" for a single session, each with its own instructions, tools and model, and the session switches between them based on the app's state, keeping the whole conversation.

struct AssistantProfile: LanguageModelSession.DynamicProfile {
    var mode: Mode

    var body: some DynamicProfile {
        switch mode {
        case .capture:
            Profile { CaptureInstructions() }
                .model(SystemLanguageModel())
        case .planning:
            Profile { PlanningInstructions() }
                .model(PrivateCloudComputeLanguageModel())
                .reasoningLevel(.deep)
        }
    }
}

If this reminds you of SwiftUI, that's normal: it's the same declarative logic, with a body re-evaluated on every message.

Around it, Apple adds a few handy building blocks. DynamicInstructions let you split your instructions into reusable pieces. historyTransform lets you send only part of the conversation to the model, for example the last twenty messages, without deleting anything. And a set of callbacks (onToolCall, onResponse…) lets you react at each step.

Small disappointment: automatic summarization of a conversation that gets too long doesn't exist. It's still up to us to build it.

Better control over tools

A tool is a function of your app that the model can call on its own, for example "search my tasks". Until now, it decided by itself whether to use it or not.

With ToolCallingMode, you can now force it to use a tool before answering (.required), or on the contrary forbid it (.disallowed). Handy to make sure it looks up the information in the user's data instead of making it up. Careful though: in required mode, you need to give it a way out, otherwise it calls the tool in a loop.

Images and Apple's tools

The local model now accepts images in the prompt. And Apple provides ready-made tools based on Vision: one to read text in an image, one to read barcodes, and one to search the app's Spotlight index.

For Keepio, I can see the use right away: take a photo of a whiteboard or a handwritten list, and turn it into tasks. Or ask the app a question about your own notes, without anything leaving the iPhone.


Finally, tools to know whether it works

This might be what I missed the most in 2025. A model never answers exactly the same way twice, so a classic unit test isn't much use. To tune Keepio's prioritization, I was testing by hand on a small set of projects and trusting my gut.

iOS 27 brings the Evaluations framework. The idea: you give it a list of examples, it runs the AI feature on each one, and it scores the results. Some checks are done in code ("there are indeed between 1 and 3 tags"). For what can't be measured that simply ("are these tags relevant?"), you can ask a second, bigger model to act as the judge.

It plugs into Swift Testing, and each run produces a report in Xcode. Apple recommends starting small, with about twenty examples. When you change a prompt, or when a new iOS version changes the model, you run it again and compare.

To see what happens at runtime, there's now usage on every response (how many tokens were consumed) and a Foundation Models instrument in Instruments, which shows the prompts sent, the responses and the tool calls.

And to test outside the app, Apple shipped an fm command in the macOS 27 terminal, a Python SDK, and even open-sourced the framework.


In one year, Foundation Models went from an API for a small local model to a real toolbox: you pick your model, chain steps together, and can finally measure what you're doing. The model got better, but it's mostly everything around it that changed.

That said, one rule hasn't moved: AI works best when you give it little to do, and let the code handle the rest. I came to the same conclusion in Keepio and in Caffio, without looking for it. A bigger model like PCC's doesn't change that, it just pushes the limit a bit further.

For Keepio, the next steps are pretty clear: move the project roadmap to PCC, where the lack of reasoning is felt the most, and set up Evaluations so I stop tuning my prompts by feel.

And on Apple's side, would be great to have more possibilities as a developer, for example an automatic summarization for conversations that get too long? Just sayin’…

Have fun coding!