Foundation Models hallintaan: Käytännön opas laitteella toimivaan tekoälyyn iOS 26:ssa

Opi hyödyntämään Applen Foundation Models -kehystä iOS 26 -sovelluksissasi. Käymme läpi LanguageModelSession-peruskäytön, @Generable-makron, streaming-vastaukset, työkalukutsut ja rajoitukset — toimivien Swift-koodiesimerkkien kera.

Johdanto: Tekoäly muuttui juuri paikalliseksi

Applen WWDC25 muutti iOS-kehityksen suuntaa tavalla, jota harva osasi odottaa. Foundation Models -kehys antaa sinulle suoran pääsyn samaan laitteella toimivaan kielimalliin, joka pyörittää Apple Intelligencea — ja se onnistuu muutamalla rivillä Swift-koodia.

Ei API-avaimia. Ei pilvipalvelumaksuja. Ei internet-yhteyttä. Ja mikä parasta: käyttäjien data pysyy kokonaan laitteella.

Jos olet seurannut aiempia artikkeleitamme Swift 6.2:n rinnakkaisuudesta, SwiftData-oppaasta ja @Observable-makrosta, tiedät jo, että Apple panostaa moderniin, tyyppiturvallisuutta korostavaan kehittäjäkokemukseen. Foundation Models on tavallaan tämän filosofian looginen jatke — tekoäly, joka istuu saumattomasti Swift-tyyppijärjestelmään.

Tässä artikkelissa rakennamme yhdessä tekoälyominaisuuksia alusta loppuun. Käymme läpi peruskäytön, strukturoidun tulosteen @Generable-makrolla, streaming-vastaukset SwiftUI:ssa, työkalukutsut ja käytännön rajoitukset. Jokainen esimerkki on toimivaa koodia, jonka voit kopioida suoraan omaan projektiisi.

Vaatimukset ja yhteensopivuus

Ennen kuin kirjoitat ensimmäistäkään koodiriviä, tarkista nämä:

  • Xcode 26 asennettuna macOS Tahoe -käyttöjärjestelmässä
  • iOS 26, iPadOS 26 tai macOS Tahoe 26 kohdealustana
  • Apple Intelligence ladattuna ja käytössä laitteella (se on noin 3 Gt:n lataus, joten varaa aikaa)
  • Yhteensopiva laite: iPhone 15 Pro tai uudempi, M1-sirulla tai uudemmalla varustettu iPad tai Mac

Yksi asia kannattaa tiedostaa heti alkuun: tällä hetkellä noin 15 % käytössä olevista iPhoneista, 30 % iPadeista ja 50 % Maceista tukee Foundation Models -kehystä. Prosentit kasvavat koko ajan, mutta toistaiseksi sinun tulee aina tarjota vaihtoehtoinen käyttökokemus laitteille, jotka eivät tue tekoälyominaisuuksia. Tämä ei ole vapaaehtoista — se on hyvää sovelluskehitystä.

Alkuun pääseminen: LanguageModelSession

Foundation Models -kehyksen sydän on LanguageModelSession-luokka. Se hoitaa keskusteluhistorian, lähettää kehotteita mallille ja palauttaa vastauksia.

Aloitetaan yksinkertaisimmasta mahdollisesta esimerkistä:

import FoundationModels

let session = LanguageModelSession()
let response = try await session.respond(to: "Selitä Swift-protokollat lyhyesti")
print(response)

Siinä se. Ei, oikeasti — tuo on kaikki mitä tarvitaan. LanguageModelSession käyttää oletuksena SystemLanguageModel.default-mallia, joka on Applen noin 3 miljardin parametrin kielimalli. Se on optimoitu tekstin tuottamiseen, tiivistämiseen, luokitteluun ja entiteettien poimimiseen.

Mallin saatavuuden tarkistaminen

Koska malli ei ole käytettävissä kaikilla laitteilla, saatavuus pitää aina tarkistaa ennen käyttöä. Tämä on se kohta, jonka moni unohtaa ensimmäisessä versiossa (olen itsekin syyllistynyt tähän):

import FoundationModels

let availability = SystemLanguageModel.default.availability

