App Intents Swiftissä 2026: Opas Siriin, Shortcutsiin ja Apple Intelligenceen

Opi rakentamaan App Intents Swiftissä ja altistamaan sovelluksesi toiminnot Siriin, Shortcutsiin ja Apple Intelligenceen. Koodiesimerkit, alustavertailu ja käytännön vinkit.

App Intents Swiftissä: Siri-opas 2026

Päivitetty: 12. elokuuta 2026

App Intents on Applen ensisijainen Swift-kehys, jolla sovelluksesi toiminnot altistetaan järjestelmätason pinnoille: Siriin, Shortcuts-appiin, Spotlightiin, Focus-suodattimiin, interaktiivisiin widgetteihin ja Apple Intelligenceen. Toisin kuin vanha SiriKit, App Intents ei rajoita sinua ennalta määriteltyihin domain-luokkiin. Kirjoitat oman AppIntent-protokollaa toteuttavan tyypin, ja järjestelmä tarjoilee sen automaattisesti käyttäjille sopivissa kohdissa. Tässä oppaassa rakennamme kokonaisia intentejä puhtaassa Swiftissä ja käydään läpi, miten sama koodi käyttäytyy eri Applen alustoilla.

  • AppIntent-protokolla määrittelee yhden käyttäjän suorittaman toiminnon, joka voi käynnistyä Siristä, Shortcutsista, widgetistä tai Apple Intelligencesta.
  • AppEntity altistaa sovelluksesi tietomallin järjestelmälle, jotta intentit voivat viitata konkreettisiin objekteihin, kuten muistiinpanoihin tai tehtäviin.
  • AppShortcut ja AppShortcutsProvider tekevät intentistä löydettävän ilman, että käyttäjän tarvitsee avata Shortcuts-appia.
  • iOS 26:n interaktiiviset widgetit ja Live Activityt kutsuvat App Intentejä suoraan, eli käytännössä sama koodi ajaa sekä Siri-komentoa että widget-painiketta.
  • Apple Intelligence käyttää App Intentejä ja niiden @Parameter-metadataa työkalupohjaisen kutsun (tool-calling) polttoaineena. Hyvät kuvaukset ja synonyymit ovat 2026:ssa SEO:ta vastaava optimointikohde.
  • Alustaerot ovat pieniä mutta oikeita: watchOS ei tue kaikkia parametrityyppejä, ja visionOS lisää spatiaalisen kontekstin, jonka voit hyödyntää @Dependency-injektoinnin kautta.

Mikä on App Intents -kehys?

App Intents on iOS 16:ssa esitelty ja iOS 26:ssa merkittävästi laajennettu Swift-kehys, jolla julkaiset sovelluksesi toiminnot ja tietomallit järjestelmän käytettäväksi. Käytännössä yksi intent-tyyppi vastaa yhtä tekoa, jonka käyttäjä voi haluta suorittaa: "lisää tehtävä", "aloita treeni", "arkistoi muistiinpano". Kun määrittelet intentin, se ilmestyy automaattisesti Shortcuts-appiin, sitä voi kutsua Siristä, se toimii Spotlight-toiminnona, sen voi liittää widget-painikkeeseen, ja iOS 26:sta alkaen Apple Intelligence osaa käyttää sitä työkaluna keskustelun aikana.

Tekninen ero SiriKitiin on ratkaiseva. SiriKit vaati intent-määrittelyt .intentdefinition-tiedostoon ja rajoitti toiminnot Applen ennalta määriteltyihin domaineihin (viestit, treenit, maksut ja niin edelleen). App Intents taas on puhtaasti Swift-koodia. Kuvailet intentin, sen parametrit ja lokalisoidun tekstin suoraan tyypissä, ja rakennusjärjestelmä generoi metadatan Xcode 26:n AppIntentsMetadataProcessor-vaiheessa. Tämä tarkoittaa myös sitä, että intentin dokumentaatio ja koodi elävät samassa paikassa, mikä pienentää katvealuetta merkittävästi.

