Foundation Models no iOS 26: Guia Completo de IA On-Device com Swift

Guia completo do Foundation Models no iOS 26. Aprenda a usar IA on-device nos seus apps SwiftUI com @Generable, streaming e Tool Calling — sem API keys, sem custos, tudo no dispositivo.

Foundation Models iOS 26: IA On-Device 2026

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érico
  • description: — 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 @Generable com 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.

Sobre o Autor Priya Raghavan

Priya spent six years at Instacart building the iOS shopper app, where she led the migration from UIKit to SwiftUI across 80+ screens and cut crash-free sessions from 99.2% to 99.87%. Before that, she was a contractor at a Bay Area design studio shipping App Store apps for two Fortune 500 retail clients. She focuses on practical SwiftUI architecture - what holds up when you have 12 engineers committing to the same codebase, not just toy MVVM examples. Her recent work involves The Composable Architecture, Swift concurrency migration audits, and reducing main-thread hangs on older devices like the iPhone XR that enterprise fleets still ship. Priya runs a small consultancy in Oakland and occasionally speaks at try! Swift NYC. She has been writing Swift since the Objective-C bridging days of 2015.