switch availability {
case .available:
    // Malli on valmis käytettäväksi
    let session = LanguageModelSession()
    let response = try await session.respond(to: "Hei!")
    print(response)
case .unavailable:
    // Laite ei tue Apple Intelligencea
    print("Tämä laite ei tue Foundation Models -kehystä")
case .disabled:
    // Apple Intelligence on poistettu käytöstä asetuksissa
    print("Ota Apple Intelligence käyttöön asetuksista")
@unknown default:
    break
}

Mallin esilataus

Jos tiedät, että tarvitset mallia pian, voit ladata sen muistiin etukäteen prewarm-metodilla. Tämä pienentää ensimmäisen vastauksen viivettä merkittävästi — ja se tuntuu käyttäjästä huomattavasti sulavammalta:

let session = LanguageModelSession()
try await session.prewarm(promptPrefix: "Analysoi seuraava teksti:")

Strukturoitu tuloste @Generable-makrolla

Perinteinen tekstipohjainen tekoälyvastaus on rehellisesti sanottuna epäluotettava: saat vapaamuotoista tekstiä, jota pitää parsia ja sitten toivoa, että formaatti pysyy jotenkin samana kerrasta toiseen. Foundation Models ratkaisee tämän ongelman Guided Generation -ominaisuudella, joka pakottaa mallin tuottamaan rakenteellista, tyyppiturvallia dataa suoraan Swift-tyyppeinä.

@Generable-makro luo käännösaikana JSON-skeeman määrittelemällesi tyypille. Malli pakotetaan token-tasolla tuottamaan tätä skeemaa vastaavaa dataa. Tämä ei siis ole pelkkää toivomista — se on rakenteellista dekoodausta.

import FoundationModels

@Generable
struct ReseptiEhdotus {
    @Guide(description: "Reseptin nimi suomeksi")
    var nimi: String
    
    @Guide(description: "Valmistusaika minuutteina")
    var valmistusaika: Int
    
    @Guide(description: "Lista raaka-aineista")
    var ainekset: [String]
    
    @Guide(description: "Vaiheittaiset valmistusohjeet")
    var vaiheet: [String]
    
    @Guide(.anyOf(["helppo", "keskivaikea", "vaativa"]))
    var vaikeustaso: String
}

Huomaa, miten @Guide-makrolla voimme antaa luonnollisella kielellä ohjeita mallin tulosteelle ja jopa rajoittaa mahdolliset arvot .anyOf-parametrilla. Kokeillaan tätä käytännössä:

let session = LanguageModelSession()

let resepti: ReseptiEhdotus = try await session.respond(
    to: "Ehdota helppo pasta-resepti kahdelle hengelle",
    generating: ReseptiEhdotus.self
)

print(resepti.nimi)           // "Sitruunapasta fetajuustolla"
print(resepti.valmistusaika)  // 20
print(resepti.ainekset)       // ["pasta", "sitruuna", "fetajuusto", ...]
print(resepti.vaikeustaso)    // "helppo"

Tämä on valtava parannus verrattuna vapaamuotoisen tekstin parsimiseen. Saat käännösaikaisen tyyppitarkistuksen, automaattisen deserialisoinnin ja varmuuden siitä, että vastaus noudattaa määrittelemääsi rakennetta. Mielestäni tämä on yksi koko kehyksen parhaista ominaisuuksista.

@Generable-tuetut tyypit

@Generable tukee seuraavia tyyppejä ominaisuuksina:

  • Primitiivit: String, Int, Double, Float, Decimal, Bool
  • Taulukot: [String], [Int] ja muut generoitavien tyyppien taulukot
  • Sisäkkäiset @Generable-tyypit: voit koostaa monimutkaisia rakenteita
  • Enumit: myös @Generable-merkityt enumit toimivat
  • Rekursiiviset tyypit: esimerkiksi puurakenteille — käytännöllistä, vaikka harvemmin tarvittavaa

Streaming-vastaukset SwiftUI:ssa

Kukaan ei halua tuijottaa tyhjää näyttöä 10–20 sekuntia. Streaming-vastaukset ratkaisevat tämän näyttämällä tulosteen sitä mukaa kuin malli tuottaa sitä.

Foundation Models -kehyksen streaming ei toimi perinteisellä token-per-token-periaatteella. Sen sijaan se lähettää tilannekuvia (snapshots) osittain generoidusta rakenteellisesta tulosteesta. @Generable-makro luo automaattisesti PartiallyGenerated-tyypin, jossa kaikki ominaisuudet ovat optionaalisia. Tämä on itse asiassa aika nerokas ratkaisu.