Kehyksellä on kolme pääprotokollaa, jotka näkyvät joka projektissa: AppIntent (toiminto), AppEntity (viitattava objekti) ja AppEnum (rajattu valintajoukko). Näiden lisäksi AppShortcutsProvider ilmoittaa käyttöjärjestelmälle, mitkä intentit näkyvät oletusarvoisesti Spotlightissa ja Siri-ehdotuksissa.

Ensimmäisen App Intentin luominen Swiftissä

Aloitetaan minimaalisella toimivalla esimerkillä. Rakennamme intentin, joka lisää muistiinpanon ja palauttaa vahvistuksen käyttäjälle. Lisää uusi Swift-tiedosto sovelluksesi kohteeseen (älä laita sitä erilliseen kehykseen, ellet erikseen konfiguroi APP_INTENTS_METADATA_EXTRACTOR-asetusta) ja kirjoita seuraava koodi:

import AppIntents

struct AddNoteIntent: AppIntent {
    static let title: LocalizedStringResource = "Lisää muistiinpano"
    static let description = IntentDescription(
        "Luo uusi muistiinpano annetulla otsikolla ja sisällöllä.",
        categoryName: "Muistiinpanot",
        searchKeywords: ["muistio", "muistiinpano", "note"]
    )

    @Parameter(title: "Otsikko", inputOptions: .init(capitalizationType: .sentences))
    var noteTitle: String

    @Parameter(title: "Sisältö", inputOptions: .init(multiline: true))
    var body: String

    static var parameterSummary: some ParameterSummary {
        Summary("Lisää muistiinpano \(\.$noteTitle) sisällöllä \(\.$body)")
    }

    @Dependency
    private var store: NoteStore

    func perform() async throws -> some IntentResult & ProvidesDialog {
        let note = try await store.create(title: noteTitle, body: body)
        return .result(dialog: "Tallensin muistiinpanon \(note.title).")
    }
}

Muutama huomio yksityiskohtiin. LocalizedStringResource on avain lokalisointiin: Xcode 26 poimii kaikki nämä merkkijonot automaattisesti String Catalogiin, joten sinun ei tarvitse ylläpitää erillistä .strings-tiedostoa intent-teksteille. parameterSummary puolestaan määrittää, miltä intentti näyttää Shortcuts-appin lauseessa. Se on käyttäjäkokemuksen kannalta tärkein rivi koko tiedostossa, koska se kertoo, miten Shortcuts renderöi täytettävät kentät.

Rehellisesti sanottuna kompastuin tähän itse ensimmäisessä App Intents -projektissani: jätin parameterSummary-määrittelyn pois ja ihmettelin, miksi Shortcuts näytti intentin nimen ilman kenttiä. Se on pakollinen jokaiselle intentille, jolla on käyttäjän täytettäviä parametreja.

@Dependency on iOS 17:ssä tuotu injektiomekanismi. Se hakee riippuvuuden App Intents Extensionissa tai isäntäsovelluksessa AppDependencyManager-rekisteristä, joka rekisteröidään sovelluksen käynnistyksessä. Tämä on paras tapa välttää tila-ongelmia, kun sama intent voi ajautua joko pääprosessissa (widget-painike) tai erillisessä laajennuksessa (Siri taustalla).

AppEntity ja AppEnum: sovelluksen mallin altistaminen

Intentit tulevat todella hyödyllisiksi vasta, kun ne osaavat viitata sovelluksesi omiin objekteihin. Tämä tehdään AppEntity-protokollalla. Entiteetti on periaatteessa arvo, jolla on vakaa tunniste, näyttönimi ja valinnainen kuvaus, eli kaikki mitä järjestelmä tarvitsee näyttääkseen sen käyttäjälle ja välittääkseen sen intentille parametrina.

import AppIntents

struct NoteEntity: AppEntity {
    let id: UUID
    let title: String
    let updatedAt: Date

