こんにちは、n/aです。
今秋リリース予定のiOS 27では、Apple Intelligenceを活用した新しい「Siri AI」が登場します。
Siri AIでは、Siriとの会話を通して
- 友人がメッセージで教えてくれたおすすめのカフェを探す
- 過去のEメールから次の打ち合わせ予定を見つける
- 家族との夏休み旅行の写真を表示する
といったように、他のアプリの機能を自然に利用できるようになります。
自分のアプリがSiri AIとスムーズに連携できるかが、今後のユーザー体験に直結してきそうです。
そこで今回は、Siri AIに備える第一歩として、アプリに App Intents を追加していく流れを紹介します。
App Intentsとは
App Intentsは、アプリ外部(Siri、Spotlight、Shortcutsアプリ、ウィジェットなど)から、
アプリのアクションやコンテンツを使えるようにするためのフレームワークです。
基本となる要素は3つです。
- Intent: アプリが提供するアクションを定義
- Entity: アプリのデータオブジェクトをIntentから扱えるようにする専用のモデルクラス
- Shortcut: IntentをSiriやShortcutsアプリから呼び出せるようにする橋渡しの仕組み
この他に、AppSchemaやiOS 27から利用可能なAPIを使うことでよりSiri AIに最適化できますが、
まずはApp IntentsだけでもSiri AIとの基本的な連携の準備が整います。
題材となるアプリ


今回の題材は、よくあるシンプルなTodoアプリです。
- SwiftUI + SwiftData
- Todoの追加・一覧表示・完了切り替えができる
- 各Todoはタイトル・対応期限・完了状態を持つ
このアプリに、Siriやショートカットアプリから呼び出せるようにApp Intentsを追加していきます。
(Todoアプリそのものについては本記事では取り扱いません。)
作成するIntent
チュートリアルとして、Todoアプリの中でも特によく使う2つのアクションをIntent化します。
- Todoを追加する
- Todoの状態を完了にする
どちらも、アプリを開かなくてもSiriやショートカットから直接実行できるようにします。
Todoを追加するIntent
まずは「Todoを追加する」というアクションから、App Intentsの基本とショートカットへの登録までを実装していきます。
1. AddTodoIntent ― Todo追加のアクションを定義する
アプリ外部からユーザーが実行したいアクション1つに対して1つのIntentを定義します。AppIntent に準拠した構造体が、Siriやショートカットから呼び出せる「アクション」の実体です。
最小構成は title と perform() です。
今回はアプリ画面を開かずに実行するため、 openAppWhenRun は false にします。
import AppIntents
import SwiftData
struct AddTodoIntent: AppIntent {
static var title: LocalizedStringResource = "Todoを追加"
static var description = IntentDescription("新しいTodoを1件追加します。")
static var openAppWhenRun: Bool = false
@Parameter(title: "タイトル")
var titleText: String
@Parameter(title: "期限", default: Date.now.addingTimeInterval(3600*24))
var dueDate: Date
static var parameterSummary: some ParameterSummary {
Summary("\(\.$dueDate)までに「\(\.$titleText)」をやる")
}
@MainActor
func perform() async throws -> some IntentResult & ProvidesDialog {
let context = ModelContext(TodoModelContainer.shared)
let item = TodoItem(
title: titleText,
dueDate: dueDate
)
context.insert(item)
try context.save()
return .result(
dialog: "「\(titleText)」(期限:\(dueDate, format: .dateTime.year().month().day().hour().minute()))を追加しました。"
)
}
}💡 実装のポイント
実行時にユーザーに「タイトル」「期限」を入力してもらうため、入力項目と対応するプロパティに @Parameter を付与します。
期限(dueDate)にはデフォルト値(Intent実行時刻から24時間後)を用意しました。
これにより、実行時に期限の入力が省略されます。
Intentを実行するとタイトルの入力UIが表示されます。

parameterSummary では、 \.$dueDate や \.$titleText のように、パラメータのキーパスを文章の中に埋め込んでいます。
こうしておくと、Shortcutsアプリの編集画面でパラメータが以下のように文章として表示され、わかりやすくなります。

perform() の中では、受け取ったパラメータを使って TodoItem (Todo1件を表すアプリのモデルクラス)を作成し、SwiftDataに保存しています。
これはアプリを開いてTodoを追加するときと同じ処理です。
returnで返した文字列はIntentの実行結果として表示されます。

