App Intents 2.0 และ Siri Shortcuts: คู่มือระบบอัตโนมัติ iOS 27

คู่มือฉบับสมบูรณ์เกี่ยวกับ App Intents 2.0 และ Siri Shortcuts สำหรับ iOS 27 สร้าง streaming responses, การสนทนา multi-turn, View Annotations และรวมกับ Foundation Models

App Intents และ Siri Shortcuts สำหรับระบบอัตโนมัติ iOS ขั้นสูงด้วย Swift และ Apple Intelligence

App Intents 2.0 และ Siri Shortcuts เป็นเส้นทางเดียวสำหรับแอปบุคคลที่สามในการรวมเข้ากับ Siri บน iOS 27 หลังจาก WWDC 2026 Apple ยกเลิก SiriKit และกำหนดให้ App Intents เป็นเฟรมเวิร์กบังคับสำหรับการโต้ตอบด้วยเสียง การค้นพบ Spotlight และเวิร์กโฟลว์อัตโนมัติ

สิ่งที่บทความนี้ครอบคลุม

บทความนี้นำเสนอการสร้าง App Intents และ Siri Shortcuts สำหรับ iOS 27 อย่างครบถ้วน ตั้งแต่แนวคิดพื้นฐานไปจนถึง streaming responses, การสนทนา multi-turn และ View Annotations API

ทำความเข้าใจ Framework App Intents 2.0

Framework App Intents ที่เปิดตัวครั้งแรกพร้อม iOS 16 และขยายเป็นเวอร์ชัน 2.0 ที่ WWDC 2026 เป็น framework แบบ Swift-native และ declarative สำหรับสร้างการกระทำที่ระบบค้นพบได้ iOS 27 นำเสนอสี่การเพิ่มเติมหลัก: streaming responses สำหรับการดำเนินการที่ใช้เวลานาน, follow-up การสนทนา multi-turn, View Annotations สำหรับอ้างอิงองค์ประกอบบนหน้าจอ และ App Schemas สำหรับความเข้าใจเชิงความหมายโดยไม่ต้องใช้วลีฝึก

ที่ WWDC 2026 Apple ยกเลิก SiriKit อย่างเป็นทางการ และกำหนดให้ App Intents เป็นวิธีเดียวที่ Siri สามารถโต้ตอบกับแอปบุคคลที่สามได้ โค้ด SiriKit ที่มีอยู่ยังคงคอมไพล์ได้พร้อมคำเตือนการยกเลิก แต่ Apple ได้ส่งสัญญาณว่าจะสนับสนุนอีกสองถึงสามปีก่อนที่จะถูกลบออก

TaskIntent.swiftswift
import AppIntents

// An AppIntent represents an action users can perform
struct CreateTaskIntent: AppIntent {
    // Title displayed in Shortcuts and Siri
    static var title: LocalizedStringResource = "Create a task"

    // Description for accessibility and suggestions
    static var description = IntentDescription(
        "Creates a new task in the application."
    )

    // Parameter with automatic validation
    @Parameter(title: "Task title")
    var taskTitle: String

    // Optional parameter with default value
    @Parameter(title: "Priority", default: .medium)
    var priority: TaskPriority

    // Action execution with async/await
    func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
        // Create task via service
        let task = TaskService.shared.createTask(
            title: taskTitle,
            priority: priority
        )

        // Return created entity for chaining
        return .result(value: TaskEntity(task: task))
    }
}

Intent ประกาศพารามิเตอร์โดยใช้ property wrapper @Parameter ซึ่งช่วยให้ Siri ขอค่าที่ขาดหายได้แบบสนทนา เมธอด perform() ดำเนินการตรรกะทางธุรกิจและส่งคืนผลลัพธ์ที่กำหนดประเภท ระบบประเภทเองทำหน้าที่เป็น schema ที่ Siri AI ค้นพบแบบไดนามิก

Streaming Responses สำหรับการดำเนินการที่ใช้เวลานาน

App Intents 2.0 เปิดตัว streaming responses ช่วยให้ intents รายงานความคืบหน้าระหว่างการดำเนินการแทนที่จะบล็อกจนกว่าจะเสร็จสิ้น ความสามารถนี้จัดการกับสถานการณ์เช่นการอัปโหลดไฟล์ การซิงค์ข้อมูล หรือการคำนวณที่ซับซ้อนที่ผู้ใช้ต้องการฟีดแบ็ก

StreamingExportIntent.swiftswift
import AppIntents

struct ExportDataIntent: AppIntent {
    static var title: LocalizedStringResource = "Export data"

