Inleiding: AI Komt naar Je App
Eerlijk gezegd, toen ik voor het eerst hoorde over het Foundation Models framework in iOS 26, was ik behoorlijk sceptisch. Weer een AI-framework, dacht ik. Maar na er een paar weken mee te hebben gespeeld, moet ik toegeven: dit is echt een gamechanger.
Met iOS 26 geeft Apple ontwikkelaars directe toegang tot het on-device taalmodel dat Apple Intelligence aandrijft — een model met 3 miljard parameters dat volledig op het apparaat draait. Geen internetverbinding nodig, geen API-kosten. Dat lees je goed: helemaal gratis.
Het mooie is dat het framework diep geïntegreerd is met Swift. Je krijgt een elegante, typeveilige API waarmee je met slechts een paar regels code intelligente functies aan je apps kunt toevoegen. Van tekstgeneratie tot gestructureerde data, van streaming tot tool calling — de mogelijkheden zijn eerlijk gezegd indrukwekkend.
Dus, laten we erin duiken. In deze gids lopen we elk aspect van het Foundation Models framework door, met praktische codevoorbeelden die je direct kunt gebruiken in je eigen projecten.
Vereisten en Beschikbaarheid
Voordat je begint, even de belangrijkste vereisten op een rijtje:
- Besturingssysteem: iOS 26, iPadOS 26, macOS Tahoe of visionOS 26
- Hardware: Een apparaat met Apple Silicon dat Apple Intelligence ondersteunt
- Ontwikkelomgeving: Xcode 26 of nieuwer
- Apple Intelligence: Moet ingeschakeld zijn in de systeeminstellingen
Niet elk Apple-apparaat ondersteunt het framework. Logisch eigenlijk, want een model met 3 miljard parameters vereist behoorlijk wat rekenkracht. Daarom is het essentieel om de beschikbaarheid te checken voordat je AI-functies aanbiedt:
import FoundationModels
let model = SystemLanguageModel.default
switch model.availability {
case .available:
// Het model is beschikbaar en klaar voor gebruik
print("Foundation Models is beschikbaar!")
case .unavailable(let reason):
switch reason {
case .appleIntelligenceNotEnabled:
print("Schakel Apple Intelligence in via Instellingen.")
case .deviceNotEligible:
print("Dit apparaat ondersteunt Apple Intelligence niet.")
default:
print("Het model is momenteel niet beschikbaar.")
}
}
Bied altijd een fallback aan voor apparaten die het framework niet ondersteunen. Je wilt niet dat je app onbruikbaar wordt voor gebruikers met oudere hardware.
Je Eerste Sessie: LanguageModelSession
Het hart van het Foundation Models framework is de LanguageModelSession. Dit is je primaire interface met het on-device taalmodel. Een sessie houdt de gesprekscontext bij en ondersteunt zowel standaard- als streaming-antwoorden.
Een eenvoudige tekstgeneratie
De simpelste manier om te starten? Maak een sessie aan en stuur een prompt:
import FoundationModels
import SwiftUI
struct ContentView: View {
@State private var response = ""
@State private var isLoading = false
var body: some View {
VStack(spacing: 20) {
if isLoading {
ProgressView("Bezig met genereren...")
} else {
Text(response)
.padding()
}
Button("Genereer Tip") {
Task {
await generateTip()
}
}
}
.padding()
}
func generateTip() async {
isLoading = true
defer { isLoading = false }
let session = LanguageModelSession()
do {
let result = try await session.respond(
to: "Geef een korte Swift-programmeertip voor beginners."
)
response = result.content
} catch {
response = "Er is een fout opgetreden: \(error.localizedDescription)"
}
}
}
Drie regels kerncode — sessie aanmaken, prompt versturen, antwoord ophalen — en je hebt al een werkende AI-integratie. Best gaaf, toch?
Sessie-instructies configureren
Je kunt het gedrag van het model sturen door instructies mee te geven bij het aanmaken van de sessie. Vergelijk het met een systeemprompt: je vertelt het model welke rol het moet spelen.
let session = LanguageModelSession {
"""
Je bent een ervaren Swift-ontwikkelaar die gespecialiseerd is in
SwiftUI en iOS-ontwikkeling. Je geeft beknopte, praktische antwoorden
met codevoorbeelden waar nodig. Je antwoordt altijd in het Nederlands.
"""
}
let result = try await session.respond(
to: "Hoe maak ik een aangepaste ViewModifier?"
)
print(result.content)
Instructies worden vóór de gebruikersprompts geplaatst. Dit helpt ook bij het verminderen van prompt-injectierisico's, omdat de intentie van de ontwikkelaar duidelijk gescheiden is van de gebruikersinvoer.
Generatieopties Afstemmen
Het Foundation Models framework geeft je fijnmazige controle over hoe het model antwoorden genereert via GenerationOptions. Hiermee pas je de creativiteit, het determinisme en de lengte van antwoorden aan:
let options = GenerationOptions(
sampling: .greedy, // Altijd het meest waarschijnlijke token
temperature: 0.5, // 0.0 = voorspelbaar, 2.0 = creatief
maximumResponseTokens: 200
)
let result = try await session.respond(
to: "Leg het MVVM-patroon uit voor SwiftUI.",
options: options
)
Sampling-modi uitgelegd
Er zijn drie sampling-modi die bepalen hoe tokens worden geselecteerd. Klinkt misschien technisch, maar het is eigenlijk vrij intuïtief:
.greedy— Selecteert altijd het meest waarschijnlijke token. Dezelfde prompt geeft altijd hetzelfde antwoord. Handig voor deterministische resultaten..random(top: k, seed: seed)— Top-k sampling: het model kiest willekeurig uit de k meest waarschijnlijke tokens. Met een seed maak je resultaten reproduceerbaar..random(probabilityThreshold: p, seed: seed)— Top-p (nucleus) sampling: tokens worden geselecteerd totdat de cumulatieve waarschijnlijkheid de drempel p bereikt.
Een leuk praktisch voorbeeld: gebruik een dagelijkse seed om elke dag een andere (maar consistente) aanbeveling te genereren:
let dagVanHetJaar = Calendar.current.ordinality(
of: .day, in: .year, for: .now
) ?? 1
let options = GenerationOptions(
sampling: .random(top: 10, seed: dagVanHetJaar),
temperature: 0.7
)
let result = try await session.respond(
to: "Geef een dagelijkse motivatietip voor programmeurs.",
options: options
)
Hierdoor krijgt de gebruiker elke dag een andere tip, maar binnen dezelfde dag altijd dezelfde. Perfect voor apps met dagelijkse content.
Guided Generation: Gestructureerde Uitvoer met @Generable
Dit is eerlijk gezegd mijn favoriete onderdeel van het hele framework. Met Guided Generation kun je het model gestructureerde Swift-datatypes laten genereren in plaats van platte tekst. Geen fragiele JSON-parsing meer — gewoon typeveilige, voorspelbare uitvoer.
Het @Generable Macro
Door het @Generable-macro toe te voegen aan een Swift-struct, vertel je het framework dat het model instanties van dit type kan genereren:
import FoundationModels
@Generable
struct ProgrammeerTip: Equatable {
let titel: String
let beschrijving: String
let taal: String
let moeilijkheidsgraad: String
}
let session = LanguageModelSession()
let result = try await session.respond(
to: "Geef een Swift-programmeertip over optionals.",
generating: ProgrammeerTip.self
)
let tip: ProgrammeerTip = result.content
print("Titel: \(tip.titel)")
print("Beschrijving: \(tip.beschrijving)")
print("Taal: \(tip.taal)")
print("Moeilijkheidsgraad: \(tip.moeilijkheidsgraad)")
Het macro genereert automatisch het benodigde schema en de parsing-logica tijdens het compileren. Alle eigenschappen moeten van een ondersteund type zijn: String, Int, Double, Bool, arrays, of andere @Generable-typen.
Veldconstraints met @Guide
Met het @Guide-macro voeg je extra beperkingen en beschrijvingen toe aan individuele eigenschappen. Dit geeft het model meer context over wat er verwacht wordt:
@Generable
struct AppBeoordeling: Equatable {
@Guide(description: "De naam van de beoordeelde iOS-app")
let appNaam: String
@Guide(description: "Een beknopte samenvatting van de beoordeling in 2-3 zinnen")
let samenvatting: String
@Guide(.anyOf(["1", "2", "3", "4", "5"]))
let sterren: String
@Guide(description: "De belangrijkste pluspunten van de app", .count(3))
let pluspunten: [String]
@Guide(description: "De belangrijkste minpunten van de app", .count(2))
let minpunten: [String]
}
De beschikbare constraint-opties zijn:
.anyOf([waarden])— Beperkt de waarde tot een specifieke lijst van opties.count(n)— Stelt een vaste lengte in voor arrays.range(bereik)— Beperkt numerieke waarden tot een bereik, bijvoorbeeld.range(0...100)description: "..."— Geeft het model een hint in natuurlijke taal
Hiërarchische Structuren
Hier wordt het pas echt interessant. Je kunt complexe, geneste datastructuren definiëren door meerdere @Generable-typen te combineren:
@Generable
struct SwiftCursus: Equatable {
@Guide(description: "De titel van de cursus")
let titel: String
@Guide(description: "Een korte beschrijving van de cursus")
let beschrijving: String
@Guide(description: "De hoofdstukken van de cursus", .count(3))
let hoofdstukken: [Hoofdstuk]
}
@Generable
struct Hoofdstuk: Equatable {
let titel: String
let onderwerpen: [String]
let geschatteDuurInMinuten: Int
}
let session = LanguageModelSession()
let result = try await session.respond(
to: "Ontwerp een korte cursus over SwiftUI-animaties.",
generating: SwiftCursus.self
)
let cursus = result.content
for hoofdstuk in cursus.hoofdstukken {
print("📖 \(hoofdstuk.titel)")
for onderwerp in hoofdstuk.onderwerpen {
print(" - \(onderwerp)")
}
}
Enumeraties als Generable Types
Naast structs kun je ook enumeraties markeren met @Generable. Dat is vooral handig voor classificatietaken:
@Generable
enum Sentiment: String, Codable {
case positief
case negatief
case neutraal
}
@Generable
struct TekstAnalyse: Equatable {
let sentiment: Sentiment
@Guide(description: "Een korte uitleg van het vastgestelde sentiment")
let uitleg: String
}
let session = LanguageModelSession()
let result = try await session.respond(
to: "Analyseer het sentiment: 'Deze app is fantastisch, maar crasht soms.'",
generating: TekstAnalyse.self
)
print("Sentiment: \(result.content.sentiment)")
print("Uitleg: \(result.content.uitleg)")
Streaming: Real-Time Antwoorden in Je UI
Niemand wil een spinner zien terwijl het model een heel verhaal genereert. Met de streaming-API van het Foundation Models framework laat je antwoorden geleidelijk verschijnen — precies zoals je dat kent van ChatGPT en vergelijkbare apps.
Eenvoudige tekststreaming
struct StreamingView: View {
@State private var tekst = ""
var body: some View {
ScrollView {
Text(tekst)
.padding()
}
.task {
await streamAntwoord()
}
}
func streamAntwoord() async {
let session = LanguageModelSession()
do {
let stream = session.streamResponse(
to: "Schrijf een kort verhaal over een Swift-ontwikkelaar."
)
for try await deelAntwoord in stream {
tekst = deelAntwoord
}
} catch {
tekst = "Fout: \(error.localizedDescription)"
}
}
}
Gestructureerde streaming met PartiallyGenerated
En hier wordt het echt cool. Wanneer je streaming combineert met @Generable-typen, ontvang je snapshots van het type T.PartiallyGenerated. Alle eigenschappen zijn optioneel, zodat je deels ingevulde data kunt tonen terwijl het model nog bezig is:
@Generable
struct WeerVoorspelling: Equatable {
let stad: String
let temperatuur: Double
let beschrijving: String
let aanbeveling: String
}
struct WeerView: View {
@State private var stad: String?
@State private var temperatuur: Double?
@State private var beschrijving: String?
@State private var aanbeveling: String?
@State private var isLoading = true
var body: some View {
VStack(alignment: .leading, spacing: 12) {
if let stad {
Text(stad)
.font(.title)
}
if let temperatuur {
Text("\(temperatuur, specifier: "%.1f")°C")
.font(.largeTitle)
}
if let beschrijving {
Text(beschrijving)
}
if let aanbeveling {
Text(aanbeveling)
.italic()
}
if isLoading {
ProgressView()
}
}
.padding()
.task {
await laadWeer()
}
}
func laadWeer() async {
let session = LanguageModelSession()
do {
let stream = session.streamResponse(
to: "Geef een weervoorspelling voor Amsterdam.",
generating: WeerVoorspelling.self
)
for try await partial in stream {
stad = partial.stad
temperatuur = partial.temperatuur
beschrijving = partial.beschrijving
aanbeveling = partial.aanbeveling
}
isLoading = false
} catch {
isLoading = false
}
}
}
De eigenschappen vullen zich één voor één naarmate het model ze genereert. Dit maakt het mogelijk om een progressieve UI te bouwen die al info toont voordat het volledige antwoord klaar is. Je gebruikers zullen het waarderen.
Tool Calling: Het Model Je Code Laten Aanroepen
Oké, dit is misschien wel het meest opwindende onderdeel van het hele framework. Met tool calling kan het taalmodel beslissen om functies in je app aan te roepen wanneer het extra informatie nodig heeft of acties moet uitvoeren. Denk aan een soort reverse API — het model roept jouw code aan.
Het Tool Protocol Implementeren
Om een tool te maken, implementeer je het Tool-protocol. De argumenten definieer je als een @Generable-struct:
import FoundationModels
struct ZoekDocumentatieTool: Tool {
let name = "zoekDocumentatie"
let description = """
Zoekt in de Apple Developer Documentation naar informatie
over een specifiek Swift- of SwiftUI-onderwerp.
"""
@Generable
struct Arguments {
@Guide(description: "Het onderwerp om naar te zoeken")
let zoekterm: String
@Guide(description: "Het framework waarbinnen gezocht moet worden")
let framework: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
// In een echte app zou je hier een API-aanroep doen
let resultaat = """
Documentatie gevonden voor '\(arguments.zoekterm)'
in \(arguments.framework):
- Overzicht en beschrijving
- Codevoorbeelden
- Gerelateerde API's
"""
return ToolOutput(resultaat)
}
}
Tools Registreren bij een Sessie
Tools geef je mee bij het aanmaken van een sessie. Je kunt instructies toevoegen die het model helpen begrijpen wanneer het de tools moet gebruiken:
let documentatieTool = ZoekDocumentatieTool()
let session = LanguageModelSession(
tools: [documentatieTool]
) {
"""
Je bent een behulpzame Swift-assistent. Wanneer de gebruiker
vraagt over specifieke API's of frameworks, gebruik dan de
zoekDocumentatie-tool om actuele informatie op te halen.
"""
}
let result = try await session.respond(
to: "Hoe gebruik ik NavigationStack in SwiftUI?"
)
print(result.content)
Het model beslist zelf wanneer het een tool aanroept. Dat is belangrijk om te weten: de aanroep is niet gegarandeerd. Als het model denkt dat het het antwoord al weet, slaat het de tool over.
Meerdere Tools Combineren
Je kunt meerdere tools registreren bij dezelfde sessie. Het model kiest dan de meest geschikte tool voor de vraag:
struct BerekenTool: Tool {
let name = "bereken"
let description = "Voert wiskundige berekeningen uit."
@Generable
struct Arguments {
@Guide(description: "De wiskundige expressie om te berekenen")
let expressie: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Voer de berekening uit
return ToolOutput("Resultaat: 42")
}
}
struct ConverteerEenhedenTool: Tool {
let name = "converteerEenheden"
let description = "Converteert waarden tussen verschillende meeteenheden."
@Generable
struct Arguments {
@Guide(description: "De waarde om te converteren")
let waarde: Double
@Guide(description: "De broneenheid")
let vanEenheid: String
@Guide(description: "De doeleenheid")
let naarEenheid: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
let resultaat = arguments.waarde * 2.54
return ToolOutput("Resultaat: \(resultaat)")
}
}
let session = LanguageModelSession(
tools: [BerekenTool(), ConverteerEenhedenTool()]
) {
"Je bent een wetenschappelijke rekenhulp."
}
Prestatieoptimalisatie en Best Practices
Na een paar weken werken met het framework heb ik een aantal tips verzameld die het verschil kunnen maken tussen een goede en een geweldige implementatie.
1. Minimaliseer @Generable Eigenschappen
Neem alleen de velden op die je daadwerkelijk nodig hebt. Elke extra eigenschap kost generatietijd — en bij een on-device model telt dat:
// Goed: alleen de benodigde velden
@Generable
struct KorteVoorspelling: Equatable {
let temperatuur: Double
let beschrijving: String
}
// Vermijd: onnodige velden die niet worden getoond
@Generable
struct UitgebreideVoorspelling: Equatable {
let temperatuur: Double
let beschrijving: String
let luchtdruk: Double // Niet nodig in de UI
let windrichting: String // Niet nodig in de UI
let dauwpunt: Double // Niet nodig in de UI
let technischeNotities: String // Niet nodig in de UI
}
2. Voorverwarm Sessies
Als je weet dat de gebruiker waarschijnlijk een AI-functie gaat gebruiken, kun je de sessie alvast voorverwarmen. Dat scheelt merkbaar in responstijd:
let session = LanguageModelSession()
// Voorverwarm wanneer de view verschijnt
try await session.prewarm(
promptPrefix: "Je bent een Swift-programmeerassistent."
)
// Later, wanneer de gebruiker interageert
let result = try await session.respond(to: userInput)
3. Houd Rekening met Token-limieten
Het on-device model heeft een gecombineerd limiet van 4096 tokens voor invoer en uitvoer. Dat klinkt als veel, maar het kan snel vol raken bij complexe prompts. Houd je prompts beknopt en splits lange gesprekken op in meerdere sessies als dat nodig is.
4. Eigenschap-volgorde is Belangrijk
Dit is een detail dat makkelijk over het hoofd wordt gezien: het model genereert eigenschappen van boven naar beneden. Plaats eigenschappen die afhankelijk zijn van andere eigenschappen dus onderaan:
@Generable
struct Analyse: Equatable {
// Eerst de details
let onderwerp: String
let kernpunten: [String]
let complexiteit: String
// Dan de samenvatting (die afhankelijk is van bovenstaande)
@Guide(description: "Samenvatting gebaseerd op de kernpunten")
let samenvatting: String
}
5. Schrijf Instructies in het Engels
Een beetje een verrassende tip misschien, maar systeeminstructies presteren het best in het Engels. Het model begrijpt de gebruikersinvoer prima in andere talen:
let session = LanguageModelSession {
// Instructies in het Engels voor betere prestaties
"""
You are a helpful programming assistant. Always respond
in the same language as the user's message. Provide
practical code examples when applicable.
"""
}
// Gebruikersprompt in het Nederlands
let result = try await session.respond(
to: "Hoe maak ik een custom environment key in SwiftUI?"
)
Integratie met SwiftUI: Een Compleet Voorbeeld
Laten we alles samenbrengen in een volledig SwiftUI-voorbeeld. We bouwen een intelligente code-assistent die Swift-vragen beantwoordt met gestructureerde uitvoer en streaming. Dit is (naar mijn mening) de beste manier om te zien hoe alle puzzelstukjes in elkaar vallen:
import SwiftUI
import FoundationModels
@Generable
struct CodeHulp: Equatable {
@Guide(description: "Een beknopte titel voor het antwoord")
let titel: String
@Guide(description: "Uitleg van het concept in begrijpelijke taal")
let uitleg: String
@Guide(description: "Een Swift-codevoorbeeld dat het concept demonstreert")
let codeVoorbeeld: String
@Guide(description: "Veelgemaakte fouten om te vermijden", .count(2))
let veelgemaakteFouten: [String]
}
@Observable
class CodeAssistentModel {
var titel: String?
var uitleg: String?
var codeVoorbeeld: String?
var veelgemaakteFouten: [String] = []
var isBezig = false
var foutmelding: String?
private var session: LanguageModelSession?
func stelVraag(_ vraag: String) async {
guard SystemLanguageModel.default.availability == .available else {
foutmelding = "Apple Intelligence is niet beschikbaar."
return
}
isBezig = true
foutmelding = nil
resetVelden()
session = LanguageModelSession {
"""
You are an expert Swift and SwiftUI developer.
Provide clear, practical answers with working
code examples. Respond in Dutch.
"""
}
do {
let stream = session!.streamResponse(
to: vraag,
generating: CodeHulp.self
)
for try await partial in stream {
titel = partial.titel
uitleg = partial.uitleg
codeVoorbeeld = partial.codeVoorbeeld
if let fouten = partial.veelgemaakteFouten {
veelgemaakteFouten = fouten.compactMap { $0 }
}
}
} catch {
foutmelding = "Er ging iets mis: \(error.localizedDescription)"
}
isBezig = false
}
private func resetVelden() {
titel = nil
uitleg = nil
codeVoorbeeld = nil
veelgemaakteFouten = []
}
}
struct CodeAssistentView: View {
@State private var model = CodeAssistentModel()
@State private var vraag = ""
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
if let titel = model.titel {
Text(titel)
.font(.title2)
.fontWeight(.bold)
}
if let uitleg = model.uitleg {
Text(uitleg)
.font(.body)
}
if let code = model.codeVoorbeeld {
Text(code)
.font(.system(.caption, design: .monospaced))
.padding()
.background(Color(.systemGray6))
.cornerRadius(8)
}
if !model.veelgemaakteFouten.isEmpty {
Text("Vermijd deze fouten:")
.font(.headline)
ForEach(model.veelgemaakteFouten, id: \.self) { fout in
Label(fout, systemImage: "exclamationmark.triangle")
.foregroundStyle(.orange)
}
}
if let fout = model.foutmelding {
Text(fout)
.foregroundStyle(.red)
}
if model.isBezig {
ProgressView()
}
}
.padding()
}
.navigationTitle("Swift Assistent")
.safeAreaInset(edge: .bottom) {
HStack {
TextField("Stel een Swift-vraag...", text: $vraag)
.textFieldStyle(.roundedBorder)
Button("Vraag") {
Task {
await model.stelVraag(vraag)
vraag = ""
}
}
.disabled(vraag.isEmpty || model.isBezig)
}
.padding()
.background(.ultraThinMaterial)
}
}
}
}
Dit voorbeeld combineert alles: gestructureerde generatie met @Generable, streaming met PartiallyGenerated, beschikbaarheidscontrole, sessiebeheer, en een complete SwiftUI-integratie met @Observable.
Privacy en Beveiliging
Een van de grootste pluspunten van het Foundation Models framework — en eerlijk gezegd een van de redenen waarom ik er zo enthousiast over ben — is de focus op privacy. Alles draait on-device. Je gebruikersgegevens verlaten nooit het apparaat.
Wat dat concreet betekent:
- Offline beschikbaarheid: Het model werkt zonder internetverbinding
- Geen serverkosten: Geen API-aanroepen, dus geen lopende kosten
- Gegevensprivacy: Alle verwerking gebeurt lokaal op het apparaat
- Lage latentie: Geen netwerkvertraging
Guardrails
Het framework bevat ingebouwde veiligheidsmaatregelen (guardrails) die de inhoudsrichtlijnen van Apple handhaven. Je kunt ze niet uitschakelen, en eerlijk gezegd is dat ook maar goed:
let session = LanguageModelSession(
guardrails: .default // Momenteel de enige optie
)
De guardrails zorgen ervoor dat het model geen schadelijke of ongepaste content genereert. Belangrijk als je je app naar de App Store wilt brengen.
Geavanceerd: Aangepaste Modellen en Adapters
Wil je nog een stap verder gaan? Het framework biedt ook ondersteuning voor gespecialiseerde modelvarianten en LoRA-adapters.
Gespecialiseerde Modellen
Naast het algemene model biedt Apple een gespecialiseerd model voor tagging- en classificatietaken:
// Standaardmodel voor algemene taken
let algemeenModel = SystemLanguageModel.default
// Geoptimaliseerd model voor entiteitsextractie en classificatie
let taggingModel = SystemLanguageModel(useCase: .contentTagging)
let session = LanguageModelSession(model: taggingModel)
LoRA Adapters
Het framework ondersteunt LoRA (Low-Rank Adaptation) adapters — kleine, gespecialiseerde gewichtsmatrices die het model afstemmen op specifieke taken zonder het basismodel te wijzigen. Elke adapter is ongeveer 160 MB groot.
- Parameter-efficiënt: Alleen de adaptergewichten worden getraind, niet het volledige model
- Rank 32: Apple gebruikt LoRA met rang 32 voor een goede balans tussen prestaties en grootte
- Snel wisselen: Adapters kunnen snel worden geladen en gewisseld zonder het basismodel opnieuw te laden
Apple biedt een trainingstoolkit waarmee je eigen adapters kunt trainen. Dit maakt het mogelijk om het model te specialiseren voor domein-specifieke taken — denk aan juridische analyse, medische terminologie, of technische documentatie.
Veelvoorkomende Patronen en Architectuur
Na wat experimenteren met het framework zijn dit de architectuurpatronen die ik het meest bruikbaar vond:
Repository Patroon voor AI-Services
protocol AIServiceProtocol {
func genereerSamenvatting(van tekst: String) async throws -> String
func analyseerSentiment(van tekst: String) async throws -> Sentiment
func genereerSuggesties(voor context: String) async throws -> [String]
}
final class OnDeviceAIService: AIServiceProtocol {
private func maakSessie(instructies: String) -> LanguageModelSession {
LanguageModelSession {
instructies
}
}
func genereerSamenvatting(van tekst: String) async throws -> String {
let session = maakSessie(
instructies: "Summarize the following text concisely in Dutch."
)
let result = try await session.respond(to: tekst)
return result.content
}
func analyseerSentiment(van tekst: String) async throws -> Sentiment {
let session = maakSessie(
instructies: "Analyze the sentiment of the provided text."
)
let result = try await session.respond(
to: tekst,
generating: TekstAnalyse.self
)
return result.content.sentiment
}
func genereerSuggesties(
voor context: String
) async throws -> [String] {
@Generable
struct Suggesties: Equatable {
@Guide(.count(3))
let items: [String]
}
let session = maakSessie(
instructies: "Generate helpful suggestions based on context."
)
let result = try await session.respond(
to: context,
generating: Suggesties.self
)
return result.content.items
}
}
Foutafhandeling
Het framework gebruikt het standaard async throws-patroon van Swift. Zorg voor goede foutafhandeling, want het model is niet altijd beschikbaar:
func veiligGenereren(prompt: String) async -> String {
guard SystemLanguageModel.default.availability == .available else {
return "AI-functies zijn niet beschikbaar op dit apparaat."
}
let session = LanguageModelSession()
do {
let result = try await session.respond(to: prompt)
return result.content
} catch {
return "Kan geen antwoord genereren. Probeer het later opnieuw."
}
}
Conclusie
Het Foundation Models framework markeert een nieuw tijdperk in iOS-ontwikkeling. Voor het eerst kunnen we als ontwikkelaars krachtige AI-mogelijkheden integreren die volledig on-device draaien — zonder serverkosten, zonder privacyzorgen en zonder internetafhankelijkheid.
Even de belangrijkste punten op een rijtje:
- Eenvoudige integratie: Met
LanguageModelSessionben je in drie regels code aan de slag - Typeveilige uitvoer: Het
@Generable-macro maakt gestructureerde generatie eenvoudig en betrouwbaar - Streaming:
PartiallyGenerated-typen maken progressieve UI-updates mogelijk - Tool calling: Het
Tool-protocol laat het model je app-logica aanroepen - Privacy: Alle verwerking gebeurt lokaal op het apparaat
- Gratis: Geen API-kosten voor on-device inferentie
Of je nu een simpele tekstgenerator bouwt of een complexe AI-gestuurde applicatie — het Foundation Models framework biedt alles wat je nodig hebt. Begin met de basis, experimenteer met gestructureerde uitvoer, en breid je implementatie geleidelijk uit met streaming en tools.
De toekomst van iOS-ontwikkeling is intelligent, privé en on-device. En eerlijk? Ik kan niet wachten om te zien wat de community ermee gaat bouwen.