# App Intents và Siri Shortcuts: tự động hóa iOS nâng cao 2026 > Hướng dẫn đầy đủ về App Intents và Siri Shortcuts cho iOS 18+. Tạo hành động Siri tùy chỉnh, tích hợp Apple Intelligence và tự động hóa ứng dụng Swift trong năm 2026. - Published: 2026-03-08 - Updated: 2026-04-29 - Author: SharpSkill - Tags: app-intents, siri-shortcuts, ios, swift, apple-intelligence - Reading time: 15 min --- Năm 2026, với Apple Intelligence và framework App Intents, ứng dụng iOS bước vào kỷ nguyên mà ý định người dùng quan trọng hơn giao diện đồ họa. Các ứng dụng không phơi bày intents trở nên vô hình trong một hệ điều hành lấy AI làm trung tâm. App Intents tạo nên nền tảng cho phép Siri, Spotlight, widget và Action Button tương tác với chức năng của ứng dụng. > **Bài viết này bao gồm** > > Bài viết trình bày toàn bộ quá trình tạo App Intents và Siri Shortcuts cho iOS 18+, từ các khái niệm cơ bản cho đến tích hợp Apple Intelligence và App Intent Domains. ## Hiểu framework App Intents Framework App Intents, ra mắt cùng iOS 16, hiện đại hóa việc xây dựng intents trong Swift bằng cách thay thế framework SiriKit Intents cũ. Kiến trúc khai báo này cho phép tạo các hành động được hệ thống khám phá: Spotlight, ứng dụng Phím tắt, Siri và Action Button. ```swift // TaskIntent.swift import AppIntents // Một AppIntent đại diện cho hành động mà người dùng có thể thực hiện struct CreateTaskIntent: AppIntent { // Tiêu đề hiển thị trong Phím tắt và Siri static var title: LocalizedStringResource = "Tạo nhiệm vụ" // Mô tả cho khả năng truy cập và đề xuất static var description = IntentDescription( "Tạo nhiệm vụ mới trong ứng dụng." ) // Tham số có xác thực tự động @Parameter(title: "Tiêu đề nhiệm vụ") var taskTitle: String // Tham số tùy chọn với giá trị mặc định @Parameter(title: "Mức độ ưu tiên", default: .medium) var priority: TaskPriority // Thực thi hành động func perform() async throws -> some IntentResult & ReturnsValue { // Tạo nhiệm vụ qua service let task = TaskService.shared.createTask( title: taskTitle, priority: priority ) // Trả về entity đã tạo để chaining return .result(value: TaskEntity(task: task)) } } ``` Intent khai báo các tham số qua property wrapper `@Parameter`, cho phép Siri yêu cầu các giá trị thiếu. Phương thức `perform()` thực thi logic nghiệp vụ và trả về kết quả đã định kiểu. ## Định nghĩa App Entities cho dữ liệu App Entities đại diện cho "danh từ" của ứng dụng: các đối tượng mà intents thao tác trên đó. Chúng cho phép Siri hiểu và xử lý dữ liệu ứng dụng. ```swift // TaskEntity.swift import AppIntents // Mô hình dữ liệu nội bộ struct Task: Identifiable, Codable { let id: UUID var title: String var priority: TaskPriority var isCompleted: Bool var dueDate: Date? } // Entity được phơi bày cho hệ thống struct TaskEntity: AppEntity { // Định danh duy nhất bắt buộc var id: UUID // Các thuộc tính có thể hiển thị var title: String var priority: TaskPriority var isCompleted: Bool // Cấu hình hiển thị trong hệ thống static var typeDisplayRepresentation: TypeDisplayRepresentation = "Nhiệm vụ" // Biểu diễn trực quan của instance var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: "\(title)", subtitle: "\(priority.rawValue)", image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle") ) } // Query mặc định để tìm kiếm entity static var defaultQuery = TaskEntityQuery() // Initializer từ mô hình nội bộ init(task: Task) { self.id = task.id self.title = task.title self.priority = task.priority self.isCompleted = task.isCompleted } } // Query để tìm kiếm và lọc entity struct TaskEntityQuery: EntityQuery { // Tìm kiếm theo định danh func entities(for identifiers: [UUID]) async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { identifiers.contains($0.id) } .map { TaskEntity(task: $0) } } // Đề xuất hiển thị trong giao diện func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { !$0.isCompleted } .prefix(5) .map { TaskEntity(task: $0) } } } ``` `EntityQuery` định nghĩa cách hệ thống tìm kiếm và đề xuất entity. Các phương thức `entities(for:)` và `suggestedEntities()` cung cấp cho các giao diện Siri và Phím tắt. > **App Enums vs App Entities** > > Sử dụng `AppEnum` cho các kiểu có tập giá trị cố định (mức độ ưu tiên, trạng thái), và `AppEntity` cho các kiểu động được người dùng tạo (nhiệm vụ, ghi chú, danh bạ). ## Tạo App Enums cho giá trị cố định App Enums phơi bày các kiểu liệt kê cho hệ thống, cho phép Siri đưa ra các lựa chọn theo ngữ cảnh. ```swift // TaskPriority.swift import AppIntents // Enum được phơi bày cho hệ thống enum TaskPriority: String, AppEnum, Codable { case low case medium case high // Tên kiểu hiển thị static var typeDisplayRepresentation: TypeDisplayRepresentation = "Mức độ ưu tiên" // Biểu diễn từng trường hợp static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [ .low: DisplayRepresentation( title: "Thấp", image: .init(systemName: "arrow.down.circle") ), .medium: DisplayRepresentation( title: "Trung bình", image: .init(systemName: "minus.circle") ), .high: DisplayRepresentation( title: "Cao", image: .init(systemName: "exclamationmark.circle") ) ] } // Enum cho trạng thái nhiệm vụ enum TaskStatus: String, AppEnum, Codable { case pending case inProgress case completed static var typeDisplayRepresentation: TypeDisplayRepresentation = "Trạng thái" static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [ .pending: "Đang chờ", .inProgress: "Đang tiến hành", .completed: "Hoàn thành" ] } ``` Các biểu diễn trực quan (icon SF Symbols) làm phong phú hiển thị trong Phím tắt và đề xuất Siri. ## Triển khai AppShortcutsProvider `AppShortcutsProvider` phơi bày App Shortcuts cho hệ thống, làm chúng có sẵn ngay lập tức mà không cần cấu hình của người dùng. Các phím tắt này xuất hiện trong Spotlight, Siri và Action Button. ```swift // ShortcutsProvider.swift import AppIntents // Provider khai báo tất cả phím tắt của ứng dụng struct TaskAppShortcutsProvider: AppShortcutsProvider { // Tối đa 10 phím tắt mỗi ứng dụng @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // Phím tắt để tạo nhiệm vụ AppShortcut( intent: CreateTaskIntent(), phrases: [ // Placeholder .applicationName là BẮT BUỘC "Tạo nhiệm vụ với \(.applicationName)", "Nhiệm vụ mới trong \(.applicationName)", "Thêm nhiệm vụ vào \(.applicationName)" ], shortTitle: "Tạo nhiệm vụ", systemImageName: "plus.circle" ) // Phím tắt để liệt kê nhiệm vụ AppShortcut( intent: ListTasksIntent(), phrases: [ "Hiển thị nhiệm vụ của tôi trong \(.applicationName)", "Hiển thị nhiệm vụ từ \(.applicationName)", "Nhiệm vụ của tôi là gì \(.applicationName)" ], shortTitle: "Nhiệm vụ của tôi", systemImageName: "list.bullet" ) // Phím tắt với tham số động AppShortcut( intent: CompleteTaskIntent(), phrases: [ "Hoàn thành \(\.$taskName) trong \(.applicationName)", "Đánh dấu \(\.$taskName) là xong với \(.applicationName)" ], shortTitle: "Hoàn thành nhiệm vụ", systemImageName: "checkmark.circle" ) } } ``` Các cụm từ giọng nói phải bao gồm placeholder `\(.applicationName)` để Siri xác định ứng dụng đích. Các tham số động như `\(\.$taskName)` cho phép các lệnh theo ngữ cảnh. > **Giới hạn 10 Shortcuts** > > Một ứng dụng có thể khai báo tối đa 10 App Shortcuts. Nên ưu tiên các hành động thường xuyên và hữu ích nhất cho người dùng. ## Intents với tham số phức tạp Intents có thể chấp nhận các tham số phức tạp, bao gồm entity và tùy chọn cấu hình. ```swift // CompleteTaskIntent.swift import AppIntents struct CompleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Hoàn thành nhiệm vụ" static var description = IntentDescription( "Đánh dấu nhiệm vụ là đã hoàn thành." ) // Tham số entity với tìm kiếm tự động @Parameter(title: "Nhiệm vụ") var task: TaskEntity // Tham số tùy chọn với ngày @Parameter(title: "Ngày hoàn thành") var completionDate: Date? // Cấu hình hộp thoại Siri static var parameterSummary: some ParameterSummary { Summary("Hoàn thành \(\.$task)") { \.$completionDate } } func perform() async throws -> some IntentResult & ProvidesDialog { // Cập nhật nhiệm vụ TaskService.shared.completeTask( id: task.id, completionDate: completionDate ?? Date() ) // Phản hồi giọng nói cho Siri return .result( dialog: "Nhiệm vụ \(task.title) đã được đánh dấu là hoàn thành." ) } } // Intent trả về danh sách entity struct ListTasksIntent: AppIntent { static var title: LocalizedStringResource = "Liệt kê nhiệm vụ" // Bộ lọc tùy chọn @Parameter(title: "Trạng thái", default: nil) var statusFilter: TaskStatus? @Parameter(title: "Mức độ ưu tiên", default: nil) var priorityFilter: TaskPriority? func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> { var tasks = TaskService.shared.fetchTasks() // Áp dụng bộ lọc 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` định nghĩa cách Siri trình bày intent trong khi thực thi bằng giọng nói, mang lại phản hồi tự nhiên. ## Tích hợp với Apple Intelligence iOS 18 giới thiệu App Intent Domains, các tập hợp API được thiết kế cho các chức năng cụ thể. Các domain này cho phép Apple Intelligence hiểu và thực thi các hành động với độ chính xác cao hơn. ```swift // BookmarkDomainIntents.swift import AppIntents // Tuân thủ domain Bookmarks để tích hợp Apple Intelligence struct SaveBookmarkIntent: AppIntent { static var title: LocalizedStringResource = "Lưu dấu trang" // Tham số URL với xác thực tự động @Parameter(title: "URL") var url: URL @Parameter(title: "Tiêu đề", default: nil) var title: String? @Parameter(title: "Thư mục", default: nil) var folder: BookmarkFolderEntity? // Mở ứng dụng nếu cần 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: "Dấu trang đã lưu: \(bookmark.title)" ) } } // Intent với khả năng nhận biết nội dung màn hình (iOS 18.4+) struct AnalyzeScreenContentIntent: AppIntent { static var title: LocalizedStringResource = "Phân tích nội dung" // Truy cập ngữ cảnh màn hình qua Apple Intelligence @Parameter(title: "Ngữ cảnh") var screenContext: String? func perform() async throws -> some IntentResult & ProvidesDialog { guard let context = screenContext else { return .result(dialog: "Không có nội dung để phân tích.") } // Xử lý nội dung được Apple Intelligence trích xuất let analysis = ContentAnalyzer.analyze(context) return .result(dialog: analysis.summary) } } ``` Các domain được định nghĩa trước (Books, Camera, Spreadsheets) cho phép Siri đáp ứng các yêu cầu chính xác nhờ các mô hình được huấn luyện trên các tác vụ cụ thể này. ## Hành động Siri với xác nhận của người dùng Đối với các hành động nhạy cảm, hệ thống có thể yêu cầu xác nhận trước khi thực thi. ```swift // DeleteTaskIntent.swift import AppIntents struct DeleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Xóa nhiệm vụ" @Parameter(title: "Nhiệm vụ") var task: TaskEntity // Yêu cầu xác nhận của người dùng static var isDiscoverable: Bool = true func perform() async throws -> some IntentResult & ProvidesDialog { // Yêu cầu xác nhận qua hộp thoại try await requestConfirmation( result: .result( dialog: "Bạn có chắc muốn xóa \(task.title) không?" ) ) // Xóa sau khi xác nhận TaskService.shared.deleteTask(id: task.id) return .result( dialog: "Nhiệm vụ \(task.title) đã được xóa." ) } } // Intent với nhiều bước hộp thoại struct ScheduleTaskIntent: AppIntent { static var title: LocalizedStringResource = "Lập lịch nhiệm vụ" @Parameter(title: "Nhiệm vụ") var task: TaskEntity @Parameter(title: "Ngày") var scheduledDate: Date @Parameter(title: "Lời nhắc", default: true) var setReminder: Bool static var parameterSummary: some ParameterSummary { When(\.$setReminder, .equalTo, true) { Summary("Lập lịch \(\.$task) cho \(\.$scheduledDate) kèm lời nhắc") } otherwise: { Summary("Lập lịch \(\.$task) cho \(\.$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: "Nhiệm vụ đã được lập lịch cho \(formattedDate)." ) } } ``` Phương thức `requestConfirmation` tạm dừng thực thi cho đến khi người dùng xác nhận, bảo vệ khỏi các hành động vô ý. ## Intents trong widget tương tác App Intents tích hợp tự nhiên với WidgetKit để tạo các widget tương tác trên iOS 17+. ```swift // TaskWidgetIntents.swift import AppIntents import WidgetKit // Intent được tối ưu cho widget (thực thi nhanh) struct ToggleTaskFromWidgetIntent: AppIntent { static var title: LocalizedStringResource = "Chuyển đổi nhiệm vụ" @Parameter(title: "ID nhiệm vụ") var taskID: String init() {} init(taskID: UUID) { self.taskID = taskID.uuidString } // Không hộp thoại cho widget func perform() async throws -> some IntentResult { guard let uuid = UUID(uuidString: taskID) else { return .result() } TaskService.shared.toggleCompletion(taskId: uuid) // Làm mới widget tức thì WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget") return .result() } } // View widget với nút tương tác 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 sử dụng cú pháp `Button(intent:)` để kết nối tương tác trực tiếp với App Intent mà không mở ứng dụng. ## Cấu hình Action Button Action Button trên iPhone 15 Pro và mới hơn có thể kích hoạt App Shortcuts trực tiếp. ```swift // ActionButtonIntent.swift import AppIntents // Intent được tối ưu cho Action Button struct QuickCaptureIntent: AppIntent { static var title: LocalizedStringResource = "Ghi nhận nhanh" static var description = IntentDescription( "Tạo nhanh nhiệm vụ với tiêu đề." ) // Mở ứng dụng để nhập static var openAppWhenRun: Bool = true func perform() async throws -> some IntentResult & OpensIntent { // Thông báo để mở màn hình ghi nhận nhanh NotificationCenter.default.post( name: .quickCaptureTriggered, object: nil ) return .result(opensIntent: ShowQuickCaptureViewIntent()) } } // Khai báo trong AppShortcutsProvider cho Action Button extension TaskAppShortcutsProvider { @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // ... các phím tắt khác AppShortcut( intent: QuickCaptureIntent(), phrases: [ "Ghi nhận nhanh \(.applicationName)", "Ghi chú nhanh \(.applicationName)" ], shortTitle: "Ghi nhận", systemImageName: "bolt.circle" ) } } ``` Người dùng có thể cấu hình Action Button để kích hoạt phím tắt này qua Cài đặt > Action Button. > **Macros iOS 18+** > > iOS 18 giới thiệu các macro `@DeferredProperty` và `@ComputedProperty` để giảm boilerplate. App Intents cũng có thể nằm trong Swift Packages để dùng lại đa nền tảng. ## Intents trong Swift Packages App Intents có thể được định nghĩa trong Swift Packages để chia sẻ giữa iOS, macOS và 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 dùng chung đa nền tảng public struct SharedCreateTaskIntent: AppIntent { public static var title: LocalizedStringResource = "Tạo nhiệm vụ" @Parameter(title: "Tiêu đề") public var taskTitle: String public init() {} public func perform() async throws -> some IntentResult { // Logic dùng chung await TaskRepository.shared.create(title: taskTitle) return .result() } } // Mở rộng dành riêng cho nền tảng trong ứng dụng #if os(iOS) extension SharedCreateTaskIntent { // Hành vi đặc thù iOS static var openAppWhenRun: Bool = false } #endif ``` Kiến trúc này duy trì một mã nguồn duy nhất cho intents trong khi điều chỉnh hành vi theo nền tảng. ## Kiểm thử App Intents App Intents có thể được kiểm thử đơn vị giống như bất kỳ mã Swift nào. ```swift // TaskIntentTests.swift import XCTest import AppIntents @testable import TaskApp final class TaskIntentTests: XCTestCase { override func setUp() { super.setUp() // Đặt lại service cho các test cô lập TaskService.shared.reset() } func testCreateTaskIntent() async throws { // Given var intent = CreateTaskIntent() intent.taskTitle = "Nhiệm vụ kiểm thử" intent.priority = .high // When let result = try await intent.perform() // Then let tasks = TaskService.shared.fetchTasks() XCTAssertEqual(tasks.count, 1) XCTAssertEqual(tasks.first?.title, "Nhiệm vụ kiểm thử") XCTAssertEqual(tasks.first?.priority, .high) } func testCompleteTaskIntent() async throws { // Given let task = TaskService.shared.createTask( title: "Nhiệm vụ cần hoàn thành", 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: "Nhiệm vụ 1", priority: .low) let task2 = TaskService.shared.createTask(title: "Nhiệm vụ 2", priority: .high) let query = TaskEntityQuery() // When let entities = try await query.entities(for: [task1.id, task2.id]) // Then XCTAssertEqual(entities.count, 2) } } ``` Các test xác minh hành vi của intent độc lập với giao diện hệ thống. ## Thực hành tốt nhất và tối ưu Một số mẫu đảm bảo App Intents hiệu năng cao và trải nghiệm người dùng tối ưu. ```swift // BestPractices.swift import AppIntents // 1. Cụm từ được bản địa hóa 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. Xử lý lỗi đúng cách enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible { case taskNotFound case invalidInput case serviceUnavailable var localizedStringResource: LocalizedStringResource { switch self { case .taskNotFound: return "Không tìm thấy nhiệm vụ." case .invalidInput: return "Dữ liệu không hợp lệ." case .serviceUnavailable: return "Dịch vụ tạm thời không khả dụng." } } } struct RobustTaskIntent: AppIntent { static var title: LocalizedStringResource = "Intent vững chắc" @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: "Đã tìm thấy nhiệm vụ: \(task.title)") } } // 3. Tối ưu EntityQuery struct OptimizedTaskQuery: EntityStringQuery { // Tìm kiếm văn bản được tối ưu func entities(matching string: String) async throws -> [TaskEntity] { // Tìm kiếm phía service có giới hạn TaskService.shared.search(query: string, limit: 10) .map { TaskEntity(task: $0) } } // Đề xuất giới hạn để tối ưu hiệu năng func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchRecentTasks(limit: 5) .map { TaskEntity(task: $0) } } } // 4. Focus Filter cho chế độ Tập trung struct TaskFocusFilter: SetFocusFilterIntent { static var title: LocalizedStringResource = "Lọc nhiệm vụ" @Parameter(title: "Chỉ hiển thị ưu tiên cao") var showHighPriorityOnly: Bool func perform() async throws -> some IntentResult { TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly) return .result() } } ``` Các mẫu này đảm bảo tích hợp mượt mà với hệ thống đồng thời duy trì hiệu năng tối ưu. ## Kết luận App Intents và Siri Shortcuts thay đổi cách người dùng tương tác với ứng dụng iOS. Năm 2026, với Apple Intelligence, việc phơi bày intents không còn là tùy chọn mà là điều thiết yếu để mang đến trải nghiệm hiện đại và trực quan. ### Danh sách kiểm tra App Intents iOS 18+ - ✅ Tạo AppIntents cho các hành động chính của ứng dụng - ✅ Định nghĩa AppEntities cho dữ liệu có thể thao tác - ✅ Sử dụng AppEnum cho các kiểu liệt kê - ✅ Triển khai AppShortcutsProvider với cụm từ giọng nói - ✅ Tuân thủ giới hạn tối đa 10 App Shortcuts - ✅ Bao gồm `\(.applicationName)` trong tất cả cụm từ - ✅ Cấu hình ParameterSummary để có phản hồi Siri - ✅ Tích hợp các domain Apple Intelligence khi phù hợp - ✅ Kiểm thử intents bằng test đơn vị - ✅ Bản địa hóa tiêu đề và mô tả --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/ios/app-intents-siri-shortcuts-advanced-ios-automation