App Intents 2.0とSiri Shortcuts: iOS 27自動化ガイド
iOS 27向けApp Intents 2.0とSiri Shortcutsの完全ガイドです。ストリーミングレスポンス、マルチターン会話、View Annotationsの構築、Foundation Modelsとの統合を解説します。

App Intents 2.0とSiri Shortcutsは、iOS 27でサードパーティアプリがSiriと統合するための唯一の方法です。WWDC 2026で、AppleはSiriKitを非推奨とし、App Intentsを音声インタラクション、Spotlight検出、自動化ワークフローの必須フレームワークとして位置づけました。
本記事では、iOS 27向けのApp IntentsとSiri Shortcutsの完全な作成方法を、基本概念からストリーミングレスポンス、マルチターン会話、View Annotations APIまで紹介します。
App Intents 2.0フレームワークを理解する
iOS 16で最初に導入され、WWDC 2026でバージョン2.0に拡張されたApp Intentsフレームワークは、システムが発見可能なアクションを構築するためのSwiftネイティブな宣言的フレームワークです。iOS 27では4つの主要な追加機能があります。長時間操作のためのストリーミングレスポンス、マルチターン会話のフォローアップ、画面上の要素を参照するためのView Annotations、トレーニングフレーズなしでセマンティックな理解を可能にするApp Schemasです。
WWDC 2026で、Appleは正式にSiriKitを非推奨とし、App IntentsをSiriがサードパーティアプリと対話する唯一の方法としました。既存のSiriKitコードは非推奨の警告付きでコンパイルが継続されますが、Appleは削除までの2〜3年のサポート期間を示しています。
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は@Parameterプロパティラッパーでパラメータを宣言し、Siriが不足している値を会話的に要求できるようにします。perform()メソッドはビジネスロジックを実行し、型付けされた結果を返します。型システム自体が、Siri AIが動的に発見するスキーマとして機能します。
長時間実行アクションのためのストリーミングレスポンス
App Intents 2.0はストリーミングレスポンスを導入し、intentが完了まで待つのではなく、実行中に進捗を報告できます。この機能は、ファイルアップロード、データ同期、複雑な計算など、ユーザーにフィードバックが必要なシナリオに対応します。
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)."
)
}
}ストリーミングレスポンスにより、数秒以上かかる操作中に自然なフィードバックが可能となり、ユーザーを無音で待たせることなくエンゲージメントを維持します。
マルチターン会話フォローアップ
マルチターン会話により、Siriは1つのセッション内で明確化の質問をし、対話を継続できます。この機能により、App Intentsは単発のコマンドから会話型ワークフローへと変化します。
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: .,
dialog: "What should the task be called?"
)
let taskPriority = try await priority ?? requestValue(
for: .,
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: .,
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で新しく導入され、ユーザーが「この写真」「3番目のもの」「あのメッセージ」といったフレーズでSiriコマンド内のUI要素を直接参照できるようにします。この機能にはSwiftUIビューにセマンティック情報を注釈付けする必要があります。
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.")
}
}ユーザーがグリッドを見ながら「この写真を共有して」と言うと、Siriは注釈を通じて「これ」を現在フォーカスされている、または最後にタップされたPhotoEntityに解決します。これにより、アイテムを言葉で説明する手間がなくなります。
View AnnotationsにはiOS 27が必要ですが、App Intentsレイヤー自体はiPhone 11以降を含むすべてのiOS 27デバイスで動作します。画面認識などのApple Intelligence機能にはiPhone 15 Pro以降が必要です。
データのためのApp Entitiesを定義する
App Entitiesはアプリケーションの「名詞」、つまりintentが操作対象とするオブジェクトを表します。これによりSiriはアプリのデータを理解し、検索し、操作できます。効果的なentityの構築は、intent駆動型アプリにおけるSwiftUI状態管理パターンにとって不可欠です。
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: /bin/bash) }
}
// Search by identifiers
func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
TaskService.shared.fetchTasks()
.filter { identifiers.contains(/bin/bash.id) }
.map { TaskEntity(task: /bin/bash) }
}
// Limited suggestions for performance
func suggestedEntities() async throws -> [TaskEntity] {
TaskService.shared.fetchRecentTasks(limit: 5)
.map { TaskEntity(task: /bin/bash) }
}
}EntityStringQueryプロトコルはテキスト検索機能を追加し、Siriが名前でentityを見つけられるようにします。suggestedEntities()メソッドはSiriとショートカットのインターフェースに関連するオプションを提供します。
固定値の集合を持つ型(優先度、ステータス)にはAppEnumを使用し、ユーザーが作成する動的な型(タスク、メモ、連絡先)にはAppEntityを使用します。これらを混在させると不要な複雑さが生じます。
固定値のためのApp Enumsを作成する
App Enumsは列挙型をシステムに公開し、Siriがビジュアル表現を伴う文脈に応じた選択肢を提供できるようにします。
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アイコンはショートカットおよびSiriの提案における表示を豊かにし、言葉で説明するよりも迅速な選択を可能にします。
iOSの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
AppShortcutsProviderを実装する
AppShortcutsProviderはApp Shortcutsをシステムに公開し、ユーザー設定なしで即座に利用可能にします。これらのショートカットはSpotlight、Siri、Action Button、そして新しいSiri AIエージェントワークフローに表示されます。
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 (.) in (.applicationName)",
"Mark (.) 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"
)
}
}音声フレーズには、Siriが対象アプリを識別するために(.applicationName)プレースホルダーを含める必要があります。(.)のような動的パラメータは文脈に応じたコマンドを可能にします。
インタラクティブウィジェットでのintents
App IntentsはWidgetKitと統合し、インタラクティブウィジェットを作成します。SwiftUIナビゲーションパターンを理解すると、ウィジェットからアプリへの遷移構築に役立ちます。
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フレームワーク統合
iOS 27はFoundation Modelsフレームワークを導入し、オンデバイスでのLLM推論を可能にします。Foundation ModelsはSiriとの直接統合はありませんが、App Intents内で自然言語処理やレスポンス生成に使用できます。
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トークンの速度で動作し、リクエストごとのコストはゼロです。このフレームワークはオンデバイス処理にiPhone 15 ProまたはiPhone 16以降が必要です。
SiriKitからApp Intentsへの移行
まだSiriKitを使用しているアプリの場合、移行は構造化されたプロセスに従います。Xcode 27はWidget設定用の「Convert to App Intent」ツールを提供していますが、カスタムハンドラは手動で書き直す必要があります。
// 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ファイルとIntent Extensionターゲットが不要になります。すべてのintentロジックはSwift構造体としてメインアプリターゲットに移動します。
App Intentsをテストする
AppIntentsTestingフレームワークは、モックなしで実際のSiri、ショートカット、Spotlightインフラストラクチャを通じてintentsを検証します。
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の動作を検証し、デプロイ前にリグレッションを検出します。
Sources
- Apple WWDC 2026 Announcements : App Intents 2.0とSiriKit非推奨
- Swift.org Blog : Swift 6.3および6.4リリース
- Apple Developer News : iOS 27 SDK要件とFoundation Modelsフレームワーク
- iOS 27 App Intents Developer Guide : ストリーミングレスポンスとView Annotationsの詳細
App Intents iOS 27実装チェックリスト
iOS 27をターゲットとするアプリにとって、App Intentsはもはや任意ではありません。Siri AIはアプリ間でマルチステップアクションを構成し、公開されたintentsのないアプリはこれらのエージェントワークフローから除外されます。
- アプリの主要なアクションのためのAppIntentsを作成する
- 操作可能なデータのためのAppEntitiesを定義する
- 列挙型にはAppEnumを使用する
- 音声フレーズを伴うAppShortcutsProviderを実装する
- 最大10個のApp Shortcutsの上限を遵守する
- すべてのフレーズに
(.applicationName)を含める - 画面上の参照のためにSwiftUIビューにView Annotationsを追加する
- 長時間実行操作のためにストリーミングレスポンスを実装する
- 複雑なワークフローのためにマルチターン会話をサポートする
- 非推奨期間が終了する前にSiriKitから移行する
- AppIntentsTestingフレームワークでintentsをテストする
- タイトルと説明をローカライズする
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
iOS のバグを見つけられますか
実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

執筆
Anthony Fillion-MailletSharpSkill 創業者
10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。
2026年8月20日 更新
タグ
共有
関連記事

WidgetKit iOS 17-26: App Intentsによるインタラクティブウィジェット構築
WidgetKitとApp IntentsでインタラクティブなiOSウィジェットを作成する完全ガイド。ボタン、トグル、アニメーション、iOS 26 Liquid Glassマテリアル、WWDC 2026アップデートを解説。

Swift における Combine vs async/await:段階的な移行パターン
Swift で Combine から async/await への移行を進めるための完全ガイド:段階的な戦略、ブリッジングパターン、iOS コードベースにおけるパラダイムの共存。

2026年のiOSアクセシビリティ面接質問: VoiceOverとDynamic Type
iOS面接に向け、VoiceOver、Dynamic Type、セマンティックtraits、監査などアクセシビリティの重要質問を解説します。