import SwiftUI
import FoundationModels

struct ReseptiNakyma: View {
    @State private var osittainenResepti: ReseptiEhdotus.PartiallyGenerated?
    @State private var onValmis = false
    @State private var lataa = false
    
    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 16) {
                if lataa {
                    ProgressView("Luodaan reseptiä...")
                }
                
                if let resepti = osittainenResepti {
                    if let nimi = resepti.nimi {
                        Text(nimi)
                            .font(.title)
                            .bold()
                    }
                    
                    if let aika = resepti.valmistusaika {
                        Label("\(aika) min", systemImage: "clock")
                    }
                    
                    if let ainekset = resepti.ainekset, !ainekset.isEmpty {
                        Text("Raaka-aineet:")
                            .font(.headline)
                        ForEach(ainekset, id: \.self) { aines in
                            Text("• \(aines)")
                        }
                    }
                    
                    if let vaiheet = resepti.vaiheet, !vaiheet.isEmpty {
                        Text("Valmistus:")
                            .font(.headline)
                        ForEach(Array(vaiheet.enumerated()), id: \.offset) { i, vaihe in
                            Text("\(i + 1). \(vaihe)")
                        }
                    }
                }
            }
            .padding()
        }
        .task {
            await luoResepti()
        }
    }
    
    func luoResepti() async {
        lataa = true
        let session = LanguageModelSession()
        
        do {
            let stream = session.streamResponse(
                to: "Ehdota nopea ja terveellinen lounasresepti",
                generating: ReseptiEhdotus.self
            )
            
            for try await osittainen in stream {
                osittainenResepti = osittainen
            }
            
            onValmis = true
        } catch {
            print("Virhe: \(error)")
        }
        
        lataa = false
    }
}

Lopputulos on elegantti: näkymä päivittyy automaattisesti sitä mukaa kun mallin tuottamat ominaisuudet täyttyvät. Käyttäjä näkee ensin nimen, sitten valmistusajan, raaka-aineet ja lopuksi vaiheet — kaikki reaaliajassa. Kokemuksena se muistuttaa ChatGPT:n streaming-vastausta, mutta kaikki tapahtuu paikallisesti.

Tärkeä huomio ominaisuuksien järjestyksestä

Foundation Models generoi ominaisuudet siinä järjestyksessä kuin ne on määritelty @Generable-tyypissä. Käytännössä tämä tarkoittaa, että sinun kannattaa laittaa käyttöliittymässä ensimmäisenä näytettävät ominaisuudet ensin — esimerkiksi otsikko ennen sisältöä.

Toinen asia: malli generoi kaikki ominaisuudet riippumatta siitä, käytätkö niitä kaikkia näkymässäsi. Älä siis lisää ylimääräisiä kenttiä "varmuuden vuoksi".

Työkalukutsut: Mallin kykyjen laajentaminen

Laitteella toimiva kielimalli ei tiedä mitään sovelluksesi datasta, nykyisestä säästä tai käyttäjän kalenterista. Työkalukutsut (Tool Calling) ratkaisevat tämän antamalla mallin kutsua kehittäjän määrittelemiä funktioita dynaamisesti.

Työkalut ovat Swift-tyyppejä, jotka noudattavat Tool-protokollaa. Malli päättää itse milloin työkalu on tarpeen — ja kehys hoitaa kutsun suorituksen ja tuloksen palauttamisen automaattisesti. Sinun ei tarvitse hallita tätä vuorovaikutusta itse.

import FoundationModels

@Generable
struct SaaEnnuste {
    var lampotila: Double
    var kuvaus: String
    var kaupunki: String
}

struct SaaTyokalu: Tool {
    let name = "hae_saa"
    let description = "Hakee nykyisen sään annetulle kaupungille"
    
    @Generable
    struct Arguments {
        @Guide(description: "Kaupungin nimi")
        var kaupunki: String
    }
    
    func call(arguments: Arguments) async throws -> String {
        // Tässä kutsuisit oikeaa sää-APIa, kuten WeatherKit
        // Palautetaan esimerkkinä staattista dataa
        return "Lämpötila \(arguments.kaupunki)ssa: 7°C, puolipilvistä"
    }
}

