Foundation Models ve Swiftu: Průvodce on-device AI pro iOS 26

Kompletní český průvodce Foundation Models frameworkem pro iOS 26. Naučte se používat on-device AI od Apple — od generování textu přes guided generation s @Generable a @Guide až po tool calling. Včetně funkčních příkladů kódu.

Proč je Foundation Models zlomový okamžik pro iOS vývojáře

Představte si, že můžete ve své aplikaci spustit jazykový model se 3 miliardami parametrů — bez API klíčů, bez serverových nákladů, bez připojení k internetu. A kompletně zdarma. Přesně to Apple přinesl s iOS 26 a frameworkem Foundation Models. Je to tentýž model, který pohání Apple Intelligence, a teď je dostupný přímo ve vašem Swift kódu.

Tohle je prostě velká věc.

Na rozdíl od cloudových řešení jako ChatGPT nebo Claude, kde data putují na vzdálené servery, Foundation Models běží kompletně na zařízení. Veškerá data zůstávají u uživatele, latence je minimální (mluvíme o 0,6 ms do prvního tokenu na iPhone 15 Pro) a generování jede rychlostí přibližně 30 tokenů za sekundu. Pro nás vývojáře to otevírá úplně novou kategorii funkcí — od generování obsahu přes klasifikaci textu až po inteligentní průvodce aplikací.

V tomto průvodci si projdeme Foundation Models od úplných základů až po pokročilé techniky jako guided generation a tool calling. Ke každé sekci přidáme funkční příklady kódu, které můžete rovnou hodit do svých projektů.

Co přesně je Foundation Models framework

Foundation Models je nativní Swift framework představený na WWDC 2025, který dává přístup k on-device jazykovému modelu Apple Intelligence. Nejde o žádný wrapper nad externím API — model s přibližně 3 miliardami parametrů a velikostí kolem 1,6 GB běží přímo na Neural Engine, GPU a CPU vašeho Apple Silicon čipu.

Framework stojí na třech hlavních pilířích:

  • Generování textu — odpovídání na dotazy, sumarizace, generování kreativního obsahu
  • Guided generation — strukturovaný, typově bezpečný výstup do nativních Swift typů pomocí maker @Generable a @Guide
  • Tool calling — model může autonomně volat funkce vaší aplikace pro přístup k externím datům

A co s tím můžete dělat v praxi? Docela dost:

  • Návrhy při vyhledávání v aplikaci
  • Generativní dialogy pro herní postavy (to je fakt cool)
  • Generování a úprava textového obsahu
  • Inteligentní průvodce funkcemi aplikace
  • Automatická tvorba kvízů a odpovídání na otázky
  • Klasifikace obsahu, sumarizace a sémantické vyhledávání

Požadavky a kontrola dostupnosti

Než začnete kódovat, ujistěte se, že máte správné prostředí:

  • Hardware: Mac s Apple Silicon (pro vývoj), zařízení s čipem A17 Pro nebo řady M (pro spuštění)
  • Systém: macOS Tahoe 26, iOS 26, iPadOS 26 nebo visionOS 26
  • IDE: Xcode 26
  • Apple Intelligence: musí být zapnutá v Nastavení zařízení

Protože ne každé zařízení Foundation Models podporuje, je zásadní kontrolovat dostupnost modelu za běhu. Nikdo nechce, aby mu aplikace spadla u uživatele se starším iPhonem. Apple k tomu poskytuje vlastnost availability na SystemLanguageModel:

import FoundationModels

let model = SystemLanguageModel.default

switch model.availability {
case .available:
    // Model je připraven k použití
    print("Model je dostupný")
case .unavailable(.appleIntelligenceNotEnabled):
    print("Zapněte Apple Intelligence v Nastavení")
case .unavailable(.modelNotReady):
    print("Model se stahuje nebo připravuje, zkuste to později")
case .unavailable(.deviceNotEligible):
    print("Toto zařízení nepodporuje Foundation Models")
default:
    print("Model není dostupný")
}