    @Parameter(title: "Format")
    var format: ExportFormat

    // Streaming response for progress reporting
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let totalItems = DataService.shared.itemCount
        var processed = 0

        // Stream progress updates to Siri
        for item in DataService.shared.allItems {
            await exportItem(item, format: format)
            processed += 1

            // Report progress at intervals
            if processed % 100 == 0 {
                await reportProgress(
                    "Exported (processed) of (totalItems) items..."
                )
            }
        }

        return .result(
            dialog: "Export complete. (totalItems) items saved as (format.rawValue)."
        )
    }
}

Streaming responses ช่วยให้ฟีดแบ็กเป็นธรรมชาติระหว่างการดำเนินการที่ใช้เวลามากกว่าไม่กี่วินาที รักษาการมีส่วนร่วมของผู้ใช้แทนที่จะปล่อยให้รอในความเงียบ

การสนทนา Multi-Turn พร้อม Follow-Ups

การสนทนา multi-turn ช่วยให้ Siri ถามคำถามเพื่อความชัดเจนและดำเนินการโต้ตอบต่อภายในเซสชันเดียว ฟีเจอร์นี้เปลี่ยน App Intents จากคำสั่งครั้งเดียวเป็นเวิร์กโฟลว์การสนทนา

ConversationalTaskIntent.swiftswift
import AppIntents

struct SmartTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Create smart task"

    @Parameter(title: "Task title")
    var taskTitle: String?

    @Parameter(title: "Priority")
    var priority: TaskPriority?

    @Parameter(title: "Due date")
    var dueDate: Date?

    // Multi-turn conversation flow
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // Request missing parameters conversationally
        let title = try await taskTitle ?? requestValue(
            for: .$taskTitle,
            dialog: "What should the task be called?"
        )

        let taskPriority = try await priority ?? requestValue(
            for: .$priority,
            dialog: "What priority level?"
        )

        // Optional follow-up
        let date: Date?
        if try await requestConfirmation(
            result: .result(dialog: "Should this task have a due date?")
        ) {
            date = try await requestValue(
                for: .$dueDate,
                dialog: "When is it due?"
            )
        } else {
            date = nil
        }

        let task = TaskService.shared.createTask(
            title: title,
            priority: taskPriority,
            dueDate: date
        )

        return .result(
            dialog: "Created '(task.title)' with (taskPriority.rawValue) priority."
        )
    }
}

เมธอด requestValue(for:dialog:) หยุดการดำเนินการชั่วคราวจนกว่า Siri จะได้รับการตอบกลับจากผู้ใช้ สร้างการแลกเปลี่ยนไปมาอย่างเป็นธรรมชาติแทนที่จะต้องการพารามิเตอร์ทั้งหมดตั้งแต่แรก

View Annotations API สำหรับการอ้างอิงบนหน้าจอ

View Annotations API ใหม่ใน iOS 27 ช่วยให้ผู้ใช้อ้างอิงองค์ประกอบ UI โดยตรงในคำสั่ง Siri โดยใช้วลีเช่น "รูปนี้", "อันที่สาม" หรือ "ข้อความนั้น" ความสามารถนี้ต้องการการ annotate view ของ SwiftUI ด้วยข้อมูลเชิงความหมาย

AnnotatedPhotoView.swiftswift
import SwiftUI
import AppIntents

struct PhotoGridView: View {
    let photos: [Photo]

    var body: some View {
        LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
            ForEach(photos) { photo in
                PhotoThumbnail(photo: photo)
                    // Annotate view for Siri reference
                    .appIntentAnnotation(
                        entity: PhotoEntity(photo: photo),
                        label: photo.title
                    )
            }
        }
    }
}

// Intent that accepts view-referenced entities
struct SharePhotoIntent: AppIntent {
    static var title: LocalizedStringResource = "Share photo"

    // Parameter resolved from on-screen annotation
    @Parameter(title: "Photo", supportsViewAnnotation: true)
    var photo: PhotoEntity

    func perform() async throws -> some IntentResult & ProvidesDialog {
        await ShareService.share(photo.id)
        return .result(dialog: "Photo shared.")
    }
}

เมื่อผู้ใช้พูดว่า "แชร์รูปนี้" ขณะดู grid Siri จะแก้ไข "นี้" เป็น PhotoEntity ที่กำลังโฟกัสหรือแตะล่าสุดผ่าน annotation นี้ขจัดอุปสรรคในการอธิบายรายการด้วยคำพูด

ความเข้ากันได้ของอุปกรณ์

