Se você é desenvolvedor iOS, com certeza já ouviu falar do Foundation Models framework — provavelmente a novidade mais empolgante da WWDC 2025 pra quem trabalha com Swift. E olha, não é exagero. Pela primeira vez, a Apple abriu acesso direto ao modelo de linguagem por trás do Apple Intelligence, e a melhor parte: tudo roda no próprio dispositivo. Sem API keys. Sem custos de nuvem. Sem internet. Privacidade total.
Neste guia, vou cobrir tudo que você precisa pra começar a usar o Foundation Models nos seus apps — da configuração inicial até recursos avançados como Guided Generation com @Generable, streaming de respostas e Tool Calling. Se você quer adicionar IA nativa aos seus projetos SwiftUI sem depender de serviços externos, cola aqui.
O Que é o Foundation Models Framework?
O Foundation Models é um framework que chegou no iOS 26 e dá aos desenvolvedores acesso ao modelo de linguagem on-device da Apple — estamos falando de um LLM com aproximadamente 3 bilhões de parâmetros. É o mesmo modelo que já alimenta funcionalidades do Apple Intelligence como resumo de textos, sugestões inteligentes e respostas automáticas.
Diferente de soluções na nuvem como ChatGPT ou Claude, que dependem de servidores remotos e conexão com a internet, o modelo da Apple roda inteiramente no hardware do usuário, aproveitando o Apple Silicon. Na prática, isso traz três vantagens bem claras:
- Privacidade absoluta — nenhum dado sai do dispositivo do usuário
- Latência mínima — sem roundtrips de rede, as respostas chegam bem mais rápido
- Funciona offline — o modelo opera sem qualquer conexão com a internet
O framework está disponível pra iOS 26, iPadOS 26, macOS Tahoe e visionOS 26, e suporta 10 idiomas nativamente — incluindo português.
Requisitos e Configuração Inicial
Antes de meter a mão na massa, confira se seu ambiente tá pronto:
- Xcode 26 instalado (disponível no Mac App Store ou no site da Apple)
- macOS Tahoe (macOS 26) ou superior
- Dispositivo com Apple Silicon (iPhone 15 Pro ou superior, iPad com chip M1+, Mac com Apple Silicon)
- Apple Intelligence ativado nas configurações do sistema
Com tudo certo, o primeiro passo é importar o framework:
import FoundationModels
Só isso. Sem dependências externas, sem pods, sem SPM packages. O framework já vem integrado ao SDK do iOS 26. Confesso que quando vi isso pela primeira vez, achei quase bom demais pra ser verdade.
Verificando a Disponibilidade do Modelo
Nem todo dispositivo suporta o Foundation Models — e esse é um detalhe que muita gente esquece. Antes de usar qualquer funcionalidade, você precisa verificar se o modelo tá disponível. A Apple fornece uma API pra isso através do SystemLanguageModel:
let model = SystemLanguageModel.default
switch model.availability {
case .available:
// Modelo pronto — pode mostrar a UI principal
print("Modelo disponível!")
case .unavailable(let reason):
switch reason {
case .deviceNotEligible:
print("Este dispositivo não suporta Apple Intelligence.")
case .appleIntelligenceNotEnabled:
print("Ative o Apple Intelligence nas Configurações.")
case .modelNotReady:
print("O modelo está sendo baixado. Tente novamente em breve.")
@unknown default:
print("Modelo indisponível.")
}
}
São três razões possíveis pra indisponibilidade:
- deviceNotEligible — o hardware não suporta (chips mais antigos, basicamente)
- appleIntelligenceNotEnabled — o usuário ainda não ativou o Apple Intelligence
- modelNotReady — o modelo tá sendo baixado ou o sistema bloqueou temporariamente (bateria baixa, Modo Jogo ativo, essas coisas)
Num app de produção, use essa verificação pra adaptar a UI. Sempre ofereça um fallback — não deixe o usuário num beco sem saída.
Primeira Interação: LanguageModelSession
O LanguageModelSession é o coração da coisa toda. Pense nele como uma sessão de conversa — você manda perguntas, recebe respostas. Simples assim.
Uso Básico (Single-Turn)
let session = LanguageModelSession()
let response = try await session.respond(to: "Explique o padrão MVVM em Swift")
print(response.content)
Três linhas de código. Só três. E você já tá interagindo com um LLM de 3 bilhões de parâmetros rodando no dispositivo. A resposta vem como String, pronta pra renderizar na UI.
Conversas Multi-Turn
Pra manter o contexto entre perguntas — tipo uma conversa de verdade — declare a sessão como variável de estado no SwiftUI:
struct ChatView: View {
@State private var session = LanguageModelSession()
@State private var question = ""
@State private var answer = ""
var body: some View {
VStack(spacing: 16) {
ScrollView {
Text(LocalizedStringKey(answer))
.padding()
}
HStack {
TextField("Faça uma pergunta...", text: $question)
.textFieldStyle(.roundedBorder)
Button("Enviar") {
Task {
let response = try await session.respond(to: question)
answer = response.content
}
}
.disabled(session.isResponding)
}
.padding()
}
}
}
Ao reutilizar a mesma instância de LanguageModelSession, o modelo mantém o histórico da conversa. Perguntas de acompanhamento funcionam naturalmente — ele entende referências a respostas anteriores sem problema.
E aquela propriedade isResponding? Super útil. Use pra desabilitar botões enquanto o modelo gera a resposta. Evita aquele bug clássico de requisição duplicada.
Instruções Personalizadas
Você pode configurar o comportamento do modelo com instruções na criação da sessão:
let session = LanguageModelSession(
instructions: "Você é um assistente especializado em receitas brasileiras. Responda sempre em português, com medidas no sistema métrico."
)
Funciona como um system prompt — define o papel, o tom e os limites antes de qualquer interação do usuário. Honestamente, com boas instruções o modelo surpreende bastante, mesmo sendo "só" 3B de parâmetros.
Streaming de Respostas
Ficar esperando a resposta completa antes de mostrar qualquer coisa na tela é uma experiência péssima pro usuário (e convenhamos, pra qualquer pessoa). O Foundation Models resolve isso com streaming nativo — as respostas vão aparecendo progressivamente, palavra por palavra, do jeitinho que você já viu no ChatGPT.
func generateAnswer() async {
do {
answer = ""
let stream = session.streamResponse(to: question)
for try await partialResponse in stream {
answer = partialResponse.asPartiallyGenerated()
}
} catch {
answer = "Erro ao gerar resposta: \(error.localizedDescription)"
}
}
O streamResponse(to:) retorna um AsyncSequence que emite fragmentos parciais da resposta. A cada iteração, você atualiza a view e o usuário vê o texto sendo "digitado" em tempo real.
Essa abordagem é genial porque transforma latência em experiência. Em vez de um spinner girando sem fim, o usuário já começa a ler enquanto o modelo continua trabalhando.
Guided Generation: Respostas Tipadas com @Generable
Até aqui, trabalhamos com respostas em texto livre — strings puras. Mas e quando você precisa de dados estruturados? Um objeto Swift com propriedades certinhas, pronto pra usar na UI ou salvar no banco?
É aí que entra o Guided Generation, e na minha opinião é o recurso mais poderoso de todo o framework. Com o macro @Generable, você define a estrutura de saída que quer e o modelo gera os dados diretamente como um tipo Swift nativo.
Definindo um Tipo @Generable
import FoundationModels
@Generable
struct QuizQuestion {
let text: String
let choices: [String]
let answer: String
let explanation: String
}
O macro @Generable gera automaticamente um schema JSON em tempo de compilação. Quando o modelo retorna a resposta, ela é decodificada direto no seu tipo Swift. Sem parsing manual, sem regex, sem surpresas desagradáveis.
Gerando uma Instância
let session = LanguageModelSession(
instructions: "Você é um professor de história do Brasil."
)
let response = try await session.respond(
to: "Crie uma pergunta sobre a Proclamação da República",
generating: QuizQuestion.self
)
let question = response.content
print(question.text) // A pergunta gerada
print(question.choices) // Array com as opções
print(question.answer) // A resposta correta
print(question.explanation) // Explicação detalhada
O parâmetro generating: diz ao modelo exatamente qual estrutura preencher. O resultado é type-safe — acesse propriedades diretamente, sem casting ou unwrapping de JSON. É a integração que todo dev Swift sonhava.
Refinando com @Guide
Quer mais controle? O macro @Guide permite adicionar restrições e descrições em propriedades individuais:
@Generable
struct QuizQuestion {
@Guide(description: "A pergunta do quiz, clara e objetiva")
let text: String
@Guide(.count(4), description: "Exatamente 4 alternativas")
let choices: [String]
@Guide(description: "A alternativa correta, idêntica a uma das choices")
let answer: String
@Guide(description: "Explicação breve de por que essa é a resposta correta")
let explanation: String
}
As restrições disponíveis incluem:
.count(n)— tamanho exato do array.minimumCount(n)— mínimo de elementos.maximumCount(n)— máximo de elementos.range(min...max)— intervalo numéricodescription:— contexto textual pra qualquer propriedade
Detalhe importante: a ordem das propriedades importa (e muito). O modelo gera os valores sequencialmente, então propriedades que dependem de outras — como explanation, que faz referência a answer — precisam ser declaradas depois.
Enums Também Funcionam
@Generable
enum Difficulty: String, Codable {
case facil
case medio
case dificil
}
Enums com @Generable restringem a saída do modelo aos cases definidos — perfeito pra classificações e categorizações onde você não quer que o modelo invente valores aleatórios.
Streaming com Guided Generation
E aqui fica ainda melhor: Guided Generation e streaming combinam perfeitamente. O modelo gera as propriedades uma a uma e você recebe snapshots parciais:
let stream = session.streamResponse(
to: "Sugira 3 receitas com frango",
generating: [Recipe].self
)
for try await partialRecipes in stream {
recipes = partialRecipes // Atualiza a UI progressivamente
}
A cada iteração, mais propriedades vão sendo preenchidas. Dá pra animar as transições na UI conforme os dados chegam — uma experiência muito mais rica que ficar olhando pra um loading spinner.
Tool Calling: Estendendo as Capacidades do Modelo
O modelo on-device é impressionante, mas convenhamos — ele tem limitações. Não acessa a internet, não sabe a previsão do tempo e não conhece os dados específicos do seu app. O Tool Calling resolve justamente isso, permitindo que o modelo invoque funções do seu código quando precisa de informações externas.
Pense em tools como superpoderes que você empresta ao modelo. Ele decide quando usar e você controla a implementação.
Criando uma Tool
Pra criar uma tool, implemente o protocolo Tool:
import FoundationModels
struct WeatherTool: Tool {
let name = "getWeather"
let description = "Obtém a previsão do tempo atual para uma cidade específica"
@Generable
struct Arguments {
@Guide(description: "Nome da cidade para consultar o clima")
let city: String
}
func call(arguments: Arguments) async throws -> String {
// Aqui você implementa a lógica real
// Ex: chamar WeatherKit, uma API REST, etc.
let weather = try await WeatherService.fetch(for: arguments.city)
return "Em \(arguments.city): \(weather.temperature)°C, \(weather.condition)"
}
}
Uma tool precisa de três coisas:
- name — identificador curto, sem espaços (é assim que o modelo referencia a tool internamente)
- description — descrição legível do que ela faz
- Arguments — struct
@Generablecom os parâmetros que o modelo vai preencher
Registrando Tools na Sessão
let session = LanguageModelSession(
tools: [WeatherTool()],
instructions: "Quando o usuário perguntar sobre o clima, use a tool getWeather."
)
Pode registrar várias tools de uma vez:
let session = LanguageModelSession(
tools: [WeatherTool(), TranslationTool(), ContactsTool()],
instructions: "Use as ferramentas disponíveis quando necessário para responder o usuário."
)
O modelo decide sozinho quando invocar cada tool, baseado na pergunta do usuário e na descrição que você forneceu. É surpreendentemente bom nisso, diga-se de passagem.
Integração Completa com SwiftUI
Bora juntar tudo num exemplo prático? Aqui vai um mini app de assistente culinário usando Guided Generation, streaming e instruções personalizadas:
import SwiftUI
import FoundationModels
@Generable
struct Recipe {
@Guide(description: "Nome da receita")
let name: String
@Guide(description: "Tempo de preparo em minutos", .range(5...120))
let prepTimeMinutes: Int
@Guide(description: "Lista de ingredientes com quantidades")
let ingredients: [String]
@Guide(description: "Passo a passo do preparo")
let steps: [String]
}
@MainActor
@Observable
class RecipeAssistant {
var recipes: [Recipe.PartiallyGenerated] = []
var isLoading = false
private let session = LanguageModelSession(
instructions: "Você é um chef brasileiro especializado em culinária caseira."
)
func suggest(ingredients: String) async {
isLoading = true
defer { isLoading = false }
do {
let prompt = "Sugira 2 receitas usando: \(ingredients)"
let stream = session.streamResponse(
to: prompt,
generating: [Recipe].self
)
for try await partial in stream {
recipes = partial
}
} catch {
print("Erro: \(error.localizedDescription)")
}
}
}
struct RecipeView: View {
@State private var assistant = RecipeAssistant()
@State private var ingredients = ""
var body: some View {
NavigationStack {
VStack(spacing: 16) {
TextField("Ingredientes (ex: frango, arroz)", text: $ingredients)
.textFieldStyle(.roundedBorder)
.padding(.horizontal)
Button("Sugerir Receitas") {
Task { await assistant.suggest(ingredients: ingredients) }
}
.buttonStyle(.borderedProminent)
.disabled(assistant.isLoading || ingredients.isEmpty)
List(assistant.recipes, id: \.name) { recipe in
VStack(alignment: .leading, spacing: 8) {
Text(recipe.name ?? "Gerando...")
.font(.headline)
if let time = recipe.prepTimeMinutes {
Text("\(time) minutos")
.font(.caption)
.foregroundStyle(.secondary)
}
}
}
}
.navigationTitle("Chef IA")
}
}
}
Esse exemplo demonstra o padrão recomendado pela Apple: uma classe @Observable encapsulando a sessão e a lógica, e uma view SwiftUI observando as mudanças. O streaming com Recipe.PartiallyGenerated permite que a UI atualize em tempo real — conforme as receitas vão sendo geradas, o usuário já consegue ver o progresso.
Otimização de Performance
O modelo on-device impressiona, mas tem limitações reais que você precisa considerar antes de ir pra produção. Vou listar as principais.
Janela de Contexto
O modelo trabalha com uma janela de 4.096 tokens (entrada + saída combinadas). Parece pouco? É porque é. Prompts muito longos comem o espaço da resposta, então seja conciso nas instruções.
Prewarm pra Melhor Latência
Se você sabe que o usuário vai interagir com o modelo em breve, pré-aqueça a sessão:
session.prewarm() // Carrega recursos na memória antecipadamente
Isso faz uma diferença real no tempo da primeira resposta. Vale muito a pena.
Mantenha Structs @Generable Enxutas
Toda propriedade num tipo @Generable será gerada pelo modelo — mesmo que você não use na UI. Se uma view precisa só do nome e do tempo de preparo, não inclua passos detalhados e ingredientes. Crie tipos @Generable específicos pra cada contexto. Sério, isso muda bastante a performance.
Ordem das Propriedades
O modelo gera propriedades sequencialmente. Coloque resumos e campos derivados por último — dá mais contexto pro modelo gerá-los com qualidade.
Idioma das Instruções
Embora o modelo suporte português nos prompts do usuário, as instruções (system prompt) tendem a dar resultados melhores em inglês. Na prática, escreva as instruções em inglês e oriente o modelo a responder em português. Testei bastante e a diferença é perceptível.
Limitações que Você Precisa Conhecer
Antes de sair adicionando IA em tudo quanto é tela, é bom ter clareza sobre o que o Foundation Models não faz:
- Requisito de hardware — só funciona em dispositivos com Apple Silicon que suportam Apple Intelligence
- Modelo fixo — você não pode trocar por outro modelo, ajustar pesos ou fazer fine-tuning
- Contexto limitado — 4.096 tokens é bem menos que modelos na nuvem (que chegam a 128K+)
- Sem acesso à internet — o modelo não busca informações em tempo real (pra isso, use Tool Calling)
- Condições do dispositivo — bateria baixa, Modo Jogo ou download em andamento podem bloquear o uso
- Qualidade inferior a modelos maiores — com 3B de parâmetros, não espere o nível de um GPT-4 ou Claude. Pra tarefas focadas é excelente, mas raciocínio complexo não é o forte dele
Foundation Models vs. APIs de Nuvem: Quando Usar Cada Um?
Essa é uma dúvida que aparece sempre: quando usar o Foundation Models e quando recorrer a APIs como OpenAI ou Anthropic? A real é que depende do caso de uso:
| Cenário | Foundation Models | APIs de Nuvem |
|---|---|---|
| Privacidade crítica (saúde, finanças) | Ideal | Requer cuidado extra |
| Funcionar offline | Ideal | Não suporta |
| Custo zero de inferência | Gratuito | Pago por token |
| Raciocínio complexo / textos longos | Limitado | Superior |
| Conhecimento atualizado | Limitado ao treinamento | Mais atual (com busca web) |
| Multimodal (imagens, áudio) | Texto apenas | Suporte amplo |
Na prática, muitos apps vão combinar as duas abordagens — Foundation Models pras tarefas rápidas e sensíveis à privacidade, e nuvem pros cenários mais pesados. Não precisa ser uma coisa ou outra.
Perguntas Frequentes (FAQ)
O Foundation Models framework funciona em todos os iPhones?
Não. O framework requer dispositivos com Apple Silicon que suportem Apple Intelligence. Na prática: iPhone 15 Pro ou superior, iPads com chip M1+ e Macs com Apple Silicon. Dispositivos mais antigos simplesmente não têm o hardware pra rodar um modelo de 3 bilhões de parâmetros localmente.
Preciso pagar para usar o Foundation Models?
Não, e essa é uma das partes mais legais. O Foundation Models é 100% gratuito. Como roda no dispositivo do usuário, não tem custos de API, limites de tokens nem assinaturas. Importou o framework, já pode usar.
O Foundation Models funciona sem internet?
Sim! Essa é talvez a maior vantagem. Depois do download inicial do modelo (que acontece automaticamente quando o Apple Intelligence é ativado), todas as inferências rodam completamente offline. Funciona no avião, no metrô, em qualquer lugar sem sinal.
Posso usar o Foundation Models com Core Data ou SwiftData?
Pode, mas com uma ressalva. Como os tipos @Generable são structs, não dá pra usá-los diretamente como modelos do SwiftData (que exige classes @Model). O caminho é gerar os dados com @Generable e depois mapear pra seus modelos de persistência. Um passo a mais, mas funciona bem.
O modelo suporta português nativamente?
Sim. O Foundation Models suporta 10 idiomas, incluindo português. Pode mandar prompts em português e receber respostas no mesmo idioma tranquilamente. Só uma dica: as instruções do sistema (system prompt) costumam funcionar melhor em inglês — vale testar no seu caso.