Jeden důležitý detail, který se hodí vědět — SystemLanguageModel nabízí dva režimy:

  • .default — optimalizovaný pro obecné generování textu, kreativní obsah a odpovídání na otázky
  • .contentTagging — specializovaný adaptér pro extrakci entit, detekci témat a automatické tagování
// Obecný model
let generalModel = SystemLanguageModel.default

// Specializovaný model pro tagování obsahu
let taggingModel = SystemLanguageModel(useCase: .contentTagging)

Základy: LanguageModelSession a generování textu

Vstupním bodem do celého frameworku je třída LanguageModelSession. Ta udržuje konverzační kontext mezi jednotlivými požadavky — funguje v podstatě jako chat, kde si model pamatuje předchozí zprávy.

Jednoduchý textový dotaz

Nejjednodušší způsob, jak model použít, je metoda respond(to:):

import FoundationModels

let session = LanguageModelSession()

do {
    let response = try await session.respond(
        to: "Vysvětli rozdíl mezi struct a class ve Swiftu"
    )
    print(response.content)
} catch {
    print("Chyba: \(error)")
}

Výsledek najdete ve vlastnosti response.content jako String. Metoda je async throws, takže potřebujete try await a standardní ošetření chyb. Nic překvapivého, pokud jste zvyklí na moderní Swift.

Instrukce pro model (systémový prompt)

Pro konzistentní chování modelu můžete nastavit instrukce při vytváření session. Instrukce fungují jako systémový prompt — definují roli, omezení a styl odpovědí:

let session = LanguageModelSession {
    "Jsi expertní Swift tutor. Odpovídáš stručně a vždy uvádíš příklady kódu."
    "Pokud si nejsi jistý odpovědí, řekni to přímo."
}

let response = try await session.respond(
    to: "Jak fungují property wrappery?"
)
print(response.content)

Instrukce se vyhodnocují před uživatelským promptem a nastavují kontext pro celou session. Můžete v nich dokonce uvést příklady (few-shot learning), což podle mé zkušenosti výrazně zlepšuje konzistenci výstupu.

Prewarm pro okamžitou odezvu

Pokud víte, že budete model potřebovat brzy, můžete ho předem načíst do paměti metodou prewarm(). To eliminuje tu nepříjemnou prodlevu při prvním dotazu:

let session = LanguageModelSession()

// Předem načteme model do paměti
try await session.prewarm(promptPrefix: "Jsi vývojářský asistent")

// Pozdější dotaz bude rychlejší
let response = try await session.respond(to: "Co je nového ve Swift 6.2?")

Streamování odpovědí pro plynulé UI

Čekat na kompletní odpověď modelu může znamenat několik sekund ticha v uživatelském rozhraní. A upřímně, to není skvělý zážitek pro uživatele. Proto Foundation Models nabízí streamování — odpověď se zobrazuje postupně, token po tokenu.

import SwiftUI
import FoundationModels

struct ChatView: View {
    @State private var session = LanguageModelSession()
    @State private var responseText = ""
    @State private var isGenerating = false

    var body: some View {
        VStack {
            ScrollView {
                Text(responseText)
                    .padding()
            }

            Button("Generuj příběh") {
                Task {
                    await generateStory()
                }
            }
            .disabled(isGenerating)
        }
    }

    func generateStory() async {
        isGenerating = true
        responseText = ""

        do {
            let stream = session.streamResponse(
                to: "Napiš krátký příběh o Swift vývojáři"
            )
            for try await partial in stream {
                responseText = partial
            }
        } catch {
            responseText = "Chyba: \(error.localizedDescription)"
        }

        isGenerating = false
    }
}