    static let typeDisplayRepresentation = TypeDisplayRepresentation(
        name: "Muistiinpano",
        numericFormat: "\(placeholder: .int) muistiinpanoa"
    )

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "\(title)",
            subtitle: "Päivitetty \(updatedAt.formatted(.relative(presentation: .named)))"
        )
    }

    static let defaultQuery = NoteQuery()
}

struct NoteQuery: EntityQuery {
    @Dependency private var store: NoteStore

    func entities(for identifiers: [NoteEntity.ID]) async throws -> [NoteEntity] {
        try await store.notes(ids: identifiers)
    }

    func suggestedEntities() async throws -> [NoteEntity] {
        try await store.recent(limit: 10)
    }
}

EntityQuery on se osa, jonka useimmat kehittäjät unohtavat toteuttaa oikein. entities(for:) on pakollinen, ja järjestelmä kutsuu sitä ratkaistakseen aiemmin tallennetut viittaukset. Esimerkiksi kun käyttäjän tallentama pikakomento suoritetaan uudelleen viikkoja myöhemmin. suggestedEntities() puolestaan syöttää Shortcuts-appin "Valitse muistiinpano" -valikkoa. Jos jätät tämän tyhjäksi, käyttäjä joutuu kirjoittamaan tunnisteen käsin, mikä käytännössä tappaa toiminnon.

Rajatuille valintajoukoille käytä AppEnum-protokollaa. Se on kevyempi kuin entiteetti eikä vaadi kyselyä:

enum NotePriority: String, AppEnum {
    case low, medium, high

    static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Prioriteetti")

    static let caseDisplayRepresentations: [NotePriority: DisplayRepresentation] = [
        .low: "Matala",
        .medium: "Normaali",
        .high: "Korkea"
    ]
}

AppShortcut ja Siri-integraatio ilman lisämääritystä

Kun intenttisi on määritelty, se on jo teknisesti käytettävissä Shortcuts-appissa. Se ei kuitenkaan ilmesty Siri-ehdotuksiin eikä käyttäjä voi sanoa "Hei Siri, lisää muistiinpano" ennen kuin julistat siitä AppShortcut-tyypin kautta. Tämä on ainoa tapa sitoa fraaseja intentteihin ilman käyttäjän manuaalista määrittelyä.

struct NotesShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: AddNoteIntent(),
            phrases: [
                "Lisää muistiinpano \(.applicationName)-sovellukseen",
                "Kirjaa \(.applicationName) muistiinpano \(\.$noteTitle)",
                "Tallenna \(\.$noteTitle) \(.applicationName)-sovellukseen"
            ],
            shortTitle: "Lisää muistiinpano",
            systemImageName: "note.text.badge.plus"
        )
    }

    static var shortcutTileColor: ShortcutTileColor = .lightBlue
}

Fraasien on aina sisällettävä .applicationName-paikanpitäjä. Ilman sitä Siri kieltäytyy rekisteröimästä pikakomentoa, koska se ei tiedä, mihin sovellukseen komento kuuluu. Voit tarjota useita fraaseja, ja Siri valitsee niistä lähimmän vastauksen käyttäjän puheesta. Iso vinkki: pidä fraasit lyhyinä ja sisällytä parametri, jonka Siri voi täyttää suoraan puheesta. Se on paljon luotettavampaa kuin fraasi, joka pyytää Siriä avaamaan täydentävän kysymyksen.

Jos rakennat monikielisen sovelluksen, App Shortcuts käyttää AppShortcuts.strings-tiedostoa (Xcode luo sen automaattisesti), johon voit lisätä kunkin kielen versiot. Suomen-, ruotsin- ja englanninkieliset fraasit voivat olla erilaisia, koska järjestelmä ei yritä kääntää niitä automaattisesti.

App Intents ja Apple Intelligence: mitä metadata todella tekee

