Private Cloud Compute answers a background App Intent. We took it out of Siri anyway.
What it took to get an answer from Apple’s larger model with no app on screen, what the two Apple Intelligence models actually are once you budget a prompt for each, and why the Siri lane ended up with no model at all.
In July, on iOS 27 beta 2, I wanted an answer to one question: if Siri runs an App Intent in the background – app process launched, nothing on screen except Siri’s own card – can that intent get a response from Private Cloud Compute?
Nothing in the documentation said yes. Nothing said no. The on-device model was documented to run anywhere; PCC had a restricted entitlement, a network round trip and a quota, and it was easy to imagine any one of those refusing a request that arrived without a foreground app behind it.
Must Read is a book tracker. The feature on the line was “Hey Siri, ask Must Read what to read next” – Siri speaks the top pick, shows three covers, and the reasoning has read the reader’s whole library. That reasoning is a large-model job. So the question was not academic: if PCC would not answer from the background, the feature did not exist.
Three things broke before the question could be asked
1. SwiftData had no context. When Siri cold-launches the app process to run an intent, the view that normally configures the data layer never appears. The first intents ran against a nil modelContext and returned nothing. The fix is small and worth copying: hoist the app’s ModelContainer to file scope so there is exactly one, and make every intent’s perform() open with a call that configures the store only if nobody has.
// DataService.swift
func ensureConfigured() {
guard modelContext == nil else { return }
configure(modelContext: appModelContainer.mainContext)
} Every intent that reads the store now starts with await DataService.shared.ensureConfigured(). Weeks later the same bug turned up in the BGTask handler, which had never been brought along. Background entry points are all the same entry point.
2. .result(opensIntent:) is an action, not an offer. I had written the fallback as “if no model is available, return a dialog and an opensIntent that opens the Ask Rufus sheet”. On beta 2 that force-opened the app on every run, success included, and killed the whole point of a hands-free answer. The rule I ended up with: anything optional goes into the snippet as a Button(intent:); the intent’s result must never carry an open it does not need.
3. Siri had opinions about the phrase. “What should I read next in Must Read” never reached the intent. The shape <free text> in <app> is routed to the system search schema first, and the word “read” is contested by Messages and Mail before any App Shortcut sees it. “Ask Must Read what to read next” works every time. Test phrases by voice, on a device, before you localise them into thirty languages.
The answer
Yes. With the three fixes in, the intent looked like this:
func perform() async throws
-> some IntentResult & ProvidesDialog & ShowsSnippetIntent {
await DataService.shared.ensureConfigured()
// PCC first, on-device second, per the engine's ladder.
let recommendation = try await RufusEngine.shared.recommend()
let top = recommendation.picks[0]
// Covers travel as JPEG bytes: AsyncImage never renders in a snippet.
return .result(
dialog: IntentDialog("Top pick: \(top.title) – \(top.reason)"),
snippetIntent: NextReadSnippetIntent(payload: payload)
)
} and Siri spoke a pick chosen by Private Cloud Compute, with a reason that referred to books actually on my shelf, from a process that never came to the foreground. The engine’s own log line named the backend: Private Cloud Compute. That was the open question of the summer, closed on a Monday evening.
Two smaller findings from the same week, for anyone about to do this:
PrivateCloudComputeLanguageModelis an iOS 27-only type, and a stored property of an iOS 27-only type cannot live inside a type that is available earlier. The pattern that compiles is a private holder gated twice – once for the OS, once for the toolchain, because the iOS 26.5 SDK carries no PCC symbols at all and a stable-Xcode build must compile the whole branch away.
#if compiler(>=6.4)
@available(iOS 27, *)
private enum PCCHolder {
static let model = PrivateCloudComputeLanguageModel()
}
#endif - FoundationModels asserts on the simulator when a model is instantiated. Guard with
#if targetEnvironment(simulator)and let the simulator exercise the no-model path on purpose. That path needs testing more than the model does.
What the two models actually are
Building the prompt for both tiers made the difference concrete. These are the budgets in the engine today:
| On-device | Private Cloud Compute | |
|---|---|---|
| Context window | ~4k tokens | ~32k tokens |
| Catalogue books in the prompt | 50 | 1,000 |
| Prompt ceiling | 2,200 tokens | 24,000 tokens |
| Response ceiling | 500 tokens | 800 tokens |
| Latency the reader sees | a second or so | several seconds |
Same task, same @Generable output type, same tool. One of them can hold a reader’s shelf and a country’s curated lists in one breath; the other can hold a shortlist. Neither is “the AI”. They are two different tools with two different failure modes, and the app is better for treating them that way.
Why Siri doesn't use it
This month the Siri lane was moved off the language model entirely. Not because PCC failed – it did not – but because of what a voice answer has to be.
- It has to exist on every phone. The model path is iOS 27 and Apple Intelligence hardware. On a device without either, Siri said “I couldn’t pick right now – open Rufus and I’ll try there”, while the Ask Rufus sheet on the same phone answered fine. A feature that apologises to most of its users is not a feature.
- It has to be quick. A person who asked Siri a question is standing there. Several seconds of PCC is fine under a sheet with a thinking animation; in Siri it is a pause.
- It has to speak the reader’s language. Must Read ships in 32 languages. Apple Intelligence writes in a fraction of them, and a Russian reader should not be answered in English because the model prefers it.
So Siri now runs the deterministic recommender – the same candidate engine the sheet uses, reading only local caches – and the sentence under each pick comes from templates assembled from facts the engine proved about the book. It answers on iOS 18, in every language, immediately. The language model’s job shrank to one thing: in the Ask Rufus sheet, on iOS 27, it rewrites the sentence under a pick, PCC first, on-device second, template if neither. That is the whole ladder, and the reader’s library is never handed to a third-party model at any rung of it.
The 3.6 promotional text says it in one line: Ask Rufus picks your next book from the ones you have finished. On iOS 27, Apple Intelligence writes the reason why.
If you're building on Foundation Models
- Decide what the model is for before deciding which model. Ours went from “choose the book” to “phrase the reason”, and the feature got better at both.
- Prove the scary path first. Background PCC was the unknown; a few days of device work answered it. Everything after was engineering.
- Budget the prompt per tier, in tokens, as data. The 4k/32k gap is not a detail; it is the design.
- Write the no-model path as if it were the product, because for most of your users, this year, it is.
Must Read 3.6 ships with iOS 27. The App Intents, the entity model and the three-tier ladder are all in it.