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.

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.
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.
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<TaskEntity> {
// 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.
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.
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.
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.
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.
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.
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.
Klaar om je iOS gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
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.
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.
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+.
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.
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 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-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
}
#endifDeze 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.
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.
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
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Tags
Delen
Gerelateerde artikelen

WidgetKit iOS 17+: Interactieve Widgets met App Intents
Volledige gids voor het maken van interactieve iOS-widgets met WidgetKit en App Intents. Knoppen, toggles, animaties en best practices voor iOS 17+ in 2026.

Combine vs async/await in Swift: Progressieve Migratiepatronen
Volledige gids voor migratie van Combine naar async/await in Swift: progressieve strategieën, bridging-patronen en paradigma-coëxistentie in iOS-codebases.

iOS-toegankelijkheidsvragen voor sollicitaties in 2026: VoiceOver en Dynamic Type
Bereid je voor op iOS-sollicitaties met essentiële toegankelijkheidsvragen: VoiceOver, Dynamic Type, semantische traits en audits.