iOS 26:ssa Apple Intelligence käyttää App Intentejä työkaluina samaan tapaan kuin OpenAI:n function calling tai Anthropicin tool use. Käyttäjä pyytää mallia "arkistoi eiliset muistiinpanot", ja malli valitsee sopivan intentin sekä täyttää sen parametrit oman kontekstinsa perusteella. Tämä tekee IntentDescription-kentästä, searchKeywords-listasta ja parametrien otsikoista mallin näkökulmasta yhtä tärkeitä kuin metadata on hakukoneille.

Käytännön suositukset perustuvat Applen 2026-dokumentaatioon ja WWDC26-sessioihin. Kirjoita kuvaus toisen persoonan verbimuodossa ("Arkistoi valitut muistiinpanot", ei "Muistiinpanojen arkistointi"). Sisällytä searchKeywords-listaan sekä muodolliset että puhekieliset variantit, koska Apple Intelligencen kielimalli osaa yhdistää ne parametreihin. Jos parametrilla on rajoituksia (esimerkiksi vain tietyt prioriteetit sallittu), määrittele ne AppEnum-tyyppinä, jolloin malli oppii välittämään vain kelvollisia arvoja.

Jos sinulla on jo Foundation Models -kehykseen perustuva Apple Intelligence -integraatio, App Intents täydentää sitä täydellisesti. Foundation Models -sessiot pystyvät kutsumaan omia työkalujasi Tool-protokollan kautta, ja tuon työkalun voi delegoida suoraan App Intent -tyyppille. Tuloksena sama toiminto on käytettävissä sekä järjestelmätason Apple Intelligencen että oman malli-integraatiosi kautta.

Interaktiiviset widgetit ja Live Activityt intenteillä

Yksi App Intentien vahvimmista puolista on se, että sama koodi ajaa myös interaktiivisten widgettien painikkeita. iOS 17 esitteli Button(intent:)- ja Toggle(isOn:intent:)-alustajat, ja iOS 26 laajensi tuen Live Activityihin ja Dynamic Island -alueen komponentteihin. Tämä poistaa aiemman vaatimuksen käyttää URL-skeemoja widgetien vuorovaikutukseen.

struct QuickCaptureWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "QuickCapture", provider: Provider()) { entry in
            VStack {
                Text(entry.latestNote?.title ?? "Ei muistiinpanoja")
                Button(intent: AddNoteIntent.blank()) {
                    Label("Uusi muistiinpano", systemImage: "plus")
                }
                .buttonStyle(.borderedProminent)
            }
        }
        .configurationDisplayName("Pikakaappaus")
    }
}

extension AddNoteIntent {
    static func blank() -> AddNoteIntent {
        var intent = AddNoteIntent()
        intent.noteTitle = ""
        intent.body = ""
        return intent
    }
}

Kun käyttäjä koskettaa painiketta, WidgetKit ajaa intentin ilman sovelluksen avaamista, ellei perform() palauta OpensIntent-tulosta. Tämä on iso käyttäjäkokemuksen parannus, mutta tarkoittaa myös, että intentin suoritusaika on rajoitettu noin viiteen sekuntiin. Verkkopyynnöt kannattaa delegoida taustatehtävälle ja päivittää widget myöhemmin WidgetCenter.shared.reloadTimelines(ofKind:)-kutsulla.

Live Activityissa sama periaate pätee, mutta iOS 26 lisäsi ControlWidget-tyypin, jolla voit laittaa intent-painikkeet Ohjauskeskukseen ja lukitusnäytön alaosaan. Lue Live Activities ja Dynamic Island iOS 26 -oppaastamme tarkemmat esimerkit ActivityKitin puolesta.

App Intents eri Applen alustoilla

Yksi App Intentien lupauksista on "kirjoita kerran, ajaa kaikkialla". Todellisuus on hieman vivahteikkaampi, ja tässä alustaerot on hyvä tietää ennen kuin lupaat asiakkaalle täyden yhteensopivuuden.