View Annotations ต้องการ iOS 27 แต่เลเยอร์ App Intents ทำงานบนอุปกรณ์ iOS 27 ทุกเครื่องรวมถึง iPhone 11 และใหม่กว่า ฟีเจอร์ Apple Intelligence เช่นการรับรู้บนหน้าจอต้องการ iPhone 15 Pro หรือใหม่กว่า

การกำหนด App Entities สำหรับข้อมูล

App Entities แทน "คำนาม" ของแอปพลิเคชัน: วัตถุที่ intents ทำงานด้วย ช่วยให้ Siri เข้าใจ ค้นหา และจัดการข้อมูลแอป การสร้าง entities ที่มีประสิทธิภาพเป็นสิ่งจำเป็นสำหรับรูปแบบ การจัดการ state ของ SwiftUI ในแอปที่ขับเคลื่อนด้วย intent

TaskEntity.swiftswift
import AppIntents

// Internal data model
struct Task: Identifiable, Codable {
    let id: UUID
    var title: String
    var priority: TaskPriority
    var isCompleted: Bool
    var dueDate: Date?
}

// Entity exposed to the system
struct TaskEntity: AppEntity {
    // Required unique identifier
    var id: UUID

    // Displayable properties
    var title: String
    var priority: TaskPriority
    var isCompleted: Bool

    // Display configuration in the system
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Task"

    // Visual representation of the instance
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "(title)",
            subtitle: "(priority.rawValue)",
            image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
        )
    }

    // Default query for searching entities
    static var defaultQuery = TaskEntityQuery()

    // Initializer from internal model
    init(task: Task) {
        self.id = task.id
        self.title = task.title
        self.priority = task.priority
        self.isCompleted = task.isCompleted
    }
}

// Optimized entity query with search
struct TaskEntityQuery: EntityStringQuery {
    // Text search with service-side filtering
    func entities(matching string: String) async throws -> [TaskEntity] {
        TaskService.shared.search(query: string, limit: 10)
            .map { TaskEntity(task: $0) }
    }

    // Search by identifiers
    func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
        TaskService.shared.fetchTasks()
            .filter { identifiers.contains($0.id) }
            .map { TaskEntity(task: $0) }
    }

    // Limited suggestions for performance
    func suggestedEntities() async throws -> [TaskEntity] {
        TaskService.shared.fetchRecentTasks(limit: 5)
            .map { TaskEntity(task: $0) }
    }
}

โปรโตคอล EntityStringQuery เพิ่มความสามารถในการค้นหาข้อความ ช่วยให้ Siri ค้นหา entities ตามชื่อ เมธอด suggestedEntities() ป้อนข้อมูลให้กับอินเทอร์เฟซ Siri และ Shortcuts พร้อมตัวเลือกที่เกี่ยวข้อง

ใช้ AppEnum สำหรับค่าคงที่

ใช้ AppEnum สำหรับประเภทที่มีชุดค่าคงที่ (ลำดับความสำคัญ สถานะ) และ AppEntity สำหรับประเภทไดนามิกที่ผู้ใช้สร้าง (งาน บันทึก รายชื่อติดต่อ) การผสมกันทำให้เกิดความซับซ้อนที่ไม่จำเป็น

การสร้าง App Enums สำหรับค่าคงที่

App Enums เปิดเผยประเภทแบบ enumerated ให้กับระบบ ช่วยให้ Siri เสนอตัวเลือกตามบริบทพร้อมการแสดงผลภาพ

TaskPriority.swiftswift
import AppIntents

// Enum exposed to the system
enum TaskPriority: String, AppEnum, Codable {
    case low
    case medium
    case high

    // Type name displayed
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Priority"

    // Representation of each case
    static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
        .low: DisplayRepresentation(
            title: "Low",
            image: .init(systemName: "arrow.down.circle")
        ),
        .medium: DisplayRepresentation(
            title: "Medium",
            image: .init(systemName: "minus.circle")
        ),
        .high: DisplayRepresentation(
            title: "High",
            image: .init(systemName: "exclamationmark.circle")
        )
    ]
}

ไอคอน SF Symbols ทำให้การแสดงผลใน Shortcuts และคำแนะนำ Siri สมบูรณ์ยิ่งขึ้น ทำให้การเลือกเร็วกว่าการอธิบายด้วยคำพูด

พร้อมที่จะพิชิตการสัมภาษณ์ iOS แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

การใช้งาน AppShortcutsProvider

AppShortcutsProvider เปิดเผย App Shortcuts ให้กับระบบ ทำให้พร้อมใช้งานทันทีโดยไม่ต้องกำหนดค่าจากผู้ใช้ ทางลัดเหล่านี้ปรากฏใน Spotlight, Siri, Action Button และเวิร์กโฟลว์ agentic ของ Siri AI ใหม่

