1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

InfocomAdvent Calendar 2022

Day 11

iOS16 AppIntentを試してみる

Posted at

iOS16 AppIntentを自作アプリに組み込んでみました。
公開中のアプリはこれです。
https://apps.apple.com/jp/app/買い物todoリスト/id1547720019

買い物に特化した簡単なToDo管理的なものが欲しくて作成したアプリです。
リストに品物を追加するときの操作ステップをAppIntentを使って減らせないか試してみました。

現状、品物追加のためには、アプリを起動して「品物を入力」ボタンを押す必要があります。この操作が面倒です。
qiita_AppIntent3.png

今回やりたかったことはAppIntentのダイアログから直接品物を入力することです。
しかし、実際に実装した結果は・・・
dialog.png
ダイアログにTextFieldは利用できないようです。残念。

この失敗例の実装は以下の通りです。
実装自体はとても簡単で良い感じです。

import SwiftUI
import AppIntents

struct ItemInputView: View {
    @State private var item = ""

    var body: some View {
        VStack {
            Spacer()
            Text("品物を入力").padding()
            Spacer()
            TextField("品物", text: $item)
        }
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        ItemInputView()
    }
}

struct OpenItemInputIntent: AppIntent {
    static let title: LocalizedStringResource = "品物を入力"
    static var openAppWhenRun: Bool = false
    
    @MainActor
    func perform() async throws -> some IntentResult {
        return .result(view:ItemInputView())
    }
}

仕方ないので方針を変えます。
今回はショートカットから実行した時はアプリを起動して品物入力画面を開くようにします。
Observerパターンを利用して、AppIntent起動時に画面を開くようにします。

処理の流れは以下の通りです。

  • ショートカットアプリからAppIntentが実行される
  • AppIntentからアプリの制御クラスに画面遷移をリクエスト
  • 制御クラスがObserverに画面オープンを通知
  • Observer(ViewController)が通知を受け取り、入力画面に遷移する

クラス図です。
図の作成にはEdrawMaxを利用しました。
https://www.edrawsoft.com/jp/edraw-max/

class_diagram.png

シーケンス図はこちら。
sequence_diagram.png

AppIntentの実装です。

struct OpenItemInputIntent: AppIntent {
    static let title: LocalizedStringResource = "品物を入力"
    static var openAppWhenRun: Bool = true
    
    @MainActor
    func perform() async throws -> some IntentResult {
        let manager = ItemManager.sharedManager
        manager.notifyItemInputRequest()
        return .result()
    }
}

Observerのインターフェースです。

protocol ItemInputRequestObserver {
    func notify()
}

制御クラスのObserver制御の実装です。

class ItemManager {
〜
    // MARK: Item input request observer Control
    func notifyItemInputRequest() {
        if let observer = self.itemInputRequestObserver {
            observer.notify()
        }
    }
    
    func setItemInputRequestObserver(observer: ItemInputRequestObserver) {
        self.itemInputRequestObserver = observer
    }
    
    func removeItemInputRequestObserver() {
        self.itemInputRequestObserver = nil
    }

〜

Observer(TodoTableViewController)側の入力画面への遷移処理です。
(一旦元の画面に戻してから入力画面に遷移するSegueをコールしています)

〜
class TodoTableViewController: UITableViewController, UNUserNotificationCenterDelegate, ItemInputRequestObserver {
〜
    override func viewDidLoad() {
        〜
        // 入力画面オープンの通知を受け取る
        self.itemManager.setItemInputRequestObserver(observer: self)
    }
〜
    func notify() {
        self.navigationController?.popToRootViewController(animated: false)
        self.performSegue(withIdentifier: "item_input", sender: nil)
    }

参考までにショートカット登録の流れをスクショで載せておきます。
qiita_AppIntent1.png
qiita_AppIntent2.png

デスクトップに作成したショートカットから直接入力画面を開けるようになりました・・・が、便利になった気がしない・・・。
qiita_AppIntent4.png

余談ですが、アプリをXcodeから実機にインストールした時に、アプリがショートカットに表示されず少し焦りました。実機を再起動したところ、表示される様になりました。

サンプルコード

今回のサンプルコードです。
https://github.com/fugasat/shopping_list

公開中のアプリです。
https://apps.apple.com/jp/app/買い物todoリスト/id1547720019

参考サイト

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?