iOS Widget 対応でやったこと

SwiftUI を使って個人開発しているアプリで Widget 対応をしたので、やったことを忘れないようにメモしておきます。

ログの整備

ふつうのアプリ開発を趣味でやっている範囲だとデバッガと print で不自由なく開発できてしまうんですが、 widget 開発する上ではちゃんとログを出力しないと思い通りに動かないときそれだと何もわからんということがあるんですよね。具体的には、アプリと widget 両方の動作を同時に追いたいときや widget をタップして deeplink でアプリに遷移するときなどは デバッガも print も役に立ちません。

そこで、 Unified Logging を使って要所でログを出すようにしました。以下のビデオにログの概念、出力の方法、参照の方法などとりあえず必要なこと全てがまとまってます。

developer.apple.com

deeplink 対応

続いて widget をタップしたときの DeepLink の対応です。今回 widget 対応したのが SwiftUI ライフサイクルのアプリだったので、以下のように関連するいくつか View で onOpenURL を使って対処しました。

www.donnywals.com

UIKit だと DeepLink をハンドリングする1つのコンポーネントがいて、そいつが DeepLink が指示するように ViewController を重ねていくのがよくある DeepLink 対応だと思うんですが、SwiftUI では View 自身が DeepLink をハンドリングしないといけないのでかなりマインドセットが違って苦労しました。

SwiftUI でも以下の記事のように View の階層自体をグローバルな EnvironmentObject で持っていれば、その object の状態を変えることでアプリの表示を一箇所から完全にコントロールできるので UIKit と同様のハンドリングが可能になるのでそれも試してみたのですが、DeepLink に関係なくルーティング自体を追加・改修するコストが高すぎてつらくなったのでやめました。

nalexn.github.io

keychain access group の設定

ログインさせるアプリの widget では、アプリ本体と widget でクレデンシャルを共有する必要があると思います。そのために keychain access group を設定しました。以下のドキュメントを見れば基本的なことはわかります。なんとなく難しそうなイメージがあったけど単に entitlements を追加してグループ名を決めるだけだった。

developer.apple.com

細かい点で以下の記事に助けられました。

dev.classmethod.jp

widget 追加

widget を追加します。以下の一連の WWDC セッションを見れば何をやるかはだいたいわかります。

developer.apple.com

developer.apple.com

developer.apple.com

レイアウトでは普通に SwiftUI を書けばいいだけなので一瞬なんですが、いくつかはまりどころがありました。

まず、画像を非同期で読み込めないので以下の記事のように Data(contentsOf: url) を使う必要があります。

note.com

また、widget と アプリから共有の embedded framework を参照することになったのですが contains disallowed nested bundles というエラーになってしまいました。Build Phases に以下のスクリプトを追加することで回避できましたが、正直なんで直ったのかは雰囲気でしか理解してません。iOS のこの辺の話難しいんですよね...ちゃんとキャッチアップしたい。

stackoverflow.com

まとめ

widget 対応は意外と簡単なところも意外と大変なところもある。人生と同じですね(?)