OminaisuusiOS / iPadOS 26macOS TahoewatchOS 12visionOS 3
AppIntent + AppShortcutKylläKylläKylläKyllä
Interaktiiviset widgetit (Button/Toggle)KylläKylläRajoitettu (vain älypino)Kyllä
Focus-suodattimet (SetFocusFilterIntent)KylläKylläEiKyllä
Apple Intelligencen työkalukutsuKylläKylläEi paikallinen (kevätpäivitys myöhemmin)Kyllä
Spatial-kontekstin injektointiEiEiEiKyllä
Shortcuts-appi natiivinaKylläKylläEiKyllä

Kokemukseni mukaan tärkein alustaero on watchOS, joka tukee vain rajattua parametrijoukkoa (perustyypit ja yksinkertaiset entiteetit; ei esimerkiksi tiedostoparametreja). Tarkista aina #if os(watchOS)-lohkoilla, että intent-toteutuksesi tarjoaa realistisen vaihtoehdon rannekelloon. Usein tämä tarkoittaa erillistä yksinkertaistettua intent-versiota kellon puolelle.

visionOS puolestaan tuo mielenkiintoisen mahdollisuuden: voit lisätä intentin @Dependency-injektiona spatiaalisen sijainnin, jota käyttäjä katsoo. Tätä hyödyntävät esimerkiksi "kiinnitä tämä ikkuna" -tyyppiset komennot. Muista, että sama intent iOS:llä ohittaa spatiaalisen kontekstin turvallisesti nil-arvona, joten yhtä koodikantaa voi ajaa molemmilla alustoilla, kunhan käsittelet nil-tapauksen.

App Intents vs SiriKit: milloin siirtyä?

Jos sovelluksesi käyttää edelleen SiriKitiä, siirtyminen on syytä tehdä nyt. Apple on merkinnyt SiriKit Intents Extension -tyypin ylläpidettäväksi, mutta Apple Intelligence -integraatio, iOS 26:n interaktiiviset widgetit ja Ohjauskeskuksen intent-painikkeet toimivat vain App Intents -kehyksellä. Käytännön migraatiopolku on suoraviivainen: pidä SiriKit-laajennus ennallaan sitä käyttäville toiminnoille, mutta lisää uudet toiminnot App Intents -tyypeinä samaan projektiin. Kummatkin kehykset voivat elää rinnakkain.

Ainoa syy, miksi kannattaisi vielä nojata SiriKitiin, on tuki iOS 15:lle ja sitä vanhemmille. Jos sovelluksen minimikohde on iOS 16 tai uudempi, App Intents on käytännössä oikea valinta joka tapauksessa. Erityisesti jos hyödynnät SwiftUI:n @Observable-makroa tilanhallintaan, App Intents istuu paremmin yhteen modernin Swift-koodin kanssa. SiriKitin generoidut Objective-C-luokat ovat huomattavan hankalia yhdistää async/await-koodiin.

App Intentien testaus, virheenkäsittely ja vianetsintä

App Intentien testauksessa on kolme tasoa: yksikkötestit, integraatiotestit Shortcuts-appin kautta ja Applen erillinen Shortcuts Developer Mode. Yksikkötestit kirjoitetaan uudella Swift Testing -kehyksellä (tai XCTestillä, jos suosit sitä) ja ne pyörittävät perform()-metodia suoraan:

import Testing
@testable import NotesApp

@Test
func addNoteIntent_persistsNote() async throws {
    let store = InMemoryNoteStore()
    AppDependencyManager.shared.add(dependency: store)

    var intent = AddNoteIntent()
    intent.noteTitle = "Testi"
    intent.body = "Sisältö"

    let result = try await intent.perform()

    #expect(await store.count == 1)
    #expect(result.value is Void)
}

Huomaa, että AppDependencyManager.shared.add(dependency:) pitää kutsua ennen intentin suoritusta. Muuten @Dependency-injektio kaatuu ajonaikaisesti (tämän jäljittämiseen paloi minulla ihan liikaa aikaa yhdessä sovellusprojektissa). Jos käytät moderneja testaustyökaluja, tutustu Swift Testing -kehysoppaaseemme, jossa käsittelemme parametrisoituja testejä ja rinnakkaisajoa.

