TL;DR
- Apple Watch アプリの ⌘R が毎回異様に遅い場合、スキームのビルド対象に iOS コンパニオンアプリが含まれていないかをまず疑う
- Watch アプリが iOS アプリに埋め込まれる構成(Embed Watch Content)だと、Xcode が自動生成したスキームに iOS アプリが入っていることがあり、Watch を実行するたびに iOS アプリのビルド → 再署名 → iPhone へのインストールまで走る
- スキームから iOS アプリを外すだけで、ビルド対象が Watch App+Widget 拡張だけになり大幅に短縮できる
- 残りの遅さ(iPhone 経由の無線転送)は watchOS の宿命だが、運用でかなり改善できる
環境
- Xcode 17 / watchOS 26 / iOS 26
- 構成: iOS アプリ+Watch アプリ(コンパニオン型・
WKCompanionAppBundleIdentifierあり)+ WidgetKit コンプリケーション拡張
症状
Apple Watch 実機に向けて ⌘R すると、コードを数行変えただけでもビルド〜インストール完了まで体感で数分待たされる。シミュレータは普通なのに実機だけ遅い。
調査
アプリサイズは問題なかった
まずアプリ自体が太っていないかを確認。ソースもアセットも合計 100KB 程度で、転送量が問題になるサイズではなかった。
du -sh "MyApp Watch App"/*
ビルド設定も問題なかった
Debug 構成は以下の通りで、ここも健全。
-
DEBUG_INFORMATION_FORMAT = dwarf(dSYM 生成なし) ONLY_ACTIVE_ARCH = YESSWIFT_OPTIMIZATION_LEVEL = -Onone
犯人はスキームだった
xcshareddata/xcschemes/*.xcscheme を開いて BuildAction を見ると、Watch App スキームのビルド対象に iOS アプリが入っていた。
<BuildActionEntries>
<BuildActionEntry ...>
<BuildableReference
BuildableName = "MyApp Watch App.app"
... />
</BuildActionEntry>
<!-- ↓ これが余計 -->
<BuildActionEntry ...>
<BuildableReference
BuildableName = "MyApp.app"
BlueprintName = "MyApp"
... />
</BuildActionEntry>
</BuildActionEntries>
iOS アプリは「Embed Watch Content」ビルドフェーズで Watch アプリを埋め込むため、iOS アプリ → Watch アプリのターゲット依存がある。この状態で Watch スキームに iOS アプリが入っていると、⌘R のたびに
- Watch App+コンプリケーション拡張のビルド
- iOS アプリ全体のビルド+Watch アプリの埋め込み+再署名
- ペア iPhone への iOS アプリのインストール
- iPhone 経由で Watch へアプリ転送(Bluetooth/WiFi)
が全部走る。Watch 側の開発では 2〜3 は不要な仕事。
依存の向きに注目すると、Watch アプリは iOS アプリに依存していない(依存しているのは iOS アプリの側)。だから Watch スキームから iOS アプリを外しても何も壊れない。
修正
Xcode の GUI なら Edit Scheme… → Build で iOS アプリの行を削除するだけ。.xcscheme を直接編集するなら、該当の BuildActionEntry ブロックを削除する。
修正後、ビルドされるターゲットが Watch App+拡張だけになったことを確認:
xcodebuild -project MyApp.xcodeproj -scheme "MyApp Watch App" \
-destination 'platform=watchOS Simulator,name=Apple Watch SE 3 (44mm)' build 2>&1 \
| grep -oE "in target '[^']+'" | sort -u
# in target 'MyApp Watch App'
# in target 'MyAppComplicationExtension'
注意点
- iPhone 側のコードを変えたときだけ、iOS アプリのスキームで別途インストールする(どうせやる操作なので手間は増えない)
- コンパニオンアプリは一度 iPhone に入っていれば、Watch の ⌘R ごとに再インストールされる必要はない。
WCSession(WatchConnectivity)もコンパニオンが入ってさえいれば動く - Widget/コンプリケーション拡張のスキームにも同じように iOS アプリが紛れ込んでいることがあるので、ついでに確認するとよい
それでも残る遅さと対策
watchOS の実機インストールは必ずペア iPhone を経由して無線転送されるため、iPhone 直挿しの感覚には絶対にならない。ただし以下でかなり変わる。
| 対策 | 理由 |
|---|---|
| iPhone を USB ケーブルで Mac に接続する | ネットワーク経由デバッグだと Watch への転送が数倍遅くなる |
| Watch を充電器に載せて画面を点けたままにする | 省電力状態だと転送・インストールが露骨に遅くなる |
| Edit Scheme → Run → Info の「Debug executable」をオフ | watchOS はデバッガのアタッチだけで数十秒かかることがある。print デバッグで足りる日はオフが快適 |
| 初回の遅さは諦める | Xcode・デバイス再起動直後はシンボル準備が走るため必ず遅い。2回目以降は速くなる |
おまけ: スキームに残った亡霊参照にも注意
今回の調査中、iOS アプリのスキームに削除済みの StoreKit Configuration ファイルへの参照が残っているのも見つけた。
<StoreKitConfigurationFileReference
identifier = "../../LocalStoreKitConfig.storekit">
</StoreKitConfigurationFileReference>
ファイルを消してもスキーム側の参照は自動では消えない。起動時の余計な構成読み込み(と失敗)の元になるので、機能を撤去したらスキームも掃除しよう。
まとめ
- 「watchOS の実機が遅いのは仕様」と思い込む前に、スキームのビルド対象を確認する
- Xcode が自動生成するスキームは、Watch アプリ作成時の構成によって iOS アプリを巻き込んでいることがある
- 依存の向き(iOS → Watch であって逆ではない)を理解していれば、安心して外せる
- 残る転送の遅さは USB 接続・充電器・デバッガ非アタッチで軽減する