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:
@Generabletakaa 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.