App Intents in iOS 26: Der Cross-Platform-Guide für Siri, Shortcuts und Apple Intelligence
Ein pragmatischer Guide zu App Intents in iOS 26: wie du sie definierst, mit AppEntity und AppShortcut kombinierst, in Apple Intelligence einbindest und wo sich das Verhalten auf iPhone, iPad, Mac Catalyst und visionOS unterscheidet.
App Intents in iOS 26 sind ein deklaratives Swift-Framework, mit dem deine App einzelne Aktionen als typisierte, wiederverwendbare Bausteine für Siri, die Shortcuts-App, Spotlight, Widgets, Control Center und Apple Intelligence bereitstellt – ohne separate Intent-Definition-Files wie noch bei SiriKit. Du schreibst einen struct, der AppIntent konformiert, hängst Parameter dran, und dieselbe Aktion wird auf iPhone, iPad, Mac Catalyst und visionOS aufgerufen. In diesem Guide zeige ich, wie ich sie tatsächlich in Produktions-Apps aufbaue – inklusive der Stellen, an denen sich das Verhalten je nach Plattform beißt.
App Intents sind seit iOS 16 verfügbar, iOS 26 bringt tiefe Integration mit Apple Intelligence, Interaktive Snippets und Visual Intelligence.
Ein Intent besteht aus drei Bausteinen: AppIntent (die Aktion), AppEntity (die Datenobjekte) und AppShortcut (die Siri-Bindung).
Auf visionOS läuft dein Intent immer im Hintergrund – Dialoge werden im Ornament über deiner Volumetric Scene angezeigt.
Mac Catalyst-Apps müssen für Menu-Bar-Aktionen zusätzlich ForegroundContinuableIntent konformieren.
Der Intent-Metadata-Extractor läuft bei jedem Build – Fehler dort brechen die Xcode-Kompilierung, nicht erst die Runtime.
App Intents ersetzen SiriKit für alle neuen Apps; SiriKit bleibt nur für Domains ohne App-Intent-Äquivalent (z. B. VoIP).
Was sind App Intents in iOS 26?
App Intents sind Swift-Typen, die eine einzelne, benannte Aktion deiner App beschreiben – etwa „Notiz hinzufügen“, „Timer starten“ oder „Rechnung als bezahlt markieren“. Das System indiziert diese Aktionen und stellt sie überall dort zur Verfügung, wo Nutzer:innen mit dem Betriebssystem und nicht mit deiner App interagieren: Siri, Shortcuts, Spotlight-Suche, Widgets, Live-Activities, Control Center (iOS 18+), Focus-Filter, Apple-Watch-Komplikationen und – neu in iOS 26 – Apple Intelligence.
Der Unterschied zu SiriKit ist grundlegend: SiriKit hatte einen festen Katalog von Domains (Messaging, Payments, Ride Booking …) und du hast dich innerhalb einer Domain in eine vorgegebene Intent-Klasse gehängt. App Intents kehren das um. Du definierst die Aktion, das System bindet sie an alle passenden Oberflächen. Ich habe letztes Jahr eine Health-App migriert, bei der wir mit SiriKit an einer Wand aus INIntent-Subklassen und .intentdefinition-Files gelandet sind – die App-Intents-Version derselben Funktionalität war ca. 40 % weniger Code und hat auf visionOS „einfach funktioniert“, ohne dass wir irgendwas plattformspezifisches geschrieben haben.
Was sich in iOS 26 gegenüber iOS 18 geändert hat: @AssistantIntent und @AssistantEntity als Toolkonformität für Apple Intelligence, Interaktive Snippets mit SwiftUI-Views als Ergebnisdarstellung, verbesserte Deferred-Property-Resolution und – für Mac-Entwickler:innen wichtig – ForegroundContinuableIntent als offizielle API, um im Menu-Bar-Kontext deine App nach vorne zu holen.
Dein erster App Intent: Von Null zu Siri
Ein minimaler App Intent besteht aus einem struct, drei statischen Properties und einer perform()-Methode. Kein .intentdefinition, kein Extension-Target, keine Info.plist-Einträge – zumindest nicht für den Basisfall.
import AppIntents
struct StartFocusSessionIntent: AppIntent {
static let title: LocalizedStringResource = "Fokus-Session starten"
static let description = IntentDescription(
"Startet eine Fokus-Session mit der angegebenen Dauer.",
categoryName: "Produktivität"
)
// openAppWhenRun steuert, ob die App in den Vordergrund kommt.
// Für Hintergrund-Aktionen (typischer Fall) auf false lassen.
static let openAppWhenRun: Bool = false
@Parameter(
title: "Dauer",
description: "Länge der Session in Minuten.",
default: 25,
controlStyle: .field,
inclusiveRange: (5, 180)
)
var minutes: Int
func perform() async throws -> some IntentResult & ProvidesDialog {
try await FocusSessionManager.shared.start(minutes: minutes)
let dialog = IntentDialog("Fokus-Session für \(minutes) Minuten gestartet.")
return .result(dialog: dialog)
}
}
Drei Dinge, auf die ich hier achte: Erstens ist title als LocalizedStringResource typisiert – das ist wichtig, weil der App-Intents-Metadata-Extractor beim Build genau diese String-Catalogue-Keys extrahiert. Zweitens, das IntentResult in Swift 6.2 ist ein Opaque-Return-Type mit Protocol-Composition (ProvidesDialog, ReturnsValue, OpensIntent, ShowsSnippetView) – kombiniere sie nach Bedarf. Drittens: perform() ist async throws und läuft auf einem Detached Task des Systems, nicht auf dem MainActor. Wenn du UI berührst, musst du selbst hüpfen.
Sobald diese Datei im App-Target liegt und du einmal baust, ist der Intent in der Shortcuts-App unter deinem App-Namen als Aktion sichtbar. Für Siri brauchst du noch einen AppShortcut (siehe unten). Und falls du dich fragst, wie du Aktionen in deinen SwiftUI-Views triggerst: der Modifier .appIntent(...) auf Buttons und Toggles funktioniert direkt in Widgets und Live-Activities.
AppEntity: Deine Datenobjekte für Siri und Shortcuts
Sobald ein Intent nicht mit primitiven Typen (Int, String, Date, URL) auskommt, brauchst du AppEntity. Eine AppEntity ist die App-Intents-Repräsentation eines Modell-Objekts – ein „Projekt“, ein „Kontakt“, eine „Notiz“. Sie muss identifizierbar sein, einen menschenlesbaren Namen haben und über eine EntityQuery auffindbar sein.
import AppIntents
struct ProjectEntity: AppEntity {
let id: UUID
let name: String
let dueDate: Date?
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation(name: "Projekt")
}
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(name)",
subtitle: dueDate.map { "Fällig \($0.formatted(date: .abbreviated, time: .omitted))" }
)
}
static var defaultQuery = ProjectQuery()
}
struct ProjectQuery: EntityQuery {
// Direkte Suche per ID (z. B. wenn Shortcuts einen gespeicherten Wert wiederholt).
func entities(for identifiers: [UUID]) async throws -> [ProjectEntity] {
try await ProjectStore.shared.projects(withIDs: identifiers)
}
// Suggested-Werte für den Parameter-Picker in Shortcuts.
func suggestedEntities() async throws -> [ProjectEntity] {
try await ProjectStore.shared.recentProjects(limit: 8)
}
}
extension ProjectQuery: EntityStringQuery {
// Freitext-Suche für Siri: "Öffne Projekt Website-Relaunch"
func entities(matching string: String) async throws -> [ProjectEntity] {
try await ProjectStore.shared.search(text: string)
}
}
Was in der Praxis oft vergessen wird: EntityStringQuery ist optional, aber ohne sie kann Siri deine Entities nicht per Sprache identifizieren. Wenn Nutzer:innen „Hey Siri, öffne Projekt X“ sagen, fällt dein Intent ohne EntityStringQuery auf die Disambiguation-UI zurück und fragt zurück. Für App-Intelligence-Kontexte ist dasselbe Protokoll die Grundlage der semantischen Suche.
SwiftData-Modelle können übrigens direkt AppEntity konformieren – seit iOS 18 gibt es mit @Model + @AppEntity Interop, wobei du die defaultQuery weiterhin von Hand baust. Das reduziert die Boilerplate signifikant. Falls du gerade erst SwiftData einführst, lohnt sich vorher ein Blick auf SwiftData-Schema-Migrationen in iOS 26, weil AppEntities durch Schema-Änderungen ebenso brechen wie Views.
AppShortcutsProvider: Siri-Phrasen und Spotlight
Damit ein Intent per „Hey Siri“ ohne vorherige Einrichtung durch die Nutzer:innen aufrufbar ist, brauchst du einen AppShortcutsProvider. Der lebt einmalig pro App und listet bis zu zehn System-Level-Shortcuts – eine harte Grenze, an die ich schon mehrfach gestoßen bin.
Zwei Regeln, die im Store-Review immer wieder zu Ablehnungen führen: Erstens muss jede Phrase entweder \(.applicationName) oder einen Parameter enthalten – sonst wird der Shortcut nicht registriert und Siri antwortet mit „Ich kenne diesen Kurzbefehl nicht“. Zweitens dürfen die Phrasen keine App-fremden Markennamen enthalten. Ich habe eine App gesehen, die „ChatGPT starten“ als Phrase eingetragen hatte – App-Store-Ablehnung binnen 12 Stunden.
Apple Intelligence und Interaktive Snippets in iOS 26
iOS 26 hat die App-Intents-API in zwei Richtungen erweitert: Assistant Schemas für Apple Intelligence und Interactive Snippets als reichere Ergebnisdarstellung. Assistant Schemas sind vordefinierte Signaturen für häufige Aktionen (Musik abspielen, Nachricht senden, Foto suchen), die Apple Intelligence versteht, ohne deine App als Kontext geladen zu haben. Du markierst deinen Intent mit @AssistantIntent(schema: .system.playMedia) und implementierst die vorgegebenen Properties.
import AppIntents
@AssistantIntent(schema: .system.search)
struct SearchProjectsIntent: AppIntent {
static let title: LocalizedStringResource = "Projekte durchsuchen"
@Parameter(title: "Suchbegriff")
var criteria: StringSearchCriteria
func perform() async throws -> some IntentResult & ShowsSnippetView {
let results = try await ProjectStore.shared.search(text: criteria.term)
return .result(view: ProjectSearchSnippet(projects: results))
}
}
// Snippet-View ist eine normale SwiftUI-View mit interaktiven Controls.
struct ProjectSearchSnippet: View {
let projects: [ProjectEntity]
var body: some View {
VStack(alignment: .leading, spacing: 12) {
ForEach(projects.prefix(5)) { project in
HStack {
Text(project.name).font(.headline)
Spacer()
Button(intent: OpenProjectIntent(project: project)) {
Image(systemName: "arrow.right.circle.fill")
}
}
}
}
.padding()
}
}
Die Interaktion ist der Punkt: Ein Snippet ist eine SwiftUI-View, die Apple Intelligence oder Siri als Antwort rendert – mit aktiven Controls. Nutzer:innen können direkt aus dem Ergebnis heraus einen weiteren Intent triggern, ohne deine App zu öffnen. Für den offiziellen Katalog der System-Schemas siehe die Apple Developer Dokumentation zu App Intents. Wenn dich die zugrundeliegenden On-Device-Modelle interessieren, habe ich das im Foundation Models Framework Guide ausführlich behandelt.
Wie unterscheiden sich App Intents auf iPad, Mac Catalyst und visionOS?
Der Punkt, an dem cross-plattform-Entwickler:innen die meiste Zeit verlieren, sind die subtilen Verhaltensunterschiede zwischen den Aufruf-Kontexten. Hier eine Übersicht, die ich mir aus schmerzhafter Erfahrung zusammengeschrieben habe:
Aspekt
iPhone / iPad
Mac Catalyst
visionOS
Ausführungskontext
Background-Task, ~10 s Limit
Foreground-fähig via ForegroundContinuableIntent
Immer Background, Ornament-Dialog
Widget-Interaktivität
iOS 17+ voll unterstützt
Widgets nicht in Mac Menu Bar
Widgets als 3D-Objekte, iOS-Verhalten
Snippet-Rendering
Bottom Sheet oder Siri UI
Popover in Menu Bar
Volumetrisches Ornament
Systembilder
SF Symbols, animierbar
SF Symbols, statisch
SF Symbols mit Tiefe
Focus-Filter
Voll unterstützt
Ab macOS 15 unterstützt
Nicht unterstützt
Control Widgets
iOS 18+ Control Center
Menu-Bar-Widgets ab macOS 15
Nicht unterstützt
Die häufigste Falle ist der Ausführungskontext auf Mac Catalyst. Wenn eine Nutzerin einen Intent aus dem macOS-Shortcuts-Fenster startet, während deine App gar nicht läuft, erwartet sie meist, dass etwas sichtbar passiert. Ein still-im-Hintergrund-ausgeführter Intent fühlt sich auf dem Mac kaputt an. Die Lösung ist ForegroundContinuableIntent:
struct EditProjectIntent: AppIntent, ForegroundContinuableIntent {
static let title: LocalizedStringResource = "Projekt bearbeiten"
@Parameter(title: "Projekt")
var project: ProjectEntity
func perform() async throws -> some IntentResult {
// Auf iOS: läuft im Hintergrund.
// Auf Mac / iPad: fordert Foreground an und öffnet den Editor.
try await requestToContinueInForeground()
await MainActor.run {
ProjectRouter.shared.openEditor(for: project)
}
return .result()
}
}
Auf visionOS ist die Situation umgekehrt: Egal was du willst, der Intent läuft im Hintergrund, und Dialoge werden als Ornament über der aktuellen Volumetric Scene angezeigt. Wenn du eine App-Öffnung brauchst, musst du OpensIntent als Ergebnis zurückgeben und der Nutzer bestätigt den Kontextwechsel via Look-and-Tap. Ähnliche Cross-Platform-Nuancen kennst du vielleicht schon von meinem Artikel zum Observable-Makro in UIKit – dort ist es das updateProperties()-Timing, das sich zwischen iPhone und Mac Catalyst unterscheidet.
App Intents vs SiriKit: Was solltest du 2026 verwenden?
Für neue Apps: App Intents, ohne Ausnahme. Apple hat auf der WWDC 2024 klargestellt, dass SiriKit nur noch für Domains gepflegt wird, für die es kein App-Intents-Äquivalent gibt – konkret VoIP-Calling, CarPlay-Messaging-Sessions und einige Fitness-Workouts. Alles andere (Notizen, Reminders, Lists, Media, Payments, Ride Booking, Search, Money Transfer) ist auf App Intents umgestellt oder migrierbar.
Wenn du eine bestehende SiriKit-App migrierst, ist mein pragmatischer Rat: nicht in einem Big-Bang. Nimm einen Intent nach dem anderen. Die Koexistenz funktioniert – INIntent-Extensions und App-Intents-Structs können parallel existieren, und Siri routet auf den passenden Handler. Das gibt dir Zeit, die App-Entity-Modellierung sauber zu machen. Für die Migration selbst hilft das WWDC-Video „Meet App Intents“ als konzeptioneller Einstieg (auch wenn es aus 2022 ist, sind die Grundlagen unverändert). Für Testing-Muster empfehle ich meinen Guide zu Swift Testing vs XCTest, weil App-Intent-Tests sich besonders gut mit dem neuen Framework schreiben lassen.
Debugging und typische Fallstricke
App Intents sind an drei Stellen fragil, an denen ich regelmäßig Zeit verliere und die dir vielleicht Stunden sparen:
1. Der Metadata-Extractor bricht den Build
Der App-Intents-SSU (Metadata-Extractor) läuft als Build-Phase und schreibt die Intent-Metadaten in ein Metadata.appintents-Bundle. Wenn dort etwas nicht passt – ein @Parameter ohne title, eine AppEntity ohne typeDisplayRepresentation, ein LocalizedStringResource-Key, der nicht im String Catalog existiert – bricht die Build-Phase. Die Fehlermeldung ist meist kryptisch („Cannot form key path“). Der Trick: die Build-Setting APP_INTENTS_ENABLE_METADATA_EXTRACTOR_DIAGNOSTICS = YES setzen, dann bekommst du menschenlesbare Fehler.
2. Siri lernt neue Phrasen nur nach App-Neuinstallation
Wenn du eine Phrase im AppShortcutsProvider änderst und die App via Xcode-Run neu installierst, ignoriert Siri die Änderung oft für 5–10 Minuten. Der zuverlässige Weg ist: App vom Gerät löschen, neu installieren, dann Shortcuts-App öffnen (ja, wirklich – das triggert die Neuindizierung). Auf Testflight passiert das automatisch beim ersten Launch.
3. AppEntity-Identifier müssen stabil sein
Shortcuts speichert AppEntity-Referenzen persistent per id. Wenn du deine IDs bei jedem Launch neu vergibst (z. B. In-Memory-UUIDs), verlieren gespeicherte Shortcuts ihre Referenz und schlagen mit „Item not found“ fehl. IDs müssen aus einem persistenten Store kommen – SwiftData-Modelle machen das automatisch, In-Memory-Modelle brauchst du selbst.
Für tiefergehende asynchrone Muster in perform()-Implementierungen ist mein Artikel zu Approachable Concurrency in Swift 6.2 relevant – App Intents sind einer der Kontexte, in denen die neuen @MainActor-Isolation-Defaults sich am deutlichsten bemerkbar machen.
Häufig gestellte Fragen
Was ist der Unterschied zwischen AppIntent und AppShortcut?
AppIntent ist die Aktion selbst – die Logik, die ausgeführt wird. AppShortcut ist die Bindung dieser Aktion an Siri-Sprachphrasen und Spotlight, definiert in einem AppShortcutsProvider. Ein Intent kann ohne Shortcut existieren (er ist dann nur in der Shortcuts-App verfügbar), aber ein Shortcut braucht immer einen Intent.
Kann ich App Intents ohne Siri nutzen?
Ja. Ein App Intent, der keinen AppShortcut hat, erscheint trotzdem in der Shortcuts-App, in Spotlight-Ergebnissen und ist als Widget-, Live-Activity- oder Control-Widget-Aktion verwendbar. Die Siri-Integration ist optional und wird über den AppShortcutsProvider aktiviert.
Warum funktioniert mein App Intent im Widget nicht?
Häufigste Ursache: openAppWhenRun = true. Widgets dürfen die App nicht öffnen – der Intent wird dann vom System stillschweigend verworfen. Setze die Property explizit auf false und stelle sicher, dass perform() innerhalb der Widget-Ausführungsbudget-Grenze (~30 Sekunden) fertig ist.
Wie teste ich App Intents in Unit-Tests?
Instanziere den Intent direkt und rufe await intent.perform() auf. Für Parameter setzt du sie über die synthetischen Properties. Swift Testing mit @Test-Funktionen eignet sich besonders gut, weil du async nativ nutzen kannst. Für Entity-Queries mockst du den Backing-Store und injizierst ihn via Dependency-Injection.
Sind App Intents auf visionOS voll unterstützt?
Ja, seit visionOS 1.0. Die Ausführungssemantik ist immer Background, Dialoge werden als Ornament über der aktuellen Scene gerendert, und Snippets werden als volumetrische Views dargestellt. Focus-Filter und Control Widgets werden auf visionOS nicht unterstützt – alle anderen App-Intents-Kontexte funktionieren.
Swift Testing in Xcode 26 löst XCTest mit Macros, Parallelisierung und parametrisierten Tests ab. Vergleich, Migration und CI-Integration mit Code-Beispielen.
Swift 6.2 macht Concurrency endlich zugänglich. Lerne, wie @MainActor als Standard, @concurrent und nonisolated(nonsending) funktionieren — und migriere dein bestehendes Projekt Schritt für Schritt mit diesem Guide.