# App Intents dan Siri Shortcuts: otomatisasi iOS lanjutan 2026 > Panduan lengkap App Intents dan Siri Shortcuts untuk iOS 18+. Membuat aksi Siri kustom, mengintegrasikan Apple Intelligence, dan mengotomatisasi aplikasi Swift di 2026. - Published: 2026-03-08 - Updated: 2026-04-29 - Author: SharpSkill - Tags: app-intents, siri-shortcuts, ios, swift, apple-intelligence - Reading time: 15 min --- Pada tahun 2026, dengan Apple Intelligence dan framework App Intents, aplikasi iOS memasuki era di mana niat pengguna lebih penting daripada antarmuka grafis. Aplikasi yang tidak mengekspos intent menjadi tidak terlihat dalam OS yang berorientasi AI. App Intents menjadi fondasi yang memungkinkan Siri, Spotlight, widget, dan Action Button untuk berinteraksi dengan fungsionalitas aplikasi. > **Apa yang dibahas artikel ini** > > Artikel ini menyajikan pembuatan App Intents dan Siri Shortcuts secara lengkap untuk iOS 18+, dari konsep dasar hingga integrasi dengan Apple Intelligence dan App Intent Domains. ## Memahami framework App Intents Framework App Intents, diperkenalkan pada iOS 16, memodernisasi pembuatan intent di Swift dengan menggantikan framework SiriKit Intents lama. Arsitektur deklaratif ini memungkinkan pembuatan aksi yang dapat ditemukan oleh sistem: Spotlight, aplikasi Pintasan, Siri, dan Action Button. ```swift // TaskIntent.swift import AppIntents // AppIntent merepresentasikan aksi yang dapat dilakukan pengguna struct CreateTaskIntent: AppIntent { // Judul yang ditampilkan di Pintasan dan Siri static var title: LocalizedStringResource = "Buat tugas" // Deskripsi untuk aksesibilitas dan saran static var description = IntentDescription( "Membuat tugas baru di aplikasi." ) // Parameter dengan validasi otomatis @Parameter(title: "Judul tugas") var taskTitle: String // Parameter opsional dengan nilai default @Parameter(title: "Prioritas", default: .medium) var priority: TaskPriority // Eksekusi aksi func perform() async throws -> some IntentResult & ReturnsValue { // Membuat tugas via service let task = TaskService.shared.createTask( title: taskTitle, priority: priority ) // Mengembalikan entity yang dibuat untuk chaining return .result(value: TaskEntity(task: task)) } } ``` Intent mendeklarasikan parameter melalui property wrapper `@Parameter`, memungkinkan Siri meminta nilai yang hilang. Method `perform()` mengeksekusi logika bisnis dan mengembalikan hasil bertipe. ## Mendefinisikan App Entities untuk data App Entities merepresentasikan "kata benda" aplikasi: objek yang dioperasikan oleh intent. Mereka memungkinkan Siri memahami dan memanipulasi data aplikasi. ```swift // TaskEntity.swift import AppIntents // Model data internal struct Task: Identifiable, Codable { let id: UUID var title: String var priority: TaskPriority var isCompleted: Bool var dueDate: Date? } // Entity yang diekspos ke sistem struct TaskEntity: AppEntity { // Identifier unik wajib var id: UUID // Properti yang dapat ditampilkan var title: String var priority: TaskPriority var isCompleted: Bool // Konfigurasi tampilan di sistem static var typeDisplayRepresentation: TypeDisplayRepresentation = "Tugas" // Representasi visual instance var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: "\(title)", subtitle: "\(priority.rawValue)", image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle") ) } // Query default untuk mencari entity static var defaultQuery = TaskEntityQuery() // Initializer dari model internal init(task: Task) { self.id = task.id self.title = task.title self.priority = task.priority self.isCompleted = task.isCompleted } } // Query untuk mencari dan memfilter entity struct TaskEntityQuery: EntityQuery { // Pencarian berdasarkan identifier func entities(for identifiers: [UUID]) async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { identifiers.contains($0.id) } .map { TaskEntity(task: $0) } } // Saran yang ditampilkan di antarmuka func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { !$0.isCompleted } .prefix(5) .map { TaskEntity(task: $0) } } } ``` `EntityQuery` mendefinisikan bagaimana sistem mencari dan menyarankan entity. Method `entities(for:)` dan `suggestedEntities()` memberi makan antarmuka Siri dan Pintasan. > **App Enums vs App Entities** > > Gunakan `AppEnum` untuk tipe dengan himpunan nilai tetap (prioritas, status), dan `AppEntity` untuk tipe dinamis yang dibuat pengguna (tugas, catatan, kontak). ## Membuat App Enums untuk nilai tetap App Enums mengekspos tipe enumerasi ke sistem, memungkinkan Siri menawarkan pilihan kontekstual. ```swift // TaskPriority.swift import AppIntents // Enum yang diekspos ke sistem enum TaskPriority: String, AppEnum, Codable { case low case medium case high // Nama tipe yang ditampilkan static var typeDisplayRepresentation: TypeDisplayRepresentation = "Prioritas" // Representasi setiap kasus static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [ .low: DisplayRepresentation( title: "Rendah", image: .init(systemName: "arrow.down.circle") ), .medium: DisplayRepresentation( title: "Sedang", image: .init(systemName: "minus.circle") ), .high: DisplayRepresentation( title: "Tinggi", image: .init(systemName: "exclamationmark.circle") ) ] } // Enum untuk status tugas enum TaskStatus: String, AppEnum, Codable { case pending case inProgress case completed static var typeDisplayRepresentation: TypeDisplayRepresentation = "Status" static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [ .pending: "Menunggu", .inProgress: "Sedang berlangsung", .completed: "Selesai" ] } ``` Representasi visual (ikon SF Symbols) memperkaya tampilan di Pintasan dan saran Siri. ## Mengimplementasikan AppShortcutsProvider `AppShortcutsProvider` mengekspos App Shortcuts ke sistem, membuatnya tersedia segera tanpa konfigurasi pengguna. Pintasan ini muncul di Spotlight, Siri, dan Action Button. ```swift // ShortcutsProvider.swift import AppIntents // Provider yang mendeklarasikan semua pintasan aplikasi struct TaskAppShortcutsProvider: AppShortcutsProvider { // Maksimum 10 pintasan per aplikasi @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // Pintasan untuk membuat tugas AppShortcut( intent: CreateTaskIntent(), phrases: [ // Placeholder .applicationName WAJIB ada "Buat tugas dengan \(.applicationName)", "Tugas baru di \(.applicationName)", "Tambahkan tugas ke \(.applicationName)" ], shortTitle: "Buat tugas", systemImageName: "plus.circle" ) // Pintasan untuk mendaftar tugas AppShortcut( intent: ListTasksIntent(), phrases: [ "Tampilkan tugas saya di \(.applicationName)", "Tampilkan tugas dari \(.applicationName)", "Apa saja tugas saya \(.applicationName)" ], shortTitle: "Tugas saya", systemImageName: "list.bullet" ) // Pintasan dengan parameter dinamis AppShortcut( intent: CompleteTaskIntent(), phrases: [ "Selesaikan \(\.$taskName) di \(.applicationName)", "Tandai \(\.$taskName) sebagai selesai dengan \(.applicationName)" ], shortTitle: "Selesaikan tugas", systemImageName: "checkmark.circle" ) } } ``` Frasa suara harus menyertakan placeholder `\(.applicationName)` agar Siri mengidentifikasi aplikasi tujuan. Parameter dinamis seperti `\(\.$taskName)` memungkinkan perintah kontekstual. > **Batas 10 Shortcuts** > > Aplikasi dapat mendeklarasikan maksimum 10 App Shortcuts. Sebaiknya prioritaskan aksi yang paling sering dan berguna bagi pengguna. ## Intent dengan parameter kompleks Intent dapat menerima parameter kompleks termasuk entity dan opsi konfigurasi. ```swift // CompleteTaskIntent.swift import AppIntents struct CompleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Selesaikan tugas" static var description = IntentDescription( "Menandai tugas sebagai selesai." ) // Parameter entity dengan pencarian otomatis @Parameter(title: "Tugas") var task: TaskEntity // Parameter opsional dengan tanggal @Parameter(title: "Tanggal selesai") var completionDate: Date? // Konfigurasi dialog Siri static var parameterSummary: some ParameterSummary { Summary("Selesaikan \(\.$task)") { \.$completionDate } } func perform() async throws -> some IntentResult & ProvidesDialog { // Memperbarui tugas TaskService.shared.completeTask( id: task.id, completionDate: completionDate ?? Date() ) // Umpan balik suara untuk Siri return .result( dialog: "Tugas \(task.title) telah ditandai sebagai selesai." ) } } // Intent yang mengembalikan daftar entity struct ListTasksIntent: AppIntent { static var title: LocalizedStringResource = "Daftar tugas" // Filter opsional @Parameter(title: "Status", default: nil) var statusFilter: TaskStatus? @Parameter(title: "Prioritas", default: nil) var priorityFilter: TaskPriority? func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> { var tasks = TaskService.shared.fetchTasks() // Terapkan filter 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` mendefinisikan bagaimana Siri menyajikan intent selama eksekusi suara, memberikan umpan balik yang alami. ## Integrasi Apple Intelligence iOS 18 memperkenalkan App Intent Domains, koleksi API yang dirancang untuk fungsionalitas spesifik. Domain ini memungkinkan Apple Intelligence memahami dan mengeksekusi aksi dengan akurasi yang lebih tinggi. ```swift // BookmarkDomainIntents.swift import AppIntents // Konformitas ke domain Bookmarks untuk integrasi Apple Intelligence struct SaveBookmarkIntent: AppIntent { static var title: LocalizedStringResource = "Simpan bookmark" // Parameter URL dengan validasi otomatis @Parameter(title: "URL") var url: URL @Parameter(title: "Judul", default: nil) var title: String? @Parameter(title: "Folder", default: nil) var folder: BookmarkFolderEntity? // Buka aplikasi jika perlu 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: "Bookmark disimpan: \(bookmark.title)" ) } } // Intent dengan kesadaran konten layar (iOS 18.4+) struct AnalyzeScreenContentIntent: AppIntent { static var title: LocalizedStringResource = "Analisis konten" // Akses ke konteks layar via Apple Intelligence @Parameter(title: "Konteks") var screenContext: String? func perform() async throws -> some IntentResult & ProvidesDialog { guard let context = screenContext else { return .result(dialog: "Tidak ada konten untuk dianalisis.") } // Memproses konten yang diekstrak oleh Apple Intelligence let analysis = ContentAnalyzer.analyze(context) return .result(dialog: analysis.summary) } } ``` Domain pradefinisi (Books, Camera, Spreadsheets) memungkinkan Siri merespons permintaan dengan akurat berkat model yang dilatih pada tugas-tugas spesifik ini. ## Aksi Siri dengan konfirmasi pengguna Untuk aksi sensitif, sistem dapat meminta konfirmasi sebelum eksekusi. ```swift // DeleteTaskIntent.swift import AppIntents struct DeleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Hapus tugas" @Parameter(title: "Tugas") var task: TaskEntity // Memerlukan konfirmasi pengguna static var isDiscoverable: Bool = true func perform() async throws -> some IntentResult & ProvidesDialog { // Meminta konfirmasi via dialog try await requestConfirmation( result: .result( dialog: "Yakin ingin menghapus \(task.title)?" ) ) // Hapus setelah konfirmasi TaskService.shared.deleteTask(id: task.id) return .result( dialog: "Tugas \(task.title) telah dihapus." ) } } // Intent dengan beberapa langkah dialog struct ScheduleTaskIntent: AppIntent { static var title: LocalizedStringResource = "Jadwalkan tugas" @Parameter(title: "Tugas") var task: TaskEntity @Parameter(title: "Tanggal") var scheduledDate: Date @Parameter(title: "Pengingat", default: true) var setReminder: Bool static var parameterSummary: some ParameterSummary { When(\.$setReminder, .equalTo, true) { Summary("Jadwalkan \(\.$task) untuk \(\.$scheduledDate) dengan pengingat") } otherwise: { Summary("Jadwalkan \(\.$task) untuk \(\.$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: "Tugas dijadwalkan untuk \(formattedDate)." ) } } ``` Method `requestConfirmation` menjeda eksekusi sampai validasi pengguna, melindungi dari aksi yang tidak disengaja. ## Intent dalam widget interaktif App Intents terintegrasi secara alami dengan WidgetKit untuk membuat widget interaktif iOS 17+. ```swift // TaskWidgetIntents.swift import AppIntents import WidgetKit // Intent yang dioptimalkan untuk widget (eksekusi cepat) struct ToggleTaskFromWidgetIntent: AppIntent { static var title: LocalizedStringResource = "Toggle tugas" @Parameter(title: "ID tugas") var taskID: String init() {} init(taskID: UUID) { self.taskID = taskID.uuidString } // Tanpa dialog untuk widget func perform() async throws -> some IntentResult { guard let uuid = UUID(uuidString: taskID) else { return .result() } TaskService.shared.toggleCompletion(taskId: uuid) // Refresh widget seketika WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget") return .result() } } // View widget dengan tombol interaktif 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) } } ``` Widget menggunakan sintaks `Button(intent:)` untuk menghubungkan interaksi langsung ke App Intent, tanpa membuka aplikasi. ## Konfigurasi Action Button Action Button pada iPhone 15 Pro dan yang lebih baru dapat memicu App Shortcuts secara langsung. ```swift // ActionButtonIntent.swift import AppIntents // Intent yang dioptimalkan untuk Action Button struct QuickCaptureIntent: AppIntent { static var title: LocalizedStringResource = "Pencatatan cepat" static var description = IntentDescription( "Membuat tugas dengan judul secara cepat." ) // Buka aplikasi untuk input static var openAppWhenRun: Bool = true func perform() async throws -> some IntentResult & OpensIntent { // Notifikasi untuk membuka layar pencatatan cepat NotificationCenter.default.post( name: .quickCaptureTriggered, object: nil ) return .result(opensIntent: ShowQuickCaptureViewIntent()) } } // Deklarasikan di AppShortcutsProvider untuk Action Button extension TaskAppShortcutsProvider { @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // ... pintasan lainnya AppShortcut( intent: QuickCaptureIntent(), phrases: [ "Pencatatan cepat \(.applicationName)", "Catatan cepat \(.applicationName)" ], shortTitle: "Catat", systemImageName: "bolt.circle" ) } } ``` Pengguna dapat mengkonfigurasi Action Button untuk memicu pintasan ini melalui Pengaturan > Tombol Aksi. > **Macros iOS 18+** > > iOS 18 memperkenalkan macros `@DeferredProperty` dan `@ComputedProperty` untuk mengurangi boilerplate. App Intents juga dapat berada dalam Swift Packages untuk reuse lintas platform. ## Intents dalam Swift Packages App Intents dapat didefinisikan dalam Swift Packages untuk berbagi antara iOS, macOS, dan 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 // Intent berbagi lintas platform public struct SharedCreateTaskIntent: AppIntent { public static var title: LocalizedStringResource = "Buat tugas" @Parameter(title: "Judul") public var taskTitle: String public init() {} public func perform() async throws -> some IntentResult { // Logika berbagi await TaskRepository.shared.create(title: taskTitle) return .result() } } // Ekstensi spesifik platform di aplikasi #if os(iOS) extension SharedCreateTaskIntent { // Perilaku spesifik iOS static var openAppWhenRun: Bool = false } #endif ``` Arsitektur ini mempertahankan satu basis kode untuk intent sambil mengadaptasi perilaku per platform. ## Menguji App Intents App Intents dapat diuji secara unit seperti kode Swift apa pun. ```swift // TaskIntentTests.swift import XCTest import AppIntents @testable import TaskApp final class TaskIntentTests: XCTestCase { override func setUp() { super.setUp() // Reset service untuk pengujian terisolasi TaskService.shared.reset() } func testCreateTaskIntent() async throws { // Given var intent = CreateTaskIntent() intent.taskTitle = "Tugas pengujian" intent.priority = .high // When let result = try await intent.perform() // Then let tasks = TaskService.shared.fetchTasks() XCTAssertEqual(tasks.count, 1) XCTAssertEqual(tasks.first?.title, "Tugas pengujian") XCTAssertEqual(tasks.first?.priority, .high) } func testCompleteTaskIntent() async throws { // Given let task = TaskService.shared.createTask( title: "Tugas untuk diselesaikan", 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: "Tugas 1", priority: .low) let task2 = TaskService.shared.createTask(title: "Tugas 2", priority: .high) let query = TaskEntityQuery() // When let entities = try await query.entities(for: [task1.id, task2.id]) // Then XCTAssertEqual(entities.count, 2) } } ``` Pengujian memverifikasi perilaku intent secara independen dari antarmuka sistem. ## Praktik terbaik dan optimisasi Beberapa pola memastikan App Intents yang berkinerja baik dan pengalaman pengguna yang optimal. ```swift // BestPractices.swift import AppIntents // 1. Frasa terlokalisasi 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. Penanganan error yang tepat enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible { case taskNotFound case invalidInput case serviceUnavailable var localizedStringResource: LocalizedStringResource { switch self { case .taskNotFound: return "Tugas tidak ditemukan." case .invalidInput: return "Data tidak valid." case .serviceUnavailable: return "Layanan sementara tidak tersedia." } } } struct RobustTaskIntent: AppIntent { static var title: LocalizedStringResource = "Intent yang tangguh" @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: "Tugas ditemukan: \(task.title)") } } // 3. Optimisasi EntityQuery struct OptimizedTaskQuery: EntityStringQuery { // Pencarian teks yang dioptimalkan func entities(matching string: String) async throws -> [TaskEntity] { // Pencarian sisi service dengan limit TaskService.shared.search(query: string, limit: 10) .map { TaskEntity(task: $0) } } // Saran terbatas demi kinerja func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchRecentTasks(limit: 5) .map { TaskEntity(task: $0) } } } // 4. Focus Filter untuk mode Fokus struct TaskFocusFilter: SetFocusFilterIntent { static var title: LocalizedStringResource = "Filter tugas" @Parameter(title: "Tampilkan hanya prioritas tinggi") var showHighPriorityOnly: Bool func perform() async throws -> some IntentResult { TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly) return .result() } } ``` Pola-pola ini memastikan integrasi yang mulus dengan sistem sambil mempertahankan kinerja optimal. ## Kesimpulan App Intents dan Siri Shortcuts mengubah cara pengguna berinteraksi dengan aplikasi iOS. Pada tahun 2026, dengan Apple Intelligence, mengekspos intent tidak lagi opsional tetapi esensial untuk memberikan pengalaman modern dan intuitif. ### Daftar periksa App Intents iOS 18+ - ✅ Buat AppIntents untuk aksi utama aplikasi - ✅ Definisikan AppEntities untuk data yang dapat dimanipulasi - ✅ Gunakan AppEnum untuk tipe enumerasi - ✅ Implementasikan AppShortcutsProvider dengan frasa suara - ✅ Patuhi batas maksimum 10 App Shortcuts - ✅ Sertakan `\(.applicationName)` di semua frasa - ✅ Konfigurasikan ParameterSummary untuk umpan balik Siri - ✅ Integrasikan domain Apple Intelligence jika sesuai - ✅ Uji intent dengan pengujian unit - ✅ Lokalisasikan judul dan deskripsi --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/ios/app-intents-siri-shortcuts-advanced-ios-automation