
アルを iOS 14 のウィジェットに対応した際いろいろハマったので共有します
アル エンジニアの小川です、こんにちは。最近はフロントエンド・バックエンドの開発をメインでやっております。
さて、先日久々に iOS アプリを触る機会があり、アルのiOSアプリを話題のウィジェットに対応しました。このウィジェットは、ユーザーがフォローしているマンガの新刊情報を表示できるというものです。
今回はその際の気付きをメモしておきたいと思います。
なお、ここではウィジェットの基本的な作り方については触れません。最初から学びたい方は、公式ドキュメント等を参照してください。
ググるのが割とむずい
iOS 14 のウィジェットについてググると、だいたい Today Extension(こちらも「ウィジェット」と呼ばれることが多い)の方がヒットしてしまいます。検索ワードに `WidgetKit` `-Today` を入れるとだいぶマシになります。
SwiftUI は初めてでもなんとかなった
iOS 14 のウィジェット開発には SwiftUI を使う必要があります。SwiftUI をがっつり触るのは初めてでしたが、iOSアプリ開発歴がある方でしたら
- @State
- @ObservedObject
- @EnvironmentObject
あたりを抑えておけば、雰囲気でなんとかなると思います。
ただし、ウィジェットで扱う SwiftUI は、通常の SwiftUI と違う挙動をするところもあるので要注意です。この点は以下で解説します。
ウィジェットの更新は Timeline でおこなわれる
SwiftUI のチュートリアル通り、@ObservedObject を利用して View の状態を管理し、@Published でプロパティを監視〜みたいなことをやろうとしたんですが、ウィジェットが全く更新されない・・・!
ググってみるとこんなQAがヒットしました。
You can't use the ObservedObject like you'd normally use in your App.
ΩΩΩ<な、なんだってー!!
つまり、ウィジェットでは ObservedObject が使えないとのことです。
In Widgets you use a TimelineProvider which creates an Entry for your view.
そんなわけで、TimelineProvider の getTimeline(in:completion:) で Cloud Firestore からデータを取得し、View 側でそのデータを取り出すようにしました。
画像の非同期読み込みも癖がある
`SwiftUI Image url` 等でググると、画像を非同期で読み込む方法がたくさんヒットします。しかし、どれを試しても画像が表示されない・・!
そして、また Stack Overflow の QA にたどり着きました。
it is not supported to load images asynchronously.
ΩΩΩ<な、なんだってー!!
つまり、ウィジェットは画像の非同期読み込みに対応していないと。。
そこで今回は、回答にもある `Data(contentsOf: url) ` を使うことにしました。
止まらないブレークポイント、表示されないログ
アプリ開発において、ブレークポイント、デバッグログは非常に重要なものだと思います。
ただし、ウィジェット開発においては「ブレークポイントはたまに止まることもある」「調子が良いとログも表示される」ぐらいの気持ちで構えていたほうが良いと思います。
私の場合は WatchKit、Today Extension 対応でいろいろと辛い目にあってきているので、いつものことだな〜という感じでした😌
ウィジェットから Firebase Authentication で本体アプリとログイン状態を共有する
アルは Firebase Authentication でログイン状況を管理しております。
今回は「ログイン済のユーザーがフォローしている作品の新刊情報を表示」するウィジェットを作りたかったので、WidgetKit から Firebase Authentication にアクセスする必要があります。異なるターゲット間で認証状態を共有する方法は次の公式ドキュメントに書かれています。
ここで迷ったのは、FirebaseApp.configure() をどこでやるべきか?ということでした。さすがに WidgetKit から Firestore を使った例はまだ少ないらしく、ググってもなかなかヒットしませんでした。
最終的に
@main
struct WidgetExtension: Widget {
let kind: String = "WidgetExtension"
init() {
FirebaseApp.configure() // ここに置いた
}
// 共有 iOS キーチェーンを使ったアプリ間認証を有効にする
try? Auth.auth().useUserAccessGroup("*****")
}
に置くことでうまくいきました。(Thanks @fromkk)
★注:ちなみに、すでに Firebase Authentication を利用しているプロジェクトで、新たに共有キーチェーンを使い始める場合は上記ドキュメントの「ログイン中のユーザーを共有キーチェーンに移行する」が必須なのでご注意ください。これをやらないと、ログイン済みのユーザーが最悪の場合、削除されます。
表示できるコンテンツがなくてリジェクトされる
開発が終わり、いざ審査に出したところ1時間でリジェクトされました。理由は
Widget does not display comics that user is following.
つまり、ユーザーがフォローしているマンガがウィジェットに表示されないよ!とのことでした。審査に出すことに集中しすぎて、レビュー用のデータを用意できていなかったわけですね。。!
アルのアプリのように、ユーザーによってカスタマイズされたコンテンツをウィジェットに表示する場合は、レビュー用のデモアカウントでもきちんとコンテンツが表示されることを事前に確認することをオススメします。
ウィジェットからアプリが起動された回数を計測する
ホーム画面に置かれたウィジェットがタップされ、アプリが起動された回数も気になると思います。その場合は次の記事の方法で「ウィジェットから起動されたかどうか」を判定できるので、そこで計測イベント等を仕込むと良いと思います。
さいごに
というわけで、マンガの新刊情報をウィジェットで確認できるアルのアプリを今後ともよろしくお願いいたします!