ShortcutsProvider.swiftswift
import AppIntents

// Provider declaring all app shortcuts
struct TaskAppShortcutsProvider: AppShortcutsProvider {
    // Maximum 10 shortcuts per application
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // Shortcut to create a task
        AppShortcut(
            intent: CreateTaskIntent(),
            phrases: [
                // The .applicationName placeholder is REQUIRED
                "Create a task with (.applicationName)",
                "New task in (.applicationName)",
                "Add a task to (.applicationName)"
            ],
            shortTitle: "Create Task",
            systemImageName: "plus.circle"
        )

        // Shortcut with dynamic parameter
        AppShortcut(
            intent: CompleteTaskIntent(),
            phrases: [
                "Complete (.$taskName) in (.applicationName)",
                "Mark (.$taskName) as done with (.applicationName)"
            ],
            shortTitle: "Complete Task",
            systemImageName: "checkmark.circle"
        )

        // Shortcut for smart conversational intent
        AppShortcut(
            intent: SmartTaskIntent(),
            phrases: [
                "Quick task (.applicationName)",
                "Remind me (.applicationName)"
            ],
            shortTitle: "Smart Task",
            systemImageName: "brain"
        )
    }
}

วลีเสียงต้องมี placeholder (.applicationName) เพื่อให้ Siri ระบุแอปเป้าหมาย พารามิเตอร์ไดนามิกเช่น (.$taskName) ช่วยให้คำสั่งตามบริบทเป็นไปได้

Intents ในวิดเจ็ตแบบโต้ตอบ

App Intents รวมเข้ากับ WidgetKit เพื่อสร้างวิดเจ็ตแบบโต้ตอบ การเข้าใจ รูปแบบการนำทางของ SwiftUI ช่วยเมื่อสร้างการเปลี่ยนผ่านจากวิดเจ็ตไปยังแอป

TaskWidgetIntents.swiftswift
import AppIntents
import WidgetKit

// Widget-optimized intent (fast execution)
struct ToggleTaskFromWidgetIntent: AppIntent {
    static var title: LocalizedStringResource = "Toggle task"

    @Parameter(title: "Task ID")
    var taskID: String

    init() {}

    init(taskID: UUID) {
        self.taskID = taskID.uuidString
    }

    // No dialog for widgets
    func perform() async throws -> some IntentResult {
        guard let uuid = UUID(uuidString: taskID) else {
            return .result()
        }

        TaskService.shared.toggleCompletion(taskId: uuid)

        // Immediate widget refresh
        WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")

        return .result()
    }
}

// Widget view with interactive button
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)
    }
}

วิดเจ็ตใช้ไวยากรณ์ Button(intent:) เพื่อเชื่อมต่อการโต้ตอบโดยตรงกับ App Intent โดยไม่ต้องเปิดแอป

การรวม Foundation Models Framework

iOS 27 เปิดตัว Foundation Models framework ที่ช่วยให้สามารถอนุมาน LLM บนอุปกรณ์ได้ แม้ว่า Foundation Models จะไม่มีการรวม Siri โดยตรง แอปสามารถใช้มันภายใน App Intents เพื่อประมวลผลภาษาธรรมชาติหรือสร้างการตอบกลับ

AIAssistedIntent.swiftswift
import AppIntents
import FoundationModels

struct SummarizeNotesIntent: AppIntent {
    static var title: LocalizedStringResource = "Summarize notes"

    @Parameter(title: "Topic")
    var topic: String

    func perform() async throws -> some IntentResult & ProvidesDialog {
        let notes = NoteService.shared.fetchNotes(matching: topic)
        let content = notes.map(.content).joined(separator: "

")

        // Use Foundation Models for on-device summarization
        let model = LanguageModel.default
        let summary = try await model.generate(
            prompt: "Summarize these notes in 2 sentences: (content)",
            maxTokens: 100
        )

        return .result(dialog: IntentDialog(summary))
    }
}

Foundation Models ทำงานประมาณ 30 โทเคนต่อวินาทีบนอุปกรณ์โดยไม่มีค่าใช้จ่ายต่อคำขอ Framework นี้ต้องการ iPhone 15 Pro หรือ iPhone 16 และใหม่กว่าสำหรับการประมวลผลบนอุปกรณ์

การย้ายจาก SiriKit ไปยัง App Intents

สำหรับแอปที่ยังใช้ SiriKit การย้ายตามกระบวนการที่มีโครงสร้าง Xcode 27 มีเครื่องมือ "Convert to App Intent" สำหรับการกำหนดค่าวิดเจ็ต แต่ handler ที่กำหนดเองต้องเขียนใหม่ด้วยตนเอง

swift
// Before: SiriKit Intent Handler
class CreateTaskIntentHandler: NSObject, CreateTaskIntentHandling {
    func handle(intent: CreateTaskIntent) async -> CreateTaskIntentResponse {
        guard let title = intent.taskTitle else {
            return CreateTaskIntentResponse(code: .failure, userActivity: nil)
        }

        let task = TaskService.shared.createTask(title: title)
        return CreateTaskIntentResponse.success(task: task)
    }
}

// After: App Intent (iOS 27)
struct CreateTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Create a task"