Työkalun käyttäminen sessiossa on suoraviivaista:

let session = LanguageModelSession()

let vastaus = try await session.respond(
    to: "Millainen sää on tänään Helsingissä?",
    tools: [SaaTyokalu()]
)

print(vastaus) // Malli käyttää työkalun palauttamaa dataa vastauksessaan

Taustalla tapahtuu paljon: malli analysoi käyttäjän kysymyksen, tunnistaa tarpeen kutsua säätyökalua, suorittaa kutsun ja muodostaa vastauksen yhdistämällä työkalun palauttaman datan luonnolliseen kieleen. Kaikki tämä tapahtuu automaattisesti, ilman että sinun tarvitsee kirjoittaa mitään ylimääräistä logiikkaa.

Käytännön esimerkki: Älykäs muistiinpanosovellus

Nyt kun perusasiat ovat hallussa, yhdistetään kaikki opittu käytännön esimerkkiin. Rakennetaan yksinkertainen muistiinpanotoiminto, joka analysoi ja luokittelee käyttäjän tekstiä automaattisesti:

import FoundationModels

@Generable
struct MuistiinpanoAnalyysi {
    @Guide(description: "Lyhyt yhteenveto muistiinpanosta, 1-2 lausetta")
    var tiivistelma: String
    
    @Guide(.anyOf(["työ", "henkilökohtainen", "opiskelu", "terveys", "talous", "muu"]))
    var kategoria: String
    
    @Guide(description: "Lista keskeisistä tehtävistä, joita tekstissä mainitaan")
    var tehtavat: [String]
    
    @Guide(description: "Kiireellisyystaso 1-5, jossa 5 on kiireellisin")
    var kiireellisyys: Int
}

class MuistiinpanopalveluAI {
    private let session = LanguageModelSession()
    
    func analysoi(teksti: String) async throws -> MuistiinpanoAnalyysi {
        guard SystemLanguageModel.default.availability == .available else {
            throw MuistiinpanoVirhe.malliEiSaatavilla
        }
        
        return try await session.respond(
            to: """
            Analysoi seuraava muistiinpano ja tunnista siitä kategoria, \
            tehtävät ja kiireellisyys:
            
            \(teksti)
            """,
            generating: MuistiinpanoAnalyysi.self
        )
    }
}

enum MuistiinpanoVirhe: Error {
    case malliEiSaatavilla
}

Tämä palvelu integroituu helposti mihin tahansa SwiftUI-näkymään. Käyttäjä kirjoittaa muistiinpanon, ja sovellus luokittelee sen automaattisesti, poimii tehtävät ja arvioi kiireellisyyden — kaikki ilman internet-yhteyttä. Aika hienoa, eikö?

Rajoitukset ja parhaat käytännöt

Foundation Models on vaikuttava kehys, mutta täydellinen se ei ole. Rajoitusten tunteminen on kriittistä ennen kuin laitat mitään tuotantoon.

Mallin rajoitukset

  • Vain tekstisyöte: Toisin kuin pilvipohjaiset multimodaaliset mallit, Foundation Models tukee ainoastaan tekstisyötettä. Kuvia, ääntä tai videoita ei voi käsitellä.
  • Mallin koko: Noin 3 miljardin parametrin malli on kompakti. Se ei pärjää pilvipalveluiden suurille kielimalleille monimutkaisessa päättelyssä — mutta yksinkertaisiin tehtäviin se riittää mainiosti.
  • Nopeus vaihtelee: iPhone 15 Pro:lla ensimmäisen tokenin viive on noin 0,6 ms/kehotetoken ja generointinopeus noin 30 tokenia sekunnissa. Vanhemmilla M1-laitteilla nopeus voi pudota merkittävästi.
  • Sisältösuodatus: Malli sisältää turvallisuussuodattimia, jotka saattavat tuottaa vääriä hälytyksiä. Testaa kehotteesi huolellisesti eri syötteillä.

