Per usare Apple Intelligence in un'app Capacitor, installa @capgo/capacitor-llm, chiama setModel({ path: 'Apple Intelligence' }), controlla getReadiness(), poi crea una chat e ricevi le risposte in streaming tramite l' textFromAi event. Sotto il cofano, il plugin usa il framework Foundation Models di Apple, introdotto con iOS 26, che esegue il modello linguistico on-device di Apple senza chiave API, senza costi per richiesta e senza che i dati lascino il telefono.
Questa guida copre cosa offre il framework, i requisiti del dispositivo, un'integrazione TypeScript completa, la context window da 4.096 token, la gestione degli errori, le note per l'App Review e come lo stesso code funziona su Android.
Cosa offre Apple Intelligence agli sviluppatori di app
Con iOS 26, Apple ha aperto il suo foundation model on-device alle app di terze parti tramite il Modelli di base framework. In Swift lavori con SystemLanguageModel e LanguageModelSession. Il plugin li incapsula in modo che il tuo code web possa usarli:
| Concetto Foundation Models | Equivalente del plugin |
|---|---|
SystemLanguageModel.default.availability |
getReadiness() |
LanguageModelSession |
createChat() restituisce un ID |
session.streamResponse(to:) |
sendMessage() più textFromAi eventi |
| Errori lanciati durante la generazione | generationError evento e una promessa rifiutata sendMessage |
Il modello ha circa 3 miliardi di parametri. È ottimo per riassumere, riscrivere, estrarre, classificare e generare testi brevi. Non sostituisce un modello cloud di grandi dimensioni per domande aperte su conoscenze generali, e Apple lo dice chiaramente nelle proprie linee guida. Progetta le funzionalità intorno al testo che l'utente ha già.
Requisiti di dispositivo e OS
| Requisito | Dettaglio |
|---|---|
| iPhone | iPhone 15 Pro, 15 Pro Max e modelli iPhone 16 e successivi |
| iPad | iPad mini (A17 Pro), iPad con chip M1 o successivi |
| Mac | Mac con Apple silicon (rilevanti per i test nel Simulator) |
| OS | iOS 26 / iPadOS 26 o versioni successive |
| Impostazione | Apple Intelligence attivata in Impostazioni > Apple Intelligence & Siri |
| Lingua | Lingua del dispositivo e di Siri impostata su una lingua supportata da Apple Intelligence |
| Archiviazione | Apple indica circa 7 GB di spazio libero per i modelli Apple Intelligence |
| Toolchain | Xcode 26, richiesto da App Store Connect anche per gli upload da aprile 2026 |
Se il tuo progetto compila ancora con una versione precedente di Xcode, vedi Il requisito Xcode 26 di Apple per le app Capacitor. Il deployment target minimo della tua app non deve cambiare. Il plugin supporta iOS 15 e versioni successive e tocca i Foundation Models solo su iOS 26.
Installa il plugin
bun add @capgo/capacitor-llm
bunx cap sync ios
Usa il plugin 8.x con Capacitor 8. Non servono chiavi Info.plist o entitlements per Apple Intelligence. Se usi ancora Capacitor 7, la guida all'aggiornamento a Capacitor 8 viene prima. Documentazione di riferimento: /docs/plugins/llm/.
Passaggio 1: Seleziona il modello e verifica la disponibilità
setModel({ path: 'Apple Intelligence' }) seleziona il modello di sistema e si risolve subito. La disponibilità effettiva arriva da getReadiness(), che mappa l'enum di disponibilità di Apple in stringhe leggibili:
readiness valore |
Significato | Cosa mostrare |
|---|---|---|
ready |
Modello disponibile | La funzionalità |
Device is not eligible for Apple Intelligence |
Hardware non supportato | Nascondi la funzionalità o usa un fallback cloud |
Apple Intelligence is not enabled |
L'utente l'ha disattivata | Un suggerimento per attivarla nelle Impostazioni |
Model is not ready |
Modello ancora in download o in preparazione | “In preparazione”, riprova più tardi |
Apple Intelligence requires iOS 26.0 or later |
iOS più vecchi | Nascondi la funzionalità |
import { Capacitor } from '@capacitor/core';
import { CapgoLLM } from '@capgo/capacitor-llm';
export type AIState = 'ready' | 'enable-in-settings' | 'preparing' | 'unsupported';
export async function initAppleIntelligence(): Promise<AIState> {
if (Capacitor.getPlatform() !== 'ios') return 'unsupported';
await CapgoLLM.setModel({ path: 'Apple Intelligence' });
const { readiness } = await CapgoLLM.getReadiness();
if (readiness === 'ready') return 'ready';
if (readiness.includes('not enabled')) return 'enable-in-settings';
if (readiness.includes('not ready')) return 'preparing';
return 'unsupported';
}
Ricontrolla quando l'app torna in primo piano. Gli utenti spesso abilitano Apple Intelligence dopo aver visto il tuo suggerimento, e il download del modello può completarsi mentre l'app è in background.
import { App } from '@capacitor/app';
App.addListener('appStateChange', async ({ isActive }) => {
if (isActive) updateAIState(await initAppleIntelligence());
});
Passaggio 2: Crea una chat con istruzioni
Una chat corrisponde a un LanguageModelSession, che mantiene la cronologia della conversazione. Il createChat() non accetta opzioni, quindi inserisci le istruzioni all'inizio del primo messaggio:
const INSTRUCTIONS =
'You summarize meeting notes. Reply with at most 5 bullet points. ' +
'Keep names and dates exactly as written. Do not add information.';
const { id: chatId } = await CapgoLLM.createChat();
Usa una chat per ogni task. Una chat che continua a crescere finirà per raggiungere il limite di contesto descritto di seguito.
Passaggio 3: Fai streaming della risposta nella UI
Su iOS, ogni textFromAi l'evento contiene uno snapshot della risposta fino a quel momento, perché è così che Foundation Models gestisce lo streaming. Su Android con Gemini Nano, gli eventi contengono solo il nuovo testo. Questo helper gestisce entrambi i casi e restituisce il testo finale:
import { CapgoLLM } from '@capgo/capacitor-llm';
function mergeChunk(current: string, chunk: string) {
return chunk.startsWith(current) ? chunk : current + chunk;
}
export async function ask(chatId: string, message: string, onText: (t: string) => void) {
let text = '';
let finish!: (value: string) => void;
let fail!: (error: Error) => void;
const done = new Promise<string>((resolve, reject) => {
finish = resolve;
fail = reject;
});
const handles = await Promise.all([
CapgoLLM.addListener('textFromAi', (e) => {
if (e.chatId !== chatId) return;
text = mergeChunk(text, e.text);
onText(text);
}),
CapgoLLM.addListener('aiFinished', (e) => {
if (e.chatId === chatId) finish(text);
}),
CapgoLLM.addListener('generationError', (e) => {
if (!e.chatId || e.chatId === chatId) fail(new Error(e.error));
}),
]);
try {
await CapgoLLM.sendMessage({ chatId, message });
return await done;
} finally {
await Promise.all(handles.map((h) => h.remove()));
}
}
Mettiamo tutto insieme: riassumere le note sul dispositivo
export async function summarizeNotes(notes: string, render: (t: string) => void) {
const state = await initAppleIntelligence();
if (state !== 'ready') throw new Error(`AI unavailable: ${state}`);
const { id } = await CapgoLLM.createChat();
const prompt = `${INSTRUCTIONS}\n\nNotes:\n${notes.slice(0, 8000)}\n\nSummary:`;
return ask(id, prompt, render);
}
Il slice è una protezione rudimentale contro input enormi. Un approccio migliore è riassumere i documenti lunghi in chunk e poi riassumere i riassunti, ognuno in una nuova chat.
In un componente del framework, disabilita il pulsante mentre ask è in esecuzione. Il plugin rifiuta una seconda sendMessage sulla stessa chat con il messaggio “la chat sta rispondendo, attendi prima di porre nuove domande”.
La context window da 4.096 token
Il modello on-device ha una context window di 4.096 token. Tutto contribuisce al limite: istruzioni, ogni turno precedente della chat, il nuovo prompt e la risposta in generazione. Quando una sessione lo supera, Foundation Models restituisce un errore “exceeded context window size”, che ti arriva come generationError evento e una promessa rifiutata sendMessage.
Regole pratiche:
- Una chat per task, non una chat per schermata.
- Mantieni le istruzioni brevi e specifiche.
- Per chat multi-turno, avvia una nuova chat quando diventa lunga e passa un breve riassunto della conversazione precedente.
- Chiedi output concisi. Le risposte lunghe consumano lo stesso budget.
Ottimizzazione output
Sul percorso Apple Intelligence il plugin usa le impostazioni di generazione predefinite del sistema. temperature, topk, e maxTokens nella setModel si applicano ai percorsi dei modelli personalizzati LiteRT-LM e MediaPipe. Per guidare il modello di Apple, affidati al prompt: indica quanto deve essere lunga la risposta, specifica il formato (elenchi puntati, JSON, una frase) e includi un breve esempio quando il formato conta.
Lo stesso code su Android
Cambia solo la selezione del modello:
const path = Capacitor.getPlatform() === 'ios' ? 'Apple Intelligence' : 'Gemini Nano';
await CapgoLLM.setModel({ path });
Su Android, setModel rifiuta quando Gemini Nano non è disponibile e puoi usare come fallback un modello Gemma scaricabile tramite lo stesso plugin. I dettagli sono in come usare Gemini Nano in un'app Capacitor.
Test
- Dispositivo reale: la via affidabile. Usa un iPhone supportato su iOS 26 con Apple Intelligence attivo.
- Simulatore: può usare il modello se il Mac host esegue macOS 26 su Apple silicon con Apple Intelligence attivo. Comportamento e velocità sono diversi da quelli di un telefono, quindi verifica sull'hardware.
- Stati non supportati: testa con Apple Intelligence disattivato per verificare la UI “abilita in Impostazioni”, e su un iPhone o una versione di iOS precedente per controllare che la funzionalità si nasconda correttamente.
App Review e privacy
Prompt e risposte restano sul dispositivo, quindi la generazione on-device non aggiunge di per sé alcuna raccolta dati alla tua etichetta privacy. Restano comunque due cose a tuo carico:
- Apple pubblica i requisiti di utilizzo accettabile per il framework Foundation Models. Leggili prima di pubblicare, soprattutto per le funzionalità che generano contenuti mostrati ad altri utenti.
- Il modello ha guardrail integrati e potrebbe rifiutare alcuni prompt o output. Tratta un rifiuto come un risultato normale e mostra un messaggio chiaro.
Poiché i prompt sono semplici stringhe nel tuo code web, puoi migliorarli dopo il lancio con Live updates di Capgo invece di aspettare una nuova build per l'App Store. Le modifiche al code nativo, inclusi gli upgrade dei plugin, richiedono ancora un rilascio sugli store, che puoi buildare in cloud con Build Capgo.
Errori comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
readiness è “Device is not eligible” |
iPhone precedenti al 15 Pro o iPad non supportati | Nascondi la funzionalità o usa un modello server |
readiness è “not enabled” |
L'utente ha disattivato Apple Intelligence | Mostra un hint in Impostazioni, ricontrolla al resume |
readiness è “Model is not ready” |
Modello ancora in download | Mostra uno stato di preparazione, riprova più tardi |
sendMessage rifiuta con “error deviceNotEligible error” |
Invio senza verificare la disponibilità | Gate attivo getReadiness() per primo |
| “chat non trovata” | Chat creata prima di una nuova setModel chiamata |
In chiamata setModel cancella le chat, creane una nuova |
| “la chat sta rispondendo” | Secondo messaggio inviato durante lo streaming | Disabilita l'input fino a aiFinished |
generationError menzionando la context window |
Chat troppo lunga | Inizia una nuova chat con un riassunto |
| Errore di build sui Foundation Models | Xcode precedente alla versione 26 | Aggiorna Xcode o fai la build nel cloud |
In sintesi
Apple Intelligence porta un modello linguistico gratuito, privato e offline su ogni iPhone compatibile con iOS 26, e un'app Capacitor può usarlo con poche righe di TypeScript. Seleziona il modello, vincola la UI a getReadiness(), tieni ogni task nella sua chat breve, fai lo streaming degli snapshot nella UI e gestisci i rifiuti e gli errori di contesto come esiti normali. Per una visione più ampia su come rilasciare funzionalità AI nelle app ibride, leggi perché Capacitor è ideale per le app mobile AI.