    @Parameter(title: "Task title")
    var taskTitle: String

    func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
        let task = TaskService.shared.createTask(title: taskTitle)
        return .result(value: TaskEntity(task: task))
    }
}

การย้ายลบไฟล์ .intentdefinition และ target Intent Extension ตรรกะ intent ทั้งหมดย้ายไปยัง target แอปหลักเป็น struct ของ Swift

การทดสอบ App Intents

Framework AppIntentsTesting ตรวจสอบ intents ผ่านโครงสร้างพื้นฐานของ Siri, Shortcuts และ Spotlight จริงโดยไม่ต้องใช้ mock

TaskIntentTests.swiftswift
import XCTest
import AppIntentsTesting
@testable import TaskApp

final class TaskIntentTests: XCTestCase {

    override func setUp() {
        super.setUp()
        TaskService.shared.reset()
    }

    func testCreateTaskIntent() async throws {
        // Given
        var intent = CreateTaskIntent()
        intent.taskTitle = "Test Task"
        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 Task")
        XCTAssertEqual(tasks.first?.priority, .high)
    }

    func testEntityQuery() async throws {
        // Given
        let task1 = TaskService.shared.createTask(title: "Task 1", priority: .low)
        let task2 = TaskService.shared.createTask(title: "Task 2", priority: .high)

        let query = TaskEntityQuery()

        // When
        let entities = try await query.entities(for: [task1.id, task2.id])

        // Then
        XCTAssertEqual(entities.count, 2)
    }

    func testStreamingIntent() async throws {
        // Given
        var intent = ExportDataIntent()
        intent.format = .json

        // When
        let result = try await intent.perform()

        // Then: verify completion dialog
        XCTAssertNotNil(result)
    }
}

การทดสอบตรวจสอบพฤติกรรมของ intent อย่างเป็นอิสระจากอินเทอร์เฟซระบบ จับ regression ก่อนการ deploy

Sources

รายการตรวจสอบการใช้งาน App Intents iOS 27

สำหรับแอปที่กำหนดเป้าหมาย iOS 27 App Intents ไม่ใช่ตัวเลือกอีกต่อไป Siri AI ประกอบการกระทำหลายขั้นตอนข้ามแอป และแอปที่ไม่มี intents ที่เผยแพร่จะถูกยกเว้นจากเวิร์กโฟลว์ agentic เหล่านี้

  • สร้าง AppIntents สำหรับการกระทำหลักของแอป
  • กำหนด AppEntities สำหรับข้อมูลที่จัดการได้
  • ใช้ AppEnum สำหรับประเภทแบบ enumerated
  • ใช้งาน AppShortcutsProvider พร้อมวลีเสียง
  • ปฏิบัติตามขีดจำกัดสูงสุด 10 App Shortcuts
  • รวม (.applicationName) ในทุกวลี
  • เพิ่ม View Annotations ให้กับ view ของ SwiftUI สำหรับการอ้างอิงบนหน้าจอ
  • ใช้งาน streaming responses สำหรับการดำเนินการที่ใช้เวลานาน
  • สนับสนุนการสนทนา multi-turn สำหรับเวิร์กโฟลว์ที่ซับซ้อน
  • ย้ายจาก SiriKit ก่อนที่หน้าต่างการยกเลิกจะปิด
  • ทดสอบ intents ด้วย framework AppIntentsTesting
  • แปลชื่อและคำอธิบาย

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

ชาเลนจ์ประจำวัน

คุณหาบั๊กใน iOS เจอไหม

โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

Anthony Fillion-Maillet

เขียนโดย

Anthony Fillion-Maillet

ผู้ก่อตั้ง SharpSkill

เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่

อัปเดตเมื่อ 20 สิงหาคม 2569

แท็ก

#app-intents
#siri-shortcuts
#ios
#swift
#apple-intelligence

แชร์

บทความที่เกี่ยวข้อง