2K Views
November 14, 24
スライド概要
「【Sansan × YUMEMI】iOSランチタイムLT」での発表資料です。
https://yumemi.connpass.com/event/333140/
フリーランスiOSエンジニア 「エンジニアと人生」コミュニティ主宰
App Intents はじめの三歩
自己紹介 • 堤 修一 • @shu223 (GitHub, Zenn, Qiita, note, Docswell, 𝕏, YouTube, Podcast, etc...) • 書籍(商業出版4冊、個人出版多数 @BOOTH):
App Intents とは • アプリの「意図(インテント)」をシステムに伝えるもの • アプリの機能をアプリ外の様々な場所から使えるようになる
App Intentsに「今」取り組むモチベーション App Intents対応 ≒ Apple Intelligence対応 Siriがアプリの機能を使ってくれるようになる(iOS 18.?)
どの機能をインテントとして切り出せばいいの? → 全部
アシスタントスキーマ(Assistant Schemes) の話 • 概要: Apple Intelligenceに対応する • 11/19開催のイベントで実践的な話をする予定
本発表の位置付け • App Intentsの概念とApple Intelligenceの関係はわかった • 「全機能切り出そう」というAppleの言い分もわかった • でもどこから始めたらいいの? → はじめの三歩目 ぐらいまでをナビゲーションする 1 1 一歩目だけだとその先に困る、三歩ぐらいやるとユーザー体験として意味のあるインテントがつくれるようになる)
一歩目 アプリを開くだけのイン テント
// AppIntentに準拠
struct OpenAppIntent: AppIntent {
// タイトルは必須
static var title: LocalizedStringResource = "Open Hoge"
// インテント実行時にアプリを起動する
static var openAppWhenRun: Bool = true
// インテント実行時の処理
func perform() async throws -> some IntentResult {
// 何もしない
}
}
return .result()
• 既存実装に手をいれる必要がない • 実質5行程度
動作確認 • インテント ≒ (ショートカットアプ リにおける)アクション • アプリをインストールするだけでリ ストに出てくるようになる
二歩目 入力を持つインテント
動画ファイルを入力に受け取ってアプリを開くインテント インテント(アクション)を実行 → 動画選択 → 選択した動画 ファイルを入力としてアプリ起動
struct TrimSilenceIntent: AppIntent {
static var title: LocalizedStringResource = ...
static var openAppWhenRun: Bool = true
// 入力パラメータとして動画ファイルを受け取る
@Parameter(title: "Video File", supportedTypeIdentifiers: ["public.movie"])
var video: IntentFile
@MainActor
func perform() async throws -> some IntentResult {
// 動画ファイルのURLにアクセス
guard let videoURL = video.fileURL else { ... }
// 実際の処理
...
}
}
return .result()
@Parameter プロパティラッパー @Parameter(title: "Video File", supportedTypeIdentifiers: ["public.movie"]) var video: IntentFile これを指定したプロパティは、インテントのパラメータ(= ア クションのパラメータ)としてショートカットアプリに表示さ れるようになる
三歩目 出力を持つインテント
動画から音声を抽出するインテント インテント(アクション)を実行 → 動画選択 → 選択した動画 ファイルを入力 → 音声を抽出(バックグラウンド処理) → 音声ファイルを出力
struct ExtractAudioIntent: AppIntent {
static var title: LocalizedStringResource = ...
static var description = ...
@Parameter(title: "Video File", supportedTypeIdentifiers: ["public.movie"])
var video: IntentFile
// IntentFile型を返すことを明示
func perform() async throws -> some IntentResult & ReturnsValue<IntentFile> {
guard let videoURL = video.fileURL else { ... }
let audioFileUrl: URL = ...
// 動画から音声データを抽出して audioFileUrl に保存
...
// 音声ファイルを `IntentFile` として出力
let intentFile = IntentFile(fileURL: audioFileUrl)
}
}
return .result(value: intentFile)
差分のある perform() メソッドだけ抜粋
// IntentFile型を返すことを明示
func perform() async throws -> some IntentResult & ReturnsValue<IntentFile> {
guard let videoURL = video.fileURL else { ... }
let audioFileUrl: URL = ...
// 動画から音声データを抽出して audioFileUrl に保存
...
// 音声ファイルを `IntentFile` として出力
let intentFile = IntentFile(fileURL: audioFileUrl)
}
return .result(value: intentFile)
ポイント1: ReturnsValue の型をちゃんと指定する before func perform() async throws -> some IntentResult エラーになる: Fatal error: perform() returned types not declared in method signature
ポイント1: ReturnsValue の型をちゃんと指定する after func perform() async throws -> some IntentResult & ReturnsValue<IntentFile>
ポイント2: IntentFile 型でファイル出力
ReturnsValue<URL> でファイルURLを出力しても、その音声
を再生できない
func perform() async throws -> some IntentResult & ReturnsValue<URL> {
...
}
return .result(value: audioFileUrl)
ポイント2: IntentFile 型でファイル出力
ReturnsValue<IntentFile> で、ファイルを出力する
func perform() async throws -> some IntentResult & ReturnsValue<IntentFile> {
...
let intentFile = IntentFile(fileURL: audioFileUrl)
}
return .result(value: intentFile)
→ 後段のアクション(例:サウンドを再生 / Play sound)
で、抽出した音声ファイルを利用可能に
まとめ • 一歩目:アプリを起動するだけのインテント • 既存実装に手をいれる必要がない • 実質5行程度 • 二歩目:入力を持つインテント • @Parameter プロパティラッパー • 三歩目:出力を持つインテント