Virheiden käsittelyyn App Intents tarjoaa oman IntentError-mallin. Palauta lokalisoituja virheitä throw IntentError.custom("Muistiinpanoa ei voitu tallentaa: \(reason)")-tyyliin, jolloin Shortcuts ja Siri näyttävät viestin sellaisenaan. Vältä pelkkien Swift-virheiden heittämistä ilman tekstiä, koska käyttäjä näkee silloin geneerisen "Sovellus ei voinut suorittaa toimintoa" -viestin, joka ei auta korjaamaan ongelmaa.

Kolmas taso, App Intents Domain -sertifiointi Applen tarkastuksessa, on hyvä pitää mielessä julkaisua ennen. Applen App Review tarkastaa iOS 26:sta alkaen, että IntentDescription-tekstit ovat rehellisiä eivätkä lupaa mitään, mitä intent ei tosiasiassa tee. Yliampuvat kuvaukset ("Ratkaisee kaikki tehtäväsi") ovat yleisin hylkäyssyy.

Ulkoisia lähteitä syvempään opiskeluun: App Intents -viitedokumentaatio Apple Developerin sivuilla, WWDC-sessio "Bring your app's core features to users with App Intents" ja Human Interface Guidelines: App Shortcuts sisältävät kaikki Applen viralliset suositukset.

Usein kysytyt kysymykset

Mikä ero on App Intents- ja SiriKit-kehyksellä?

SiriKit oli rajoitettu Applen ennalta määriteltyihin domaineihin (viestit, treenit, maksut) ja vaati intent-määrittelyt .intentdefinition-tiedostoon. App Intents on puhtaasti Swift-pohjainen kehys, jossa määrittelet omat toiminnot ilman domain-rajoituksia. Vain App Intents tukee Apple Intelligenceä, interaktiivisia widgettejä ja Ohjauskeskuksen intent-painikkeita.

Voiko App Intent -kehystä käyttää ilman Siriä tai Shortcuts-appia?

Kyllä. Interaktiiviset widgetit, Live Activityt, Ohjauskeskuksen painikkeet ja Apple Intelligencen työkalukutsut käyttävät kaikki samaa AppIntent-tyyppiä, mutta eivät vaadi käyttäjältä Shortcuts-appin avaamista tai Siri-fraasin sanomista. Fraasit on syytä lisätä silti, koska ne parantavat löydettävyyttä.

Miten Apple Intelligence löytää ja valitsee oikean intentin?

Apple Intelligencen kielimalli tarkastelee IntentDescription-kuvausta, searchKeywords-listaa ja parametrien otsikoita valitakseen sopivimman intentin käyttäjän pyyntöön. Kirjoita kuvaus toisen persoonan verbimuodossa ja sisällytä sekä muodolliset että puhekieliset synonyymit. Tämä on käytännössä uusi SEO-optimointi.

Miksi App Intent -painike widgetissä ei tee mitään?

Yleisin syy on, että intent on määritelty erillisessä kehyksessä ilman APP_INTENTS_METADATA_EXTRACTOR-asetusta, jolloin WidgetKit ei näe sitä. Toinen yleinen syy on @Dependency-injektion puuttuminen widget-prosessissa. Muista rekisteröidä samat riippuvuudet sekä sovelluksen että widget-laajennuksen käynnistysajassa.

Tukeeko watchOS App Intent -kehystä täysin?

watchOS 12 tukee AppIntent- ja AppShortcut-tyyppejä sekä älypinon widget-painikkeita, mutta ei Focus-suodattimia eikä paikallista Apple Intelligencen työkalukutsua. Monimutkaiset parametrityypit (tiedostot, kuvat) eivät myöskään toimi, joten käytä yksinkertaistettuja intent-versioita kellon puolella.

Hiroshi Sato
Tietoa Kirjoittajasta Hiroshi Sato

Apple Platforms specialist building for iOS, macOS, visionOS, and the occasional watchOS app nobody asked for.