React Native × ExpoでApple Watchのコンプリケーションを実装してみる
Apple Watchにはコンプリケーションという機能があり、文字盤上にアプリのウィジェットを配置することができます。 
本記事では、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がありました。楽しみですね!
Discussion