Metoda streamResponse(to:) vrací AsyncSequence, kde každý element obsahuje dosud vygenerovaný text. Tady je jedna fajn věc — na rozdíl od některých jiných implementací Foundation Models nestreamuje jednotlivé tokeny, ale kompletní snapshoty odpovědi. Každý nový element obsahuje vše od začátku, takže aktualizace UI je triviální.

Šikovná vlastnost session je také isResponding — hodí se pro blokování nových vstupů během generování:

Button("Odeslat") {
    Task { await sendMessage() }
}
.disabled(session.isResponding)

Guided generation: Typově bezpečný výstup s @Generable

Tak, a tady se Foundation Models skutečně odlišuje od konkurence. Guided generation umožňuje modelu generovat přímo nativní Swift struktury místo prostého textu. Žádné parsování JSON řetězců, žádné křehké regulární výrazy — model spolehlivě naplní vámi definovaný typ.

Osobně považuju tuhle funkci za nejsilnější stránku celého frameworku.

Makro @Generable

Označte svůj typ makrem @Generable a Foundation Models automaticky vygeneruje JSON schéma, které model použije pro strukturovaný výstup:

import FoundationModels

@Generable
struct FilmRecenze {
    let nazev: String
    let hodnoceni: Int
    let plusy: [String]
    let minusy: [String]
    let shrnuti: String
}

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Napiš recenzi na film Interstellar",
    generating: FilmRecenze.self
)

// response.content je typu FilmRecenze — plně typově bezpečné
print(response.content.nazev)       // "Interstellar"
print(response.content.hodnoceni)   // 9
print(response.content.plusy)       // ["Vizuální efekty", ...]

Všechny vlastnosti v @Generable typu musí být samy generable — tedy primitivní typy (String, Int, Double, Bool), pole, vnořené @Generable typy nebo enumy.

Makro @Guide pro přesnější kontrolu

Když potřebujete modelu přesněji říct, co má generovat, přichází na řadu makro @Guide. Umožňuje přidat popisy vlastností a nastavit omezení na generované hodnoty:

@Generable
struct Recept {
    @Guide(description: "Název receptu v češtině")
    let nazev: String

    @Guide(description: "Doba přípravy v minutách", .range(5...180))
    let dobaPripravy: Int

    @Guide(description: "Obtížnost receptu", .anyOf(["snadný", "střední", "pokročilý"]))
    let obtiznost: String

    @Guide(.count(4...8))
    let ingredience: [String]

    let postup: [String]
}

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Navrhni recept na české svíčkové",
    generating: Recept.self
)

let recept = response.content
print("\(recept.nazev) — \(recept.obtiznost), \(recept.dobaPripravy) min")
print("Ingredience: \(recept.ingredience.joined(separator: ", "))")

Tady je přehled dostupných omezení v @Guide:

  • description: — textový popis vlastnosti pro model (upřímně, tohle je to nejdůležitější)
  • .range() — omezení číselného rozsahu
  • .anyOf([]) — výběr z konkrétních hodnot
  • .count() — přesný počet nebo rozsah prvků pole
  • .constant() — fixní hodnota
  • Regex vzor — omezení formátu řetězce pomocí regulárního výrazu

Enumy v guided generation

Výčtové typy (enum) jsou v guided generation obzvláště silné. Proč? Protože model může generovat pouze platné hodnoty — žádné překvapení za běhu:

@Generable
enum Nalada: String {
    case pozitivni = "pozitivní"
    case negativni = "negativní"
    case neutralni = "neutrální"
    case smisena = "smíšená"
}

@Generable
struct AnalyzaTextu {
    let nalada: Nalada
    let klicoveBody: [String]
    @Guide(description: "Stručné shrnutí v jedné větě")
    let shrnuti: String
}

let response = try await session.respond(
    to: "Analyzuj tento text: 'Nový iPhone je skvělý, ale cena je příliš vysoká.'",
    generating: AnalyzaTextu.self
)

switch response.content.nalada {
case .smisena:
    print("Text vyjadřuje smíšené pocity")
default:
    print("Nálada: \(response.content.nalada.rawValue)")
}