2. TodoShortcutsProvider ― Siriやショートカットへの橋渡し
作成したIntentをSiriやShortcutsアプリから呼び出しやすくするため、 AppShortcutsProvider を作成し、Intent毎に AppShortcut を登録します。
import AppIntents
struct TodoShortcutsProvider: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
return [
AppShortcut(
intent: AddTodoIntent(),
phrases: [
"\(.applicationName)でTodoを追加",
"新しいTodoを\(.applicationName)で作成"
],
shortTitle: "Todo追加",
systemImageName: "plus"
)
]
}
static var shortcutTileColor: ShortcutTileColor { .blue }
}💡 実装のポイント
AppShortcut の phrases に音声フレーズを登録しておくと、「(アプリ名)でTodoを追加」のような発話でSiriから直接実行できるようになります。\(.applicationName) の部分はアプリの実際の名前に置き換わります。
shortTitle と systemImageName でShortcutsアプリでの見た目をカスタマイズします。

ここまでで、App Intentsの一番基本的な形である「Intentを作り、パラメータを定義し、ショートカットに登録する」という流れを一通り実装できました。
Todoの状態を完了にするIntent
次に「指定したTodoの状態を完了にする」というアクションを作ります。
こちらは「アプリの中のどのデータを指しているか」をSiriに伝える必要があるので、先程より一段階複雑になります。
3. TodoEntity ― アプリデータをSiriやShortcutアプリで扱える形式にする
このアプリではTodoを TodoItem というモデルクラスで表現しますが、そのままではSiriやShortcutアプリに渡せません。
SiriやShortcutアプリが扱えるカスタムデータは AppEntity に準拠した型だけなので、 TodoItem に対応する TodoEntity を用意します。
import AppIntents
import SwiftData
struct TodoEntity: AppEntity {
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation(name: "Todo")
}
static var defaultQuery = TodoEntityQuery()
let id: UUID
let title: String
var dueDate: Date
let isDone: Bool
init(_ item: TodoItem) {
self.id = item.id
self.title = item.title
self.dueDate = item.dueDate
self.isDone = item.isDone
}
var displayRepresentation: DisplayRepresentation {
return DisplayRepresentation(
title: "\(title)",
subtitle: "期限: \(dueDate, format: .dateTime.month().day().hour().minute())"
)
}
}💡 実装のポイント
displayRepresentation はSiriやShortcutsアプリ上で TodoEntity をどう表示するかを指定します。
実際に表示すると以下のようになります。

