🍎

React Native × ExpoでApple Watchのコンプリケーションを実装してみる

に公開

Apple Watchにはコンプリケーションという機能があり、文字盤上にアプリのウィジェットを配置することができます。

https://developer.apple.com/documentation/clockkit/creating-complications-for-your-watchos-app

本記事では、React Native × Expoを使いContinuous Native Generationの思想に則ったフローで、コンプリケーションをタップ→Watch Appを起動させるまでを実装します。

iPhoneアプリ作成

Expoで新プロジェクトを作成します。

npx create-expo-app@latest
~~~~
✅ Your project is ready!

To run your project, navigate to the directory and run one of the following npm commands.

- cd watch-comp
- npm run android
- npm run ios
- npm run web

Simulatorで立ち上げます。

npm run ios

立ち上がりました。

Watch Appターゲット作成

次にiPhoneアプリをApple Watchに対応させるためにWatch Appのターゲットを実装します。こちらのConfig Pluginライブラリを使うことで、Apple TargetsをiOSディレクトリの外で管理できます。https://github.com/EvanBacon/expo-apple-targets

npx create-target Watch
~~~
[bacons/apple-targets] Expo config is missing required ios.appleTeamId property. Find this in Xcode and add to the Expo Config to correct. iOS builds may fail until this is corrected.
Creating a watch Apple target.

Writing expo-target.config.js file
Writing Info.plist file
Target created! Run npx expo prebuild -p ios to fully generate the target. Develop native code in Xcode.

ios.appleTeamIdが必要なのでapp.jsonに追加します。

{
  "expo": {
    "name": "watch-comp",
    "slug": "watch-comp",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/images/icon.png",
    "scheme": "watchcomp",
    "userInterfaceStyle": "automatic",
    "newArchEnabled": true,
    "ios": {
      "supportsTablet": true,
      "appleTeamId": "your-team-id" // appleTeamIdを入れてください
    },
    ~~~

prebuildコマンドでXcode Projectを生成します。

npx expo prebuild -p ios --clean

Xcodeで開きます。

xed ios

expo:targetsディレクトリにwatchディレクトリが生成されてますね。エディタ右側のPreviewにテンプレートのViewも表示できています。

SimulatorにインストールするにはiPhoneとのペアリングが必要です。
こちらの記事を参考に設定します。
https://zenn.dev/oka_yuuji/articles/906e1247e44b3c

SimulatorでもWatchという名前でインストールできました。

Watch用のWidget作成

あとはWatch用のWidgetを作成するだけですね。...。しかしexpo-apple-targetsのtargetオプションにWatch Widgetが見当たりません...。

現状のexpo-apple-targetsは、Watch用のWidget(Complications)を作成するターゲット定義にはまだ対応していないことが判明しました。
Watch用のWidgetはexpo-apple-targetsで生成したWatch Appをtargetとして登録する必要があるので、そこが未対応のようです。

自分で実装するしかないか...と思いながらIssueやPRを眺めていると、なんと対応PRがありました!コメントを見ても動いてるようで、あとはBaconさんのレビュー、マージ待ちのようです。 https://github.com/EvanBacon/expo-apple-targets/pull/121

PRとしてはざっくり、以下のような内容となっています。

  • Watch Widgetのテンプレート追加
  • Watchをtargetとして指定可能にする
  • Watch WidgetよりもWatchが先に生成されるようにソート(Watch WidgetのtargetとなるWatch Extensionが先に生成される必要があるため)

ということなので、こちらのPRブランチで試させていただきましょう。

FetchしてPRのブランチに切り替えます。

git fetch https://github.com/EvanBacon/expo-apple-targets.git pull/121/head:pr-121
~~~
git switch pr-121
Switched to branch 'pr-121'

このままパスを指定して使うことも可能ですが、今回はnpm packして元のリポジトリのルートに置きます。
create-targetのCLIが必要なのでEvanBacon/expo-apple-targets/packages/create-targetをpackします。

npm pack

生成されたtgzファイルを自分のプロジェクトのルートに配置し、installします。

npm install --save-dev ./create-target-2.0.0.tgz

実行してみましょう。

npx create-target watch-widget

~~~
Writing expo-target.config.js file
Writing Info.plist file
Target created! Run npx expo prebuild -p ios to fully generate the target. Develop native code in Xcode.

できましたね!!

prebuildで再生成します。

npx expo prebuild -p ios

エディタ右側のPreviewに表示されている小さな腕時計のような画像が今回作成したコンプリケーションです。

Widgets.swiftは「タップしたらWatch Appに遷移するだけの最低限のコードに変更しています。

Simulatorで動作確認してみましょう。「モジュラー」文字盤を追加し、コンプリケーションでWatchを選択します。

タップすると...

動きましたね!
PR作成者mpiannucciさん、ありがとうございます。

おわりに

Expo SDK 55ではSwiftを触らずにTypeScriptだけでWidgetを実装できるようになるというPostがありました。楽しみですね!

https://x.com/k7grzywacz/status/2000980494998175934?s=20

参考

Discussion