# App Intents en Siri Shortcuts: geavanceerde iOS-automatisering 2026 > Volledige gids over App Intents en Siri Shortcuts voor iOS 18+. Eigen Siri-acties bouwen, Apple Intelligence integreren en de Swift-app automatiseren in 2026. - Published: 2026-03-08 - Updated: 2026-04-29 - Author: SharpSkill - Tags: app-intents, siri-shortcuts, ios, swift, apple-intelligence - Reading time: 15 min --- In 2026 betreden iOS-applicaties met Apple Intelligence en het App Intents-framework een tijdperk waarin gebruikersintentie belangrijker is dan de grafische interface. Apps zonder intents worden onzichtbaar in een AI-georiënteerd besturingssysteem. App Intents vormen het fundament dat Siri, Spotlight, widgets en de Action Button in staat stelt te interageren met de app-functionaliteit. > **Wat dit artikel behandelt** > > Dit artikel toont de volledige creatie van App Intents en Siri Shortcuts voor iOS 18+, van fundamentele concepten tot integratie met Apple Intelligence en App Intent Domains. ## Het App Intents-framework begrijpen Het App Intents-framework, geïntroduceerd met iOS 16, moderniseert de constructie van intents in Swift en vervangt het oudere SiriKit Intents-framework. Deze declaratieve architectuur maakt het mogelijk acties te creëren die door het systeem ontdekt worden: Spotlight, Opdrachten-app, Siri en Action Button. ```swift // TaskIntent.swift import AppIntents // Een AppIntent vertegenwoordigt een actie die de gebruiker kan uitvoeren struct CreateTaskIntent: AppIntent { // Titel weergegeven in Opdrachten en Siri static var title: LocalizedStringResource = "Taak aanmaken" // Beschrijving voor toegankelijkheid en suggesties static var description = IntentDescription( "Maakt een nieuwe taak aan in de app." ) // Parameter met automatische validatie @Parameter(title: "Taaktitel") var taskTitle: String // Optionele parameter met standaardwaarde @Parameter(title: "Prioriteit", default: .medium) var priority: TaskPriority // Uitvoering van de actie func perform() async throws -> some IntentResult & ReturnsValue { // Maak de taak via de service let task = TaskService.shared.createTask( title: taskTitle, priority: priority ) // Retourneer de aangemaakte entity voor chaining return .result(value: TaskEntity(task: task)) } } ``` De intent declareert zijn parameters via de `@Parameter` property wrapper, waardoor Siri ontbrekende waarden kan opvragen. De `perform()`-methode voert de bedrijfslogica uit en retourneert een getypeerd resultaat. ## App Entities definiëren voor data App Entities vertegenwoordigen de "zelfstandige naamwoorden" van de applicatie: de objecten waarop intents werken. Ze stellen Siri in staat de app-data te begrijpen en te manipuleren. ```swift // TaskEntity.swift import AppIntents // Intern datamodel struct Task: Identifiable, Codable { let id: UUID var title: String var priority: TaskPriority var isCompleted: Bool var dueDate: Date? } // Entity blootgesteld aan het systeem struct TaskEntity: AppEntity { // Vereiste unieke identifier var id: UUID // Weergeefbare eigenschappen var title: String var priority: TaskPriority var isCompleted: Bool // Weergaveconfiguratie in het systeem static var typeDisplayRepresentation: TypeDisplayRepresentation = "Taak" // Visuele representatie van de instantie var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: "\(title)", subtitle: "\(priority.rawValue)", image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle") ) } // Standaard query voor het zoeken van entities static var defaultQuery = TaskEntityQuery() // Initializer vanuit het interne model init(task: Task) { self.id = task.id self.title = task.title self.priority = task.priority self.isCompleted = task.isCompleted } } // Query om entities te zoeken en filteren struct TaskEntityQuery: EntityQuery { // Zoeken op identifiers func entities(for identifiers: [UUID]) async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { identifiers.contains($0.id) } .map { TaskEntity(task: $0) } } // Suggesties getoond in de interface func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { !$0.isCompleted } .prefix(5) .map { TaskEntity(task: $0) } } } ``` De `EntityQuery` definieert hoe het systeem entities zoekt en voorstelt. De methoden `entities(for:)` en `suggestedEntities()` voeden de Siri- en Opdrachten-interfaces. > **App Enums vs App Entities** > > Gebruik `AppEnum` voor types met een vaste verzameling waarden (prioriteit, status), en `AppEntity` voor dynamische, door gebruikers aangemaakte types (taken, notities, contacten). ## App Enums maken voor vaste waarden App Enums stellen opgesomde types beschikbaar aan het systeem, waardoor Siri contextuele keuzes kan aanbieden. ```swift // TaskPriority.swift import AppIntents // Enum blootgesteld aan het systeem enum TaskPriority: String, AppEnum, Codable { case low case medium case high // Weergegeven typenaam static var typeDisplayRepresentation: TypeDisplayRepresentation = "Prioriteit" // Representatie van elk geval static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [ .low: DisplayRepresentation( title: "Laag", image: .init(systemName: "arrow.down.circle") ), .medium: DisplayRepresentation( title: "Gemiddeld", image: .init(systemName: "minus.circle") ), .high: DisplayRepresentation( title: "Hoog", image: .init(systemName: "exclamationmark.circle") ) ] } // Enum voor de status van de taak enum TaskStatus: String, AppEnum, Codable { case pending case inProgress case completed static var typeDisplayRepresentation: TypeDisplayRepresentation = "Status" static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [ .pending: "In afwachting", .inProgress: "Bezig", .completed: "Voltooid" ] } ``` Visuele representaties (SF Symbols-iconen) verrijken de weergave in Opdrachten en Siri-suggesties. ## AppShortcutsProvider implementeren De `AppShortcutsProvider` stelt App Shortcuts beschikbaar aan het systeem, direct beschikbaar zonder gebruikersconfiguratie. Deze shortcuts verschijnen in Spotlight, Siri en de Action Button. ```swift // ShortcutsProvider.swift import AppIntents // Provider die alle app-shortcuts declareert struct TaskAppShortcutsProvider: AppShortcutsProvider { // Maximaal 10 shortcuts per applicatie @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // Shortcut om een taak aan te maken AppShortcut( intent: CreateTaskIntent(), phrases: [ // De .applicationName placeholder is VERPLICHT "Taak aanmaken met \(.applicationName)", "Nieuwe taak in \(.applicationName)", "Taak toevoegen aan \(.applicationName)" ], shortTitle: "Taak aanmaken", systemImageName: "plus.circle" ) // Shortcut om taken op te lijsten AppShortcut( intent: ListTasksIntent(), phrases: [ "Toon mijn taken in \(.applicationName)", "Geef taken weer van \(.applicationName)", "Wat zijn mijn taken \(.applicationName)" ], shortTitle: "Mijn taken", systemImageName: "list.bullet" ) // Shortcut met dynamische parameter AppShortcut( intent: CompleteTaskIntent(), phrases: [ "Voltooi \(\.$taskName) in \(.applicationName)", "Markeer \(\.$taskName) als gedaan met \(.applicationName)" ], shortTitle: "Taak voltooien", systemImageName: "checkmark.circle" ) } } ``` Spraakzinnen moeten de placeholder `\(.applicationName)` bevatten zodat Siri de doelapp identificeert. Dynamische parameters zoals `\(\.$taskName)` maken contextuele commando's mogelijk. > **Limiet van 10 Shortcuts** > > Een applicatie kan maximaal 10 App Shortcuts declareren. Het is verstandig de meest frequente en nuttige acties voor gebruikers te prioriteren. ## Intents met complexe parameters Intents kunnen complexe parameters accepteren, inclusief entities en configuratie-opties. ```swift // CompleteTaskIntent.swift import AppIntents struct CompleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Taak voltooien" static var description = IntentDescription( "Markeert een taak als voltooid." ) // Entity-parameter met automatisch zoeken @Parameter(title: "Taak") var task: TaskEntity // Optionele parameter met datum @Parameter(title: "Voltooiingsdatum") var completionDate: Date? // Configuratie van de Siri-dialoog static var parameterSummary: some ParameterSummary { Summary("Voltooi \(\.$task)") { \.$completionDate } } func perform() async throws -> some IntentResult & ProvidesDialog { // Werk de taak bij TaskService.shared.completeTask( id: task.id, completionDate: completionDate ?? Date() ) // Spraakfeedback voor Siri return .result( dialog: "Taak \(task.title) is gemarkeerd als voltooid." ) } } // Intent die een lijst van entities retourneert struct ListTasksIntent: AppIntent { static var title: LocalizedStringResource = "Taken oplijsten" // Optioneel filter @Parameter(title: "Status", default: nil) var statusFilter: TaskStatus? @Parameter(title: "Prioriteit", default: nil) var priorityFilter: TaskPriority? func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> { var tasks = TaskService.shared.fetchTasks() // Pas filters toe if let status = statusFilter { tasks = tasks.filter { switch status { case .completed: return $0.isCompleted case .pending, .inProgress: return !$0.isCompleted } } } if let priority = priorityFilter { tasks = tasks.filter { $0.priority == priority } } let entities = tasks.map { TaskEntity(task: $0) } return .result(value: entities) } } ``` `ParameterSummary` definieert hoe Siri de intent presenteert tijdens spraakuitvoering en biedt natuurlijke feedback. ## Apple Intelligence-integratie iOS 18 introduceert App Intent Domains, verzamelingen van API's ontworpen voor specifieke functionaliteiten. Deze domeinen stellen Apple Intelligence in staat acties met grotere precisie te begrijpen en uit te voeren. ```swift // BookmarkDomainIntents.swift import AppIntents // Conformiteit met het Bookmarks-domein voor Apple Intelligence-integratie struct SaveBookmarkIntent: AppIntent { static var title: LocalizedStringResource = "Bladwijzer opslaan" // URL-parameter met automatische validatie @Parameter(title: "URL") var url: URL @Parameter(title: "Titel", default: nil) var title: String? @Parameter(title: "Map", default: nil) var folder: BookmarkFolderEntity? // Open app indien nodig static var openAppWhenRun: Bool = false func perform() async throws -> some IntentResult & ProvidesDialog { let bookmark = BookmarkService.shared.save( url: url, title: title, folder: folder?.id ) return .result( dialog: "Bladwijzer opgeslagen: \(bookmark.title)" ) } } // Intent met bewustzijn van schermcontent (iOS 18.4+) struct AnalyzeScreenContentIntent: AppIntent { static var title: LocalizedStringResource = "Inhoud analyseren" // Toegang tot schermcontext via Apple Intelligence @Parameter(title: "Context") var screenContext: String? func perform() async throws -> some IntentResult & ProvidesDialog { guard let context = screenContext else { return .result(dialog: "Geen inhoud om te analyseren.") } // Verwerk inhoud geëxtraheerd door Apple Intelligence let analysis = ContentAnalyzer.analyze(context) return .result(dialog: analysis.summary) } } ``` Vooraf gedefinieerde domeinen (Books, Camera, Spreadsheets) stellen Siri in staat nauwkeurig te reageren op verzoeken dankzij modellen getraind op deze specifieke taken. ## Siri-acties met gebruikersbevestiging Voor gevoelige acties kan het systeem bevestiging vragen vóór de uitvoering. ```swift // DeleteTaskIntent.swift import AppIntents struct DeleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Taak verwijderen" @Parameter(title: "Taak") var task: TaskEntity // Vereist gebruikersbevestiging static var isDiscoverable: Bool = true func perform() async throws -> some IntentResult & ProvidesDialog { // Vraag bevestiging via dialoog try await requestConfirmation( result: .result( dialog: "Weet je zeker dat je \(task.title) wilt verwijderen?" ) ) // Verwijder na bevestiging TaskService.shared.deleteTask(id: task.id) return .result( dialog: "Taak \(task.title) is verwijderd." ) } } // Intent met meerdere dialoogstappen struct ScheduleTaskIntent: AppIntent { static var title: LocalizedStringResource = "Taak plannen" @Parameter(title: "Taak") var task: TaskEntity @Parameter(title: "Datum") var scheduledDate: Date @Parameter(title: "Herinnering", default: true) var setReminder: Bool static var parameterSummary: some ParameterSummary { When(\.$setReminder, .equalTo, true) { Summary("Plan \(\.$task) voor \(\.$scheduledDate) met herinnering") } otherwise: { Summary("Plan \(\.$task) voor \(\.$scheduledDate)") } } func perform() async throws -> some IntentResult & ProvidesDialog { TaskService.shared.schedule( taskId: task.id, date: scheduledDate, reminder: setReminder ) let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium dateFormatter.timeStyle = .short let formattedDate = dateFormatter.string(from: scheduledDate) return .result( dialog: "Taak gepland voor \(formattedDate)." ) } } ``` De `requestConfirmation`-methode pauzeert de uitvoering tot gebruikersvalidatie en beschermt tegen onbedoelde acties. ## Intents in interactieve widgets App Intents integreren naadloos met WidgetKit om interactieve widgets te maken op iOS 17+. ```swift // TaskWidgetIntents.swift import AppIntents import WidgetKit // Intent geoptimaliseerd voor widget (snelle uitvoering) struct ToggleTaskFromWidgetIntent: AppIntent { static var title: LocalizedStringResource = "Taak omschakelen" @Parameter(title: "Taak-ID") var taskID: String init() {} init(taskID: UUID) { self.taskID = taskID.uuidString } // Geen dialoog voor widgets func perform() async throws -> some IntentResult { guard let uuid = UUID(uuidString: taskID) else { return .result() } TaskService.shared.toggleCompletion(taskId: uuid) // Onmiddellijke widget-vernieuwing WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget") return .result() } } // Widget-view met interactieve knop import SwiftUI struct TaskWidgetView: View { let task: Task var body: some View { Button(intent: ToggleTaskFromWidgetIntent(taskID: task.id)) { HStack { Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle") .foregroundStyle(task.isCompleted ? .green : .secondary) Text(task.title) .strikethrough(task.isCompleted) } .padding() } .buttonStyle(.plain) } } ``` Widgets gebruiken de syntaxis `Button(intent:)` om de interactie direct te koppelen aan het App Intent, zonder de applicatie te openen. ## Action Button-configuratie De Action Button op iPhone 15 Pro en later kan App Shortcuts direct activeren. ```swift // ActionButtonIntent.swift import AppIntents // Intent geoptimaliseerd voor Action Button struct QuickCaptureIntent: AppIntent { static var title: LocalizedStringResource = "Snelle vastlegging" static var description = IntentDescription( "Maakt snel een taak met een titel." ) // Opent app voor invoer static var openAppWhenRun: Bool = true func perform() async throws -> some IntentResult & OpensIntent { // Notificatie om snelle vastleggingsscherm te openen NotificationCenter.default.post( name: .quickCaptureTriggered, object: nil ) return .result(opensIntent: ShowQuickCaptureViewIntent()) } } // Declareren in AppShortcutsProvider voor Action Button extension TaskAppShortcutsProvider { @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // ... andere shortcuts AppShortcut( intent: QuickCaptureIntent(), phrases: [ "Snelle vastlegging \(.applicationName)", "Snelle notitie \(.applicationName)" ], shortTitle: "Vastleggen", systemImageName: "bolt.circle" ) } } ``` Gebruikers kunnen de Action Button configureren om deze shortcut te activeren via Instellingen > Action Button. > **iOS 18+ Macros** > > iOS 18 introduceert de macros `@DeferredProperty` en `@ComputedProperty` om boilerplate te verminderen. App Intents kunnen ook in Swift Packages staan voor cross-platform hergebruik. ## Intents in Swift Packages App Intents kunnen worden gedefinieerd in Swift Packages voor delen tussen iOS, macOS en watchOS. ```swift // Package.swift // swift-tools-version: 5.9 import PackageDescription let package = Package( name: "TaskIntents", platforms: [ .iOS(.v17), .macOS(.v14), .watchOS(.v10) ], products: [ .library(name: "TaskIntents", targets: ["TaskIntents"]) ], dependencies: [], targets: [ .target( name: "TaskIntents", dependencies: [] ) ] ) // Sources/TaskIntents/SharedIntents.swift import AppIntents // Cross-platform gedeelde intent public struct SharedCreateTaskIntent: AppIntent { public static var title: LocalizedStringResource = "Taak aanmaken" @Parameter(title: "Titel") public var taskTitle: String public init() {} public func perform() async throws -> some IntentResult { // Gedeelde logica await TaskRepository.shared.create(title: taskTitle) return .result() } } // Platform-specifieke uitbreiding in app #if os(iOS) extension SharedCreateTaskIntent { // iOS-specifiek gedrag static var openAppWhenRun: Bool = false } #endif ``` Deze architectuur behoudt één codebase voor de intents en past het gedrag per platform aan. ## App Intents testen App Intents kunnen unit-getest worden zoals elke Swift-code. ```swift // TaskIntentTests.swift import XCTest import AppIntents @testable import TaskApp final class TaskIntentTests: XCTestCase { override func setUp() { super.setUp() // Reset service voor geïsoleerde tests TaskService.shared.reset() } func testCreateTaskIntent() async throws { // Given var intent = CreateTaskIntent() intent.taskTitle = "Test taak" intent.priority = .high // When let result = try await intent.perform() // Then let tasks = TaskService.shared.fetchTasks() XCTAssertEqual(tasks.count, 1) XCTAssertEqual(tasks.first?.title, "Test taak") XCTAssertEqual(tasks.first?.priority, .high) } func testCompleteTaskIntent() async throws { // Given let task = TaskService.shared.createTask( title: "Taak te voltooien", priority: .medium ) var intent = CompleteTaskIntent() intent.task = TaskEntity(task: task) // When _ = try await intent.perform() // Then let updatedTask = TaskService.shared.fetchTask(id: task.id) XCTAssertTrue(updatedTask?.isCompleted ?? false) } func testEntityQuery() async throws { // Given let task1 = TaskService.shared.createTask(title: "Taak 1", priority: .low) let task2 = TaskService.shared.createTask(title: "Taak 2", priority: .high) let query = TaskEntityQuery() // When let entities = try await query.entities(for: [task1.id, task2.id]) // Then XCTAssertEqual(entities.count, 2) } } ``` Tests verifiëren het gedrag van intents onafhankelijk van de systeeminterface. ## Best practices en optimalisaties Verschillende patronen zorgen voor performante App Intents en een optimale gebruikerservaring. ```swift // BestPractices.swift import AppIntents // 1. Gelokaliseerde zinnen struct LocalizedTaskIntent: AppIntent { static var title: LocalizedStringResource = "task.create.title" static var description = IntentDescription( "task.create.description", categoryName: "task.category" ) @Parameter(title: "task.parameter.title") var taskTitle: String func perform() async throws -> some IntentResult { // ... return .result() } } // 2. Correcte foutafhandeling enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible { case taskNotFound case invalidInput case serviceUnavailable var localizedStringResource: LocalizedStringResource { switch self { case .taskNotFound: return "Taak niet gevonden." case .invalidInput: return "Ongeldige gegevens." case .serviceUnavailable: return "Dienst tijdelijk niet beschikbaar." } } } struct RobustTaskIntent: AppIntent { static var title: LocalizedStringResource = "Robuuste intent" @Parameter(title: "ID") var taskId: String func perform() async throws -> some IntentResult & ProvidesDialog { guard let uuid = UUID(uuidString: taskId) else { throw TaskIntentError.invalidInput } guard let task = TaskService.shared.fetchTask(id: uuid) else { throw TaskIntentError.taskNotFound } return .result(dialog: "Taak gevonden: \(task.title)") } } // 3. EntityQuery-optimalisatie struct OptimizedTaskQuery: EntityStringQuery { // Geoptimaliseerde tekstuele zoekopdracht func entities(matching string: String) async throws -> [TaskEntity] { // Service-side zoeken met limiet TaskService.shared.search(query: string, limit: 10) .map { TaskEntity(task: $0) } } // Beperkte suggesties voor performance func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchRecentTasks(limit: 5) .map { TaskEntity(task: $0) } } } // 4. Focus Filter voor Focus-modi struct TaskFocusFilter: SetFocusFilterIntent { static var title: LocalizedStringResource = "Taken filteren" @Parameter(title: "Toon alleen hoge prioriteit") var showHighPriorityOnly: Bool func perform() async throws -> some IntentResult { TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly) return .result() } } ``` Deze patronen zorgen voor een vlotte systeemintegratie met behoud van optimale performance. ## Conclusie App Intents en Siri Shortcuts transformeren de manier waarop gebruikers met iOS-applicaties interageren. In 2026, met Apple Intelligence, is het blootstellen van intents niet langer optioneel maar essentieel om een moderne en intuïtieve ervaring te leveren. ### Checklist App Intents iOS 18+ - ✅ AppIntents aanmaken voor de hoofdacties van de app - ✅ AppEntities definiëren voor manipuleerbare data - ✅ AppEnum gebruiken voor opgesomde types - ✅ AppShortcutsProvider implementeren met spraakzinnen - ✅ Het maximum van 10 App Shortcuts respecteren - ✅ `\(.applicationName)` opnemen in alle zinnen - ✅ ParameterSummary configureren voor Siri-feedback - ✅ Apple Intelligence-domeinen integreren indien van toepassing - ✅ Intents testen met unit-tests - ✅ Titels en beschrijvingen lokaliseren --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/ios/app-intents-siri-shortcuts-advanced-ios-automation