defaultQuery に TodoEntityQuery() を指定していますが、これについては後ほど説明します。
4. MarkTodoDoneIntent ― Todo完了のアクションを定義する
完了したいTodo ( TodoEntity ) をパラメータとして受け取り、状態を変更するアクションを定義します。
import AppIntents
import SwiftData
struct MarkTodoDoneIntent: AppIntent {
static var title: LocalizedStringResource = "Todoを完了する"
static var description = IntentDescription("指定したTodoを完了にします。")
static var openAppWhenRun: Bool = false
@Parameter(title: "Todo")
var todo: TodoEntity
static var parameterSummary: some ParameterSummary {
Summary("\(\.$todo)を完了する")
}
@MainActor
func perform() async throws -> some IntentResult & ProvidesDialog {
let context = ModelContext(TodoModelContainer.shared)
let targetID = todo.id
let descriptor = FetchDescriptor<TodoItem>(
predicate: #Predicate { $0.id == targetID }
)
guard let item = try context.fetch(descriptor).first else {
throw TodoIntentError.notFound
}
item.isDone = true
try context.save()
return .result(dialog: "「\(item.title)」を完了しました。")
}
}
enum TodoIntentError: Swift.Error, CustomLocalizedStringResourceConvertible {
case notFound
var localizedStringResource: LocalizedStringResource {
switch self {
case .notFound:
return "指定されたTodoが見つかりませんでした。"
}
}
}💡 実装のポイント
ユーザーから受け取るパラメータの型を先程の TodoEntity にしています。perform() では TodoEntity の id を使って一致する TodoItem を検索し、状態を完了に変更します。
このIntentを実行すると、候補となるTodoが提示され、ユーザーはその中から1件を選択できます。
この時に候補となるTodoを決定するのが、先程 TodoEntity の defaultQuery に指定した TodoEntityQuery です。
5. TodoEntityQuery ― Entityを検索する
EntityQuery は、Entityを選択するパラメータに対して「候補として何を出すか」「IDから実データをどう引くか」を決める仕組みです。
import AppIntents
import SwiftData
struct TodoEntityQuery: EntityQuery {
@MainActor
func entities(for identifiers: [TodoEntity.ID]) async throws -> [TodoEntity] {
let context = ModelContext(TodoModelContainer.shared)
let descriptor = FetchDescriptor<TodoItem>(
predicate: #Predicate { identifiers.contains($0.id) }
)
return try context.fetch(descriptor).map(TodoEntity.init)
}
@MainActor
func suggestedEntities() async throws -> [TodoEntity] {
let context = ModelContext(TodoModelContainer.shared)
let descriptor = FetchDescriptor<TodoItem>(
predicate: #Predicate { $0.isDone == false },
sortBy: [SortDescriptor(\.dueDate, order: .forward)]
)
return try context.fetch(descriptor).map(TodoEntity.init)
}
}💡 実装のポイント
suggestedEntities() は、SiriやShortcutアプリの編集画面でユーザーに提示する候補一覧を返します。
ここでは未完了のTodoを期限が近い順に返しています。
TodoShortcutsProviderへの登録はAddTodoIntentと同様です。
ここまでが、アプリ内のデータを取得・操作する基本的なIntentの実装になります。
発展編 ― 結果表示をカスタマイズする
上記の AddTodoIntent や MarkTodoDoneIntent は、メッセージだけを結果として返していますが、
App Intentsでは実行結果としてSwiftUIのカスタムビューを返すこともできます。
例えば「未完了のTodo一覧を取得する」Intentであれば、List形式のカスタムビューを結果として表示させるとわかりやすくなります。
@MainActor
func perform() async throws -> some IntentResult & ReturnsValue<[TodoEntity]> & ProvidesDialog & ShowsSnippetView {
let context = ModelContext(TodoModelContainer.shared)
let descriptor = FetchDescriptor<TodoItem>(
sortBy: [SortDescriptor(\.dueDate, order: .forward)]
)
var items = try context.fetch(descriptor)
items = items.filter { $0.isDone == false }
let entities = items.map(TodoEntity.init)
return .result(
value: entities,
dialog: "未完了のTodoは\(entities.count)件です。",
view: ResultView(items: items)
)
}perform() の戻り値に ShowsSnippetView を追加し、任意のカスタムビュークラス(ここでは ResultView )を渡します。ResultView は例えば以下のように実装します。期限が過ぎているものは赤字で表示します。
struct ResultView: View {
var items: [TodoItem]
var body: some View {
ForEach(items) { item in
VStack(alignment: .leading, spacing: 4) {
Text(item.title)
.foregroundStyle(.primary)
HStack(spacing: 0) {
Text(item.dueDate, format: .dateTime.month().day().hour().minute())
.foregroundStyle(isOverdue(item) ? .red : .secondary)
Spacer()
}
.font(.caption2)
Divider()
.foregroundStyle(.separator)
.padding(.top, 4)
}
.padding(.top, items.first == item ? 8 : 0)
.padding(.leading, 20)
}
}
private func isOverdue(_ item: TodoItem) -> Bool {
!item.isDone && item.dueDate < .now
}
}実際の表示は以下のようになります。

まとめ
App Intentsの導入は、iOS 26の今からできるSiri AIへの準備であるとともに、
多様な経路からアプリを利用しやすくし、UXを向上させる取り組みでもあります。
まずはアプリの主要な機能からApp Intentsを導入してみるのがおすすめです。
関連記事
-
第1回 Visual C++で作成したDLL内のクラスをC#で利用する方法
こんにちは、ILCです。 Visual C++ (以下 VC++)で作成されたDynamic...
公開日:2024.01.19 更新日:2024.01.19
tag : Windows
-
-
-
【新機能探訪】Android 13から導入された『アプリごとの言語設定』
こんにちは、KNSKです。よろしくお願いします。 今回は Android13の新機能である『...
公開日:2022.12.09 更新日:2022.12.09
tag : スマートデバイス
-
第1回 ラズパイを使用したBLE通信 ~ ディスプレイ、キーボード、マウスを接続しないで設定 前編 ~
こんにちは、GTです。よろしくお願いします。 最近業務でラズパイのBluetooth機能を使...
公開日:2021.12.24 更新日:2021.12.24
tag : Bluetooth Raspberry Pi
-
こんにちは。WwWです。 システム系の開発をしていると様々な問題が起こります。 そこで今回は...
公開日:2023.04.28 更新日:2023.04.28