Streamování strukturovaného výstupu

Guided generation lze kombinovat se streamováním, což je skvělé pro UX. Foundation Models pro to automaticky generuje typ PartiallyGenerated — v podstatě zrcadlovou kopii vašeho @Generable typu, kde jsou všechny vlastnosti optional. Jak model postupně generuje data, vlastnosti se plní jedna po druhé:

@State private var partialRecept: Recept.PartiallyGenerated?

func streamRecept() async {
    let stream = session.streamResponse(
        to: "Navrhni jednoduchý recept",
        generating: Recept.self
    )

    do {
        for try await partial in stream {
            partialRecept = partial
        }
    } catch {
        print("Chyba: \(error)")
    }
}

// V UI:
if let nazev = partialRecept?.nazev {
    Text(nazev).font(.title)
}
if let ingredience = partialRecept?.ingredience {
    ForEach(ingredience, id: \.self) { item in
        Text("• \(item)")
    }
}

Ještě jeden tip na závěr této sekce: vlastnosti se generují v pořadí, v jakém jsou deklarovány ve zdrojovém kódu. Takže pokud je hodnota jedné vlastnosti závislá na jiné (třeba shrnutí závisí na obsahu), umístěte závislou vlastnost až za ty, na kterých závisí.

Tool calling: Rozšiřte schopnosti modelu

On-device model nemá přístup k internetu, aktuálním datům ani k datům vaší aplikace. To dává smysl — běží lokálně a je omezený na to, co zná z tréninku. Přesně pro tyto situace existuje tool calling — mechanismus, kterým model autonomně volá funkce vaší aplikace, když potřebuje další informace.

Definice nástroje

Nástroj vytvoříte implementací protokolu Tool:

import FoundationModels

struct PocasiTool: Tool {
    let name = "pocasi"
    let description = "Získá aktuální počasí pro zadané město."

    @Generable
    struct Arguments {
        @Guide(description: "Název města, pro které se má zjistit počasí")
        let mesto: String
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        // V reálné aplikaci by zde byl API call
        let teplota = 22
        let popis = "Polojasno"
        return ToolOutput(
            "Počasí v \(arguments.mesto): \(teplota)°C, \(popis)"
        )
    }
}

Protokol Tool vyžaduje čtyři věci:

  • name — identifikátor nástroje (bez mezer a speciálních znaků)
  • description — popis v přirozeném jazyce, který model použije k rozhodování, kdy nástroj zavolat
  • Arguments@Generable struktura definující vstupní parametry
  • call(arguments:) — asynchronní metoda s logikou nástroje, vracející ToolOutput

Registrace a použití nástrojů

Nástroje se registrují při vytváření session:

let pocasiTool = PocasiTool()

let session = LanguageModelSession(tools: [pocasiTool]) {
    "Jsi přátelský asistent pro cestování. Když se uživatel ptá na počasí, použij nástroj pocasi."
}

let response = try await session.respond(
    to: "Jaké je počasí v Praze?"
)
print(response.content)
// Model automaticky zavolá PocasiTool a zahrne výsledek do odpovědi

Celý průběh pod kapotou funguje takto:

  1. Model obdrží dotaz uživatele a vyhodnotí, jestli potřebuje nástroj
  2. Pokud ano, autonomně vygeneruje argumenty pro nástroj (díky @Generable)
  3. Framework zavolá metodu call(arguments:) vašeho nástroje
  4. Výsledek se automaticky vloží zpět do konverzace
  5. Model vygeneruje finální odpověď s využitím dat z nástroje

A samozřejmě můžete registrovat více nástrojů najednou — model si sám vybere ten správný podle kontextu:

let session = LanguageModelSession(
    tools: [pocasiTool, restauraceTool, mapaTool]
) {
    "Jsi cestovní průvodce. Pomáhej uživatelům plánovat výlety."
}

Integrace se SwiftUI: Kompletní příklad

