# App Intents i Siri Shortcuts: zaawansowana automatyzacja iOS 2026 > Kompletny przewodnik po App Intents i Siri Shortcuts dla iOS 18+. Tworzenie własnych akcji Siri, integracja Apple Intelligence i automatyzacja aplikacji Swift w 2026. - Published: 2026-03-08 - Updated: 2026-04-29 - Author: SharpSkill - Tags: app-intents, siri-shortcuts, ios, swift, apple-intelligence - Reading time: 15 min --- W 2026 roku, dzięki Apple Intelligence i frameworkowi App Intents, aplikacje iOS wkraczają w erę, w której intencja użytkownika jest ważniejsza niż interfejs graficzny. Aplikacje, które nie udostępniają intentów, stają się niewidoczne w systemie zorientowanym na AI. App Intents stanowi fundament umożliwiający Siri, Spotlight, widgetom i Action Button interakcję z funkcjonalnościami aplikacji. > **Co obejmuje ten artykuł** > > Ten artykuł przedstawia kompletne tworzenie App Intents i Siri Shortcuts dla iOS 18+, od fundamentalnych koncepcji po integrację z Apple Intelligence i App Intent Domains. ## Zrozumienie frameworku App Intents Framework App Intents, wprowadzony wraz z iOS 16, modernizuje konstrukcję intentów w Swift, zastępując starszy framework SiriKit Intents. Ta deklaratywna architektura pozwala tworzyć akcje wykrywalne przez system: Spotlight, aplikacja Skróty, Siri i Action Button. ```swift // TaskIntent.swift import AppIntents // AppIntent reprezentuje akcję, którą użytkownik może wykonać struct CreateTaskIntent: AppIntent { // Tytuł wyświetlany w Skrótach i Siri static var title: LocalizedStringResource = "Utwórz zadanie" // Opis dla dostępności i sugestii static var description = IntentDescription( "Tworzy nowe zadanie w aplikacji." ) // Parametr z automatyczną walidacją @Parameter(title: "Tytuł zadania") var taskTitle: String // Parametr opcjonalny z wartością domyślną @Parameter(title: "Priorytet", default: .medium) var priority: TaskPriority // Wykonanie akcji func perform() async throws -> some IntentResult & ReturnsValue { // Utworzenie zadania przez serwis let task = TaskService.shared.createTask( title: taskTitle, priority: priority ) // Zwraca utworzoną encję do łańcuchowania return .result(value: TaskEntity(task: task)) } } ``` Intent deklaruje swoje parametry przez property wrapper `@Parameter`, pozwalając Siri pytać o brakujące wartości. Metoda `perform()` wykonuje logikę biznesową i zwraca wynik o określonym typie. ## Definiowanie App Entities dla danych App Entities reprezentują "rzeczowniki" aplikacji: obiekty, na których operują intenty. Pozwalają Siri rozumieć i manipulować danymi aplikacji. ```swift // TaskEntity.swift import AppIntents // Wewnętrzny model danych struct Task: Identifiable, Codable { let id: UUID var title: String var priority: TaskPriority var isCompleted: Bool var dueDate: Date? } // Encja wystawiona systemowi struct TaskEntity: AppEntity { // Wymagany unikalny identyfikator var id: UUID // Wyświetlane właściwości var title: String var priority: TaskPriority var isCompleted: Bool // Konfiguracja wyświetlania w systemie static var typeDisplayRepresentation: TypeDisplayRepresentation = "Zadanie" // Wizualna reprezentacja instancji var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: "\(title)", subtitle: "\(priority.rawValue)", image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle") ) } // Domyślne zapytanie do wyszukiwania encji static var defaultQuery = TaskEntityQuery() // Initializer z modelu wewnętrznego init(task: Task) { self.id = task.id self.title = task.title self.priority = task.priority self.isCompleted = task.isCompleted } } // Zapytanie do wyszukiwania i filtrowania encji struct TaskEntityQuery: EntityQuery { // Wyszukiwanie po identyfikatorach func entities(for identifiers: [UUID]) async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { identifiers.contains($0.id) } .map { TaskEntity(task: $0) } } // Sugestie wyświetlane w interfejsie func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { !$0.isCompleted } .prefix(5) .map { TaskEntity(task: $0) } } } ``` `EntityQuery` definiuje sposób, w jaki system wyszukuje i sugeruje encje. Metody `entities(for:)` i `suggestedEntities()` zasilają interfejsy Siri i Skrótów. > **App Enums vs App Entities** > > Należy używać `AppEnum` dla typów ze stałym zbiorem wartości (priorytet, status), a `AppEntity` dla typów dynamicznych tworzonych przez użytkownika (zadania, notatki, kontakty). ## Tworzenie App Enums dla stałych wartości App Enums udostępniają systemowi typy wyliczeniowe, pozwalając Siri oferować kontekstowe wybory. ```swift // TaskPriority.swift import AppIntents // Enum udostępniony systemowi enum TaskPriority: String, AppEnum, Codable { case low case medium case high // Wyświetlana nazwa typu static var typeDisplayRepresentation: TypeDisplayRepresentation = "Priorytet" // Reprezentacja każdego przypadku static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [ .low: DisplayRepresentation( title: "Niski", image: .init(systemName: "arrow.down.circle") ), .medium: DisplayRepresentation( title: "Średni", image: .init(systemName: "minus.circle") ), .high: DisplayRepresentation( title: "Wysoki", image: .init(systemName: "exclamationmark.circle") ) ] } // Enum dla statusu zadania enum TaskStatus: String, AppEnum, Codable { case pending case inProgress case completed static var typeDisplayRepresentation: TypeDisplayRepresentation = "Status" static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [ .pending: "Oczekujące", .inProgress: "W toku", .completed: "Ukończone" ] } ``` Reprezentacje wizualne (ikony SF Symbols) wzbogacają wyświetlanie w Skrótach i sugestiach Siri. ## Implementacja AppShortcutsProvider `AppShortcutsProvider` udostępnia App Shortcuts systemowi, czyniąc je natychmiast dostępnymi bez konfiguracji użytkownika. Te skróty pojawiają się w Spotlight, Siri i Action Button. ```swift // ShortcutsProvider.swift import AppIntents // Provider deklarujący wszystkie skróty aplikacji struct TaskAppShortcutsProvider: AppShortcutsProvider { // Maksymalnie 10 skrótów na aplikację @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // Skrót do tworzenia zadania AppShortcut( intent: CreateTaskIntent(), phrases: [ // Placeholder .applicationName jest WYMAGANY "Utwórz zadanie w \(.applicationName)", "Nowe zadanie w \(.applicationName)", "Dodaj zadanie do \(.applicationName)" ], shortTitle: "Utwórz zadanie", systemImageName: "plus.circle" ) // Skrót do listowania zadań AppShortcut( intent: ListTasksIntent(), phrases: [ "Pokaż moje zadania w \(.applicationName)", "Wyświetl zadania z \(.applicationName)", "Jakie są moje zadania \(.applicationName)" ], shortTitle: "Moje zadania", systemImageName: "list.bullet" ) // Skrót z dynamicznym parametrem AppShortcut( intent: CompleteTaskIntent(), phrases: [ "Ukończ \(\.$taskName) w \(.applicationName)", "Oznacz \(\.$taskName) jako wykonane w \(.applicationName)" ], shortTitle: "Ukończ zadanie", systemImageName: "checkmark.circle" ) } } ``` Frazy głosowe muszą zawierać placeholder `\(.applicationName)`, aby Siri zidentyfikowała aplikację docelową. Dynamiczne parametry takie jak `\(\.$taskName)` umożliwiają komendy kontekstowe. > **Limit 10 skrótów** > > Aplikacja może zadeklarować maksymalnie 10 App Shortcuts. Warto priorytetyzować akcje najczęstsze i najbardziej użyteczne dla użytkowników. ## Intenty z parametrami złożonymi Intenty mogą przyjmować złożone parametry, w tym encje i opcje konfiguracyjne. ```swift // CompleteTaskIntent.swift import AppIntents struct CompleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Ukończ zadanie" static var description = IntentDescription( "Oznacza zadanie jako ukończone." ) // Parametr typu encja z automatycznym wyszukiwaniem @Parameter(title: "Zadanie") var task: TaskEntity // Parametr opcjonalny z datą @Parameter(title: "Data ukończenia") var completionDate: Date? // Konfiguracja dialogu Siri static var parameterSummary: some ParameterSummary { Summary("Ukończ \(\.$task)") { \.$completionDate } } func perform() async throws -> some IntentResult & ProvidesDialog { // Aktualizacja zadania TaskService.shared.completeTask( id: task.id, completionDate: completionDate ?? Date() ) // Feedback głosowy dla Siri return .result( dialog: "Zadanie \(task.title) zostało oznaczone jako ukończone." ) } } // Intent zwracający listę encji struct ListTasksIntent: AppIntent { static var title: LocalizedStringResource = "Wylistuj zadania" // Filtr opcjonalny @Parameter(title: "Status", default: nil) var statusFilter: TaskStatus? @Parameter(title: "Priorytet", default: nil) var priorityFilter: TaskPriority? func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> { var tasks = TaskService.shared.fetchTasks() // Zastosuj filtry 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` definiuje sposób, w jaki Siri prezentuje intent podczas wykonywania głosowego, dostarczając naturalnego feedbacku. ## Integracja z Apple Intelligence iOS 18 wprowadza App Intent Domains, kolekcje API zaprojektowane dla konkretnych funkcjonalności. Te domeny pozwalają Apple Intelligence rozumieć i wykonywać akcje z większą precyzją. ```swift // BookmarkDomainIntents.swift import AppIntents // Zgodność z domeną Bookmarks dla integracji Apple Intelligence struct SaveBookmarkIntent: AppIntent { static var title: LocalizedStringResource = "Zapisz zakładkę" // Parametr URL z automatyczną walidacją @Parameter(title: "URL") var url: URL @Parameter(title: "Tytuł", default: nil) var title: String? @Parameter(title: "Folder", default: nil) var folder: BookmarkFolderEntity? // Otwórz aplikację w razie potrzeby 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: "Zakładka zapisana: \(bookmark.title)" ) } } // Intent ze świadomością zawartości ekranu (iOS 18.4+) struct AnalyzeScreenContentIntent: AppIntent { static var title: LocalizedStringResource = "Analizuj zawartość" // Dostęp do kontekstu ekranu przez Apple Intelligence @Parameter(title: "Kontekst") var screenContext: String? func perform() async throws -> some IntentResult & ProvidesDialog { guard let context = screenContext else { return .result(dialog: "Brak zawartości do analizy.") } // Przetwarzaj zawartość wyekstrahowaną przez Apple Intelligence let analysis = ContentAnalyzer.analyze(context) return .result(dialog: analysis.summary) } } ``` Predefiniowane domeny (Books, Camera, Spreadsheets) pozwalają Siri precyzyjnie odpowiadać na żądania dzięki modelom trenowanym na tych konkretnych zadaniach. ## Akcje Siri z potwierdzeniem użytkownika Dla wrażliwych akcji system może żądać potwierdzenia przed wykonaniem. ```swift // DeleteTaskIntent.swift import AppIntents struct DeleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Usuń zadanie" @Parameter(title: "Zadanie") var task: TaskEntity // Wymaga potwierdzenia użytkownika static var isDiscoverable: Bool = true func perform() async throws -> some IntentResult & ProvidesDialog { // Żądanie potwierdzenia przez dialog try await requestConfirmation( result: .result( dialog: "Czy na pewno chcesz usunąć \(task.title)?" ) ) // Usuń po potwierdzeniu TaskService.shared.deleteTask(id: task.id) return .result( dialog: "Zadanie \(task.title) zostało usunięte." ) } } // Intent z wieloma krokami dialogu struct ScheduleTaskIntent: AppIntent { static var title: LocalizedStringResource = "Zaplanuj zadanie" @Parameter(title: "Zadanie") var task: TaskEntity @Parameter(title: "Data") var scheduledDate: Date @Parameter(title: "Przypomnienie", default: true) var setReminder: Bool static var parameterSummary: some ParameterSummary { When(\.$setReminder, .equalTo, true) { Summary("Zaplanuj \(\.$task) na \(\.$scheduledDate) z przypomnieniem") } otherwise: { Summary("Zaplanuj \(\.$task) na \(\.$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: "Zadanie zaplanowane na \(formattedDate)." ) } } ``` Metoda `requestConfirmation` wstrzymuje wykonanie do walidacji użytkownika, chroniąc przed przypadkowymi akcjami. ## Intenty w widgetach interaktywnych App Intents naturalnie integrują się z WidgetKit do tworzenia interaktywnych widgetów w iOS 17+. ```swift // TaskWidgetIntents.swift import AppIntents import WidgetKit // Intent zoptymalizowany pod widget (szybkie wykonanie) struct ToggleTaskFromWidgetIntent: AppIntent { static var title: LocalizedStringResource = "Przełącz zadanie" @Parameter(title: "ID zadania") var taskID: String init() {} init(taskID: UUID) { self.taskID = taskID.uuidString } // Brak dialogu dla widgetów func perform() async throws -> some IntentResult { guard let uuid = UUID(uuidString: taskID) else { return .result() } TaskService.shared.toggleCompletion(taskId: uuid) // Natychmiastowe odświeżenie widgetu WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget") return .result() } } // Widok widgetu z interaktywnym przyciskiem 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) } } ``` Widgety używają składni `Button(intent:)`, aby bezpośrednio połączyć interakcję z App Intent, bez otwierania aplikacji. ## Konfiguracja Action Button Action Button w iPhone 15 Pro i nowszych może wyzwalać App Shortcuts bezpośrednio. ```swift // ActionButtonIntent.swift import AppIntents // Intent zoptymalizowany pod Action Button struct QuickCaptureIntent: AppIntent { static var title: LocalizedStringResource = "Szybkie przechwycenie" static var description = IntentDescription( "Szybko tworzy zadanie z tytułem." ) // Otwiera aplikację do wprowadzenia static var openAppWhenRun: Bool = true func perform() async throws -> some IntentResult & OpensIntent { // Powiadomienie do otwarcia ekranu szybkiego przechwytywania NotificationCenter.default.post( name: .quickCaptureTriggered, object: nil ) return .result(opensIntent: ShowQuickCaptureViewIntent()) } } // Zadeklaruj w AppShortcutsProvider dla Action Button extension TaskAppShortcutsProvider { @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // ... inne skróty AppShortcut( intent: QuickCaptureIntent(), phrases: [ "Szybkie przechwycenie \(.applicationName)", "Szybka notatka \(.applicationName)" ], shortTitle: "Przechwyć", systemImageName: "bolt.circle" ) } } ``` Użytkownicy mogą skonfigurować Action Button do wyzwalania tego skrótu przez Ustawienia > Action Button. > **Makra iOS 18+** > > iOS 18 wprowadza makra `@DeferredProperty` i `@ComputedProperty` redukujące boilerplate. App Intents mogą również znajdować się w Swift Packages dla wieloplatformowego ponownego użycia. ## Intenty w Swift Packages App Intents mogą być definiowane w Swift Packages do współdzielenia między iOS, macOS i 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 // Wspólny intent wieloplatformowy public struct SharedCreateTaskIntent: AppIntent { public static var title: LocalizedStringResource = "Utwórz zadanie" @Parameter(title: "Tytuł") public var taskTitle: String public init() {} public func perform() async throws -> some IntentResult { // Wspólna logika await TaskRepository.shared.create(title: taskTitle) return .result() } } // Rozszerzenie specyficzne dla platformy w aplikacji #if os(iOS) extension SharedCreateTaskIntent { // Zachowanie specyficzne dla iOS static var openAppWhenRun: Bool = false } #endif ``` Ta architektura utrzymuje pojedynczą bazę kodu intentów, dostosowując zachowanie per platformę. ## Testowanie App Intents App Intents mogą być testowane jednostkowo jak każdy kod Swift. ```swift // TaskIntentTests.swift import XCTest import AppIntents @testable import TaskApp final class TaskIntentTests: XCTestCase { override func setUp() { super.setUp() // Reset serwisu dla testów izolowanych TaskService.shared.reset() } func testCreateTaskIntent() async throws { // Given var intent = CreateTaskIntent() intent.taskTitle = "Zadanie testowe" intent.priority = .high // When let result = try await intent.perform() // Then let tasks = TaskService.shared.fetchTasks() XCTAssertEqual(tasks.count, 1) XCTAssertEqual(tasks.first?.title, "Zadanie testowe") XCTAssertEqual(tasks.first?.priority, .high) } func testCompleteTaskIntent() async throws { // Given let task = TaskService.shared.createTask( title: "Zadanie do ukończenia", 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: "Zadanie 1", priority: .low) let task2 = TaskService.shared.createTask(title: "Zadanie 2", priority: .high) let query = TaskEntityQuery() // When let entities = try await query.entities(for: [task1.id, task2.id]) // Then XCTAssertEqual(entities.count, 2) } } ``` Testy weryfikują zachowanie intentów niezależnie od interfejsu systemu. ## Najlepsze praktyki i optymalizacje Kilka wzorców zapewnia wydajne App Intents i optymalne doświadczenie użytkownika. ```swift // BestPractices.swift import AppIntents // 1. Frazy zlokalizowane 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. Prawidłowa obsługa błędów enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible { case taskNotFound case invalidInput case serviceUnavailable var localizedStringResource: LocalizedStringResource { switch self { case .taskNotFound: return "Nie znaleziono zadania." case .invalidInput: return "Nieprawidłowe dane." case .serviceUnavailable: return "Usługa tymczasowo niedostępna." } } } struct RobustTaskIntent: AppIntent { static var title: LocalizedStringResource = "Solidny 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: "Zadanie znalezione: \(task.title)") } } // 3. Optymalizacja EntityQuery struct OptimizedTaskQuery: EntityStringQuery { // Zoptymalizowane wyszukiwanie tekstowe func entities(matching string: String) async throws -> [TaskEntity] { // Wyszukiwanie po stronie serwisu z limitem TaskService.shared.search(query: string, limit: 10) .map { TaskEntity(task: $0) } } // Ograniczone sugestie dla wydajności func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchRecentTasks(limit: 5) .map { TaskEntity(task: $0) } } } // 4. Focus Filter dla trybów Skupienia struct TaskFocusFilter: SetFocusFilterIntent { static var title: LocalizedStringResource = "Filtruj zadania" @Parameter(title: "Pokaż tylko wysoki priorytet") var showHighPriorityOnly: Bool func perform() async throws -> some IntentResult { TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly) return .result() } } ``` Te wzorce zapewniają płynną integrację z systemem przy zachowaniu optymalnej wydajności. ## Podsumowanie App Intents i Siri Shortcuts transformują sposób, w jaki użytkownicy wchodzą w interakcję z aplikacjami iOS. W 2026 roku, dzięki Apple Intelligence, ujawnianie intentów nie jest już opcjonalne, ale niezbędne do dostarczania nowoczesnego i intuicyjnego doświadczenia. ### Lista kontrolna App Intents iOS 18+ - ✅ Utwórz AppIntents dla głównych akcji aplikacji - ✅ Zdefiniuj AppEntities dla danych do manipulacji - ✅ Użyj AppEnum dla typów wyliczeniowych - ✅ Zaimplementuj AppShortcutsProvider z frazami głosowymi - ✅ Przestrzegaj limitu maksymalnie 10 App Shortcuts - ✅ Uwzględnij `\(.applicationName)` we wszystkich frazach - ✅ Skonfiguruj ParameterSummary dla feedbacku Siri - ✅ Zintegruj domeny Apple Intelligence, jeśli dotyczy - ✅ Testuj intenty testami jednostkowymi - ✅ Zlokalizuj tytuły i opisy --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/ios/app-intents-siri-shortcuts-advanced-ios-automation