Siri 連携を実装しようと SiriKit のドキュメントを読み始めたとき、そのボイラープレートの多さに一度諦めかけた経験があります。INIntent、INExtension、Intent Definition ファイル……やることが多すぎて本筋の開発が進まありません。そんなとき App Intents フレームワークの存在を改めて調べ直し、Antigravity と組み合わせることで驚くほどシンプルに実装できると気づきました。
ここでは実際に Antigravity を使って App Intents を実装した体験をもとに、Siri とショートカットアプリへの連携を最速で構築する方法をご紹介します。
App Intents が SiriKit と根本的に違う理由
SiriKit では Intent Definition ファイル(.intentdefinition)を GUI で編集し、Xcode に自動生成させたクラスを継承する構造でした。コードとファイルが分離していて変更のたびに再ビルドが必要になるため、Antigravity のような AI コーディングツールとの相性はあまりよくありませんでした。「ここを修正して」と指示しても、生成ファイルと手書きコードが混在して競合が起きやすかったのです。
App Intents(iOS 16 以降)はその構造を一変させました。すべて Swift コードで完結し、プロトコルに準拠するだけで Siri とショートカットアプリが自動認識します。
- SiriKit(旧方式): Intent Definition ファイル + Xcode 生成コード + App Extension の3点セット
- App Intents(現在): Swift コードのみ、単一ターゲットで完結、コード生成不要
この違いが Antigravity との相性を大きく改善しています。コードが1箇所に集まっているため、Antigravity に「App Intent を追加して」と指示するだけで、必要なプロトコルとメタデータを含む完全なコードを生成してくれます。
Antigravity でのプロジェクトセットアップ
まず Antigravity でプロジェクトを開き、以下のプロンプトを入力します。ターゲットバージョンを明示するのが重要です(iOS 16 と 17 以降で API の一部が異なるため)。
iOS アプリに App Intents フレームワークを追加したい。
ターゲットは iOS 17 以上。まず「タスクを追加する」という
基本的な App Intent を1つ作ってほしい。EntityQuery は今は不要。
Antigravity はプロジェクト構造を確認した上で、以下のようなコードを生成します。
import AppIntents
// タスクを追加する App Intent
struct AddTaskIntent: AppIntent {
static var title: LocalizedStringResource = "タスクを追加"
static var description = IntentDescription("新しいタスクをアプリに追加します")
@Parameter(title: "タスク名")
var taskName: String
@Parameter(title: "期限", default: nil)
var dueDate: Date?
func perform() async throws -> some IntentResult & ProvidesDialog {
// TaskRepository は自分のアプリのデータ層に合わせて変更
let task = await TaskRepository.shared.add(name: taskName, dueDate: dueDate)
return .result(dialog: "\(task.name)を追加しました")
}
}このコードをビルドして iPhone のショートカットアプリを開くと、「タスクを追加」アクションが即座に表示されます。Extension も Intent Definition ファイルも不要です。初めて試したとき、あまりの手軽さに拍子抜けしました。
パラメータと AppEnum で選択肢を提示する
App Intents の真価は、パラメータの型安全性にあります。文字列だけでなく AppEnum を使うと、Siri とショートカットアプリで選択肢をリストとして提示できます。
Antigravity に「優先度を選択できるようにして。高・中・低の3択」と指示するとこんなコードが生成されます。
enum TaskPriority: String, AppEnum {
case high = "high"
case medium = "medium"
case low = "low"
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "優先度")
static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
.high: "高",
.medium: "中",
.low: "低",
]
}
struct AddTaskIntent: AppIntent {
static var title: LocalizedStringResource = "タスクを追加"
static var description = IntentDescription("新しいタスクをアプリに追加します")
@Parameter(title: "タスク名")
var taskName: String
@Parameter(title: "優先度", default: .medium)
var priority: TaskPriority
func perform() async throws -> some IntentResult & ProvidesDialog {
let task = await TaskRepository.shared.add(
name: taskName,
priority: priority.rawValue
)
return .result(
dialog: "\(task.name)(優先度: \(priority))を追加しました"
)
}
}Siri で「高優先度でレポート提出を追加して」と話しかけると、パラメータが自動的にマッピングされます。英語でも「Add report deadline with high priority」で同様に動作します。
AppShortcutsProvider でショートカットアプリに起動フレーズを登録する
App Intents だけではショートカットアプリのアクションとして表示されますが、「起動フレーズ」は別途登録が必要です。AppShortcutsProvider を使うと、アプリインストール直後から Siri で呼び出せるようになります。
struct TaskAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: AddTaskIntent(),
phrases: [
// \(.applicationName) はアプリ名に自動置換される
"\(.applicationName)にタスクを追加",
"\(.applicationName)で\(.taskName)を追加"
],
shortTitle: "タスクを追加",
systemImageName: "plus.circle"
)
}
}AppShortcutsProvider を実装したクラスに @main はつけず、Xcode のビルドターゲットに含めるだけで自動的に登録されます。ここを省略するケースが多いので注意が必要です。
Antigravity が生成するコードのよくある落とし穴
実際に Antigravity を使って開発したときにハマったポイントを3つ挙げます。知っておくと時間を大幅に節約できます。
perform() の中で UI 更新を直接呼ぶとクラッシュする
App Intents の perform() はバックグラウンドスレッドで実行されます。Antigravity はしばしば perform() 内で UIKit の処理を直接呼び出すコードを生成しますが、これはクラッシュの原因になります。
// ❌ Antigravity が生成しがちなコード(クラッシュの原因)
func perform() async throws -> some IntentResult {
viewModel.updateUI()
return .result()
}
// ✅ MainActor.run でラップする
func perform() async throws -> some IntentResult {
await MainActor.run {
viewModel.updateUI()
}
return .result()
}iOS バージョンの指定を省略すると古い API が使われる
ターゲットバージョンを最初のプロンプトで明示しないと、Antigravity が iOS 16 向けの少し古い API を使うことがあります。特に IntentResult の返り値型は iOS 17 で強化されているため、プロンプトに「iOS 17 以上」と書く習慣をつけましょう。
@Parameter に Optionals を使うときの初期値設定
@Parameter(title: "期限", default: nil) のように nil を初期値にすると、ショートカットアプリ上でパラメータが必須扱いになるケースがあります。任意パラメータにするには requestDisambiguationDialog ではなく明示的に省略可能であることをドキュメント化すると安全です。
Antigravity との Swift 開発
Siri とショートカットアプリでのテスト
シミュレーターではなく実機でのテストを強くおすすめします。Siri の音声認識精度が異なるため、日本語の発話フレーズ調整は実機でないと正確に確認できません。
テスト手順としては次のような流れになります。
- 実機でビルド・インストール
- 設定アプリ → Siri と検索 →
[アプリ名]からショートカットを追加 - 「ヘイ Siri、
[フレーズ]」でテスト - ショートカットアプリでアクションを検索して動作確認
Live Activities や Dynamic Island と App Intents を組み合わせた高度な UI パターンについては、ライブアクティビティと Dynamic Island の実装ガイドを合わせてご参照ください。また、ウィジェットとの連携についてはWidgetKit ホームウィジェット開発ガイドもお役に立てると思います。
App Intents は Siri 連携だけでなく、iOS 26 の Spotlight 検索やフォーカスフィルターにも活用できる可能性があります。今回の実装は最小限の入り口ですが、まず1つの Intent を動かしてみることが大事だと思います。
今日の記事のコードをそのまま Antigravity に貼り付けて、自分のアプリに Intent を追加してみてください。Siri が初めて反応してくれた瞬間の手応えは、実装の手間を上回るものがあります。