Pojďme spojit vše dohromady v reálném příkladu. Postavíme jednoduchý generátor kvízových otázek — je to skvělý způsob, jak si osahat guided generation v praxi:

import SwiftUI
import FoundationModels

@Generable
struct KvizOtazka {
    @Guide(description: "Otázka z oblasti programování ve Swiftu")
    let otazka: String

    @Guide(.count(4))
    let moznosti: [String]

    @Guide(description: "Správná odpověď — musí být jedna z možností")
    let spravnaOdpoved: String

    @Guide(description: "Vysvětlení, proč je odpověď správná")
    let vysvetleni: String
}

struct KvizView: View {
    @State private var session = LanguageModelSession {
        "Jsi expert na Swift programování. Generuješ kvízové otázky v češtině."
    }
    @State private var otazka: KvizOtazka?
    @State private var vybranaOdpoved: String?
    @State private var zobrazitVysledek = false
    @State private var nacitani = false

    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                if nacitani {
                    ProgressView("Generuji otázku...")
                } else if let otazka {
                    Text(otazka.otazka)
                        .font(.headline)
                        .padding()

                    ForEach(otazka.moznosti, id: \.self) { moznost in
                        Button {
                            vybranaOdpoved = moznost
                            zobrazitVysledek = true
                        } label: {
                            Text(moznost)
                                .frame(maxWidth: .infinity)
                                .padding()
                                .background(barvaOdpovedi(moznost))
                                .foregroundStyle(.white)
                                .clipShape(RoundedRectangle(cornerRadius: 10))
                        }
                        .disabled(zobrazitVysledek)
                    }

                    if zobrazitVysledek {
                        Text(otazka.vysvetleni)
                            .font(.callout)
                            .padding()
                            .background(.ultraThinMaterial)
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                    }
                }

                Button(otazka == nil ? "Začít kvíz" : "Další otázka") {
                    Task { await generovatOtazku() }
                }
                .buttonStyle(.borderedProminent)
                .disabled(nacitani)
            }
            .padding()
            .navigationTitle("Swift kvíz")
        }
    }

    func barvaOdpovedi(_ moznost: String) -> Color {
        guard zobrazitVysledek else { return .blue }
        if moznost == otazka?.spravnaOdpoved { return .green }
        if moznost == vybranaOdpoved { return .red }
        return .gray
    }

    func generovatOtazku() async {
        nacitani = true
        zobrazitVysledek = false
        vybranaOdpoved = nil

        do {
            let response = try await session.respond(
                to: "Vygeneruj novou kvízovou otázku o Swiftu",
                generating: KvizOtazka.self
            )
            otazka = response.content
        } catch {
            print("Chyba generování: \(error)")
        }

        nacitani = false
    }
}

Tento příklad hezky demonstruje sílu guided generation — celá logika kvízu stojí na tom, že model vrátí přesně definovanou strukturu KvizOtazka s otázkou, čtyřmi možnostmi, správnou odpovědí i vysvětlením. Žádné parsování, žádné hádání formátu. Prostě to funguje.

Kontextové okno a jeho správa

Tady přichází studená sprcha. On-device model má kontextové okno o velikosti 4 096 tokenů. To je pevný limit zahrnující úplně vše — instrukce, historii konverzace, prompt uživatele i odpověď modelu.

Oproti cloudovým modelům s kontextem stovek tisíc tokenů je to výrazné omezení. Ale dá se s tím pracovat.

Praktické rady pro efektivní správu kontextu:

  • Udržujte instrukce stručné — každé slovo v systémovém promptu ukrajuje z dostupného prostoru
  • Minimalizujte vlastnosti v @Generable typech — model generuje všechny vlastnosti bez ohledu na to, jestli je v UI zobrazíte
  • Vytvářejte nové session pro nezávislé dotazy — nepotřebujete udržovat historii, když otázky spolu nesouvisí
  • Monitorujte transcript — vlastnost session.transcript ukazuje kompletní historii konverzace včetně tool callů

Pokud limit překročíte, framework vyhodí chybu. Tu byste měli ošetřit a nabídnout uživateli možnost začít novou konverzaci.

Osvědčené postupy a tipy pro výkon

Po delší práci s Foundation Models se vykrystalizovalo několik pravidel, která fakt dělají rozdíl v kvalitě výsledků:

  • Pište instrukce v angličtině — i když generujete obsah v jiném jazyce, anglické systémové prompty produkují konzistentnější výsledky. Model je primárně trénovaný na anglických datech a prostě to poznat
  • Volejte prewarm() co nejdříve — ideálně při onAppear view, kde budete model potřebovat
  • Používejte streamování — výrazně snižuje vnímanou latenci a uživatelé to ocení
  • Udržujte @Generable typy ploché — hluboce vnořené struktury zvyšují latenci a spotřebu tokenů
  • Řaďte vlastnosti správně — generují se v pořadí deklarace, shrnutí a závislé hodnoty patří na konec
  • Chraňte se před prompt injection — nikdy nevkládejte neošetřený uživatelský vstup přímo do instrukcí. Oddělujte systémový kontext od uživatelských dat
  • Testujte na reálných zařízeních — výkon v simulátoru neodpovídá realitě, protože model běží na Neural Engine

Omezení, o kterých byste měli vědět

Foundation Models není všelék. A to je v pořádku — je dobré znát limity předem, než začnete plánovat funkce, které nepůjdou realizovat:

  • Pouze text — v současné verzi framework podporuje jen textové úlohy, žádné zpracování obrázků, audia nebo videa
  • Není to chatbot pro obecné znalosti — model s 3B parametry exceluje v specifických úlohách (sumarizace, klasifikace, generování krátkého obsahu), ale nemá znalostní bázi srovnatelnou s velkými cloudovými modely
  • 4 096 tokenů — pevný limit kontextového okna omezuje délku konverzací a složitost výstupů
  • Závislost na hardwaru — vyžaduje Apple Silicon s podporou Apple Intelligence (A17 Pro a novější)
  • Pouze Apple platformy — žádná podpora pro Android, web nebo jiné platformy

Často kladené otázky

Musím platit za používání Foundation Models?

Ne. Foundation Models běží kompletně na zařízení uživatele a je zdarma. Nepotřebujete žádné API klíče, předplatné ani serverovou infrastrukturu. Model je jednoduše součástí operačního systému.

Lze on-device model dotrénovat na vlastních datech?

Ne, on-device Foundation Model nelze fine-tunovat. Chování modelu ale můžete ovlivnit prostřednictvím instrukcí (systémového promptu), few-shot příkladů a tool callingu pro přístup k vlastním datům. Pokud potřebujete specializované modely, podívejte se na Create ML nebo Core ML.

Mohu spustit více session paralelně?

Ano, to jde. Jedna session sice nemůže zpracovávat více dotazů najednou (nový respond nelze volat, dokud předchozí nedoběhne), ale můžete vytvořit více nezávislých instancí LanguageModelSession a spouštět je paralelně v různých Task kontextech.

Jaké jazyky model podporuje?

Foundation Models podporuje více jazyků — angličtinu, francouzštinu, němčinu, italštinu, portugalštinu, španělštinu, zjednodušenou čínštinu, japonštinu a korejštinu. Nejlepší výsledky ale dostanete v angličtině. Pro instrukce doporučuji vždy angličtinu, i když generujete obsah v jiném jazyce.

Jak velký je model a ovlivní velikost mé aplikace?

Model má přibližně 1,6 GB, ale je součástí operačního systému — nestahuje se s vaší aplikací. Velikost vaší aplikace tedy nijak neovlivní. Stačí importovat FoundationModels a model je dostupný.

O Autorovi Editorial Team

Our team of expert writers and editors.