Parhaat käytännöt

  • Tarkista aina saatavuus: Käytä SystemLanguageModel.default.availability-tarkistusta ja tarjoa aina vaihtoehto laitteille, jotka eivät tue kehystä.
  • Käytä esilatauksia: prewarm-metodi vähentää viivettä käyttöliittymissä, joissa malli tarvitaan nopeasti.
  • Optimoi @Generable-tyypit: Määrittele vain tarvittavat ominaisuudet ja järjestä ne sen mukaan, miten ne näytetään käyttöliittymässä.
  • Hyödynnä streaming-vastauksia: Älä pakota käyttäjää odottamaan koko vastausta — käytä streamResponse-metodia aina kun mahdollista.
  • Käsittele virheet kunnolla: Malli voi epäonnistua nopeusrajoitusten, muistipaineen tai turvallisuussuodattimien vuoksi. Varaudu näihin kaikkiin.

Nopeusrajoitukset

Apple soveltaa nopeusrajoituksia Foundation Models -kehyksen käytölle, mutta pieni yllätys: etualan sovelluksilla, joilla on käyttöliittymä, ei ole nopeusrajoitusta. Sen sijaan macOS:n komentorivityökaluilla rajoituksia sovelletaan. Pidä tämä mielessä erityisesti testatessa ja automaatiokäytössä.

Yhteenveto: Milloin käyttää Foundation Models -kehystä?

Foundation Models loistaa käyttötapauksissa, joissa tarvitaan:

  • Yksityisyyttä: Arkaluontoinen data pysyy laitteella
  • Offline-toimintaa: Sovellus toimii ilman internet-yhteyttä
  • Kustannustehokkuutta: Ei API-maksuja — käyttö on rajatonta
  • Tyyppiturvallisuutta: @Generable takaa käännösaikaisen tarkistuksen

Jos taas tarvitset multimodaalista syötettä, suurta konteksti-ikkunaa tai pilvimallien tasoista päättelykykyä, pilvipohjaiset API:t ovat parempi valinta. Omasta kokemuksesta voin sanoa, että paras strategia tuotantosovelluksessa on yhdistää molemmat: käytä Foundation Models -kehystä nopeisiin, yksityisyyttä vaativiin tehtäviin ja pilvi-API:a monimutkaisempiin tarpeisiin.

Usein kysytyt kysymykset (UKK)

Tarvitseeko Foundation Models -kehys internet-yhteyttä?

Ei tarvitse. Kielimalli toimii kokonaan laitteella Apple Silicon -suorittimella, GPU:lla ja Neural Enginellä. Apple Intelligence -ominaisuuksien alkuperäinen lataus (noin 3 Gt) tosin vaatii yhteyden, mutta sen jälkeen kaikki pyörii paikallisesti.

Onko Foundation Models -kehyksen käyttö ilmaista?

Kyllä, täysin ilmaista. Toisin kuin pilvipohjaiset API:t, käyttökerroista tai tokeneista ei peritä maksua. Malli on osa käyttöjärjestelmää, joten se ei myöskään kasvata sovelluksesi kokoa.

Mitkä laitteet tukevat Foundation Models -kehystä?

iPhone 15 Pro tai uudempi, M1-sirulla tai uudemmalla varustetut iPadit ja Macit. Lisäksi laitteella on oltava iOS 26, iPadOS 26 tai macOS Tahoe 26, ja Apple Intelligencen on oltava käytössä. Tällä hetkellä noin 15 % iPhoneista, 30 % iPadeista ja 50 % Maceista on yhteensopivia.

Voiko @Generable-makroa käyttää enumien kanssa?

Kyllä voi. @Generable tukee sekä structeja että enumeja. Voit käyttää sitä esimerkiksi kategorisointiin, jossa malli valitsee oikean enum-tapauksen kontekstin perusteella. Myös rekursiiviset tyypit ovat tuettuja.

Miten Foundation Models eroaa pilvipohjaisista kielimalleista kuten ChatGPT?

Suurin ero on suorituspaikka: Foundation Models toimii täysin laitteella, kun pilvipohjaiset mallit vaativat internet-yhteyden ja palvelinpuolen käsittelyn. Applen malli on pienempi (noin 3 miljardia parametria) ja tukee vain tekstisyötettä, mutta tarjoaa ylivoimaisen yksityisyyden, offline-toiminnan ja nollakulut. Pilvipohjaiset mallit puolestaan tarjoavat suuremman konteksti-ikkunan, multimodaalisen syötteen ja tehokkaamman päättelykyvyn. Molemmilla on paikkansa.

Tietoa Kirjoittajasta Editorial Team

Our team of expert writers and editors.