watchOS用アプリ作成を学ぶために、既にある程度設計や機能が確立しているOSSを題材にしてみました。
その際に既存の採用技術や設計にフィットさせていく際に詰まったところや困ったことをまとめてみました。
watchOSの開発は情報がかなり少ないので、同じく困った人の助けになれば幸いです。
前提
PokedexというOSSを題材にさせていただきました。
watchOSアプリを入れるにあたって、以下の技術を採用していることを前提に本記事を書いております。
- Embedded Framework
- Carthage
- XcodeGen
- Mint
watchOSアプリの導入がメインなので詳しくは割愛しますが、アプリ概要や採用している技術についての詳細はこちらをご覧ください。
また、今回はAppleWatch独立ではなく、Watch App for iOS App
のターゲットによって追加される、iPhoneと連携して動作するwatchOSアプリを前提としております。
WatchOSアプリ導入の手順
上記のような技術を採用しているPokedexに対して、以下の手順でwatchOSアプリを導入しました。
- 既存プロジェクトにまず手動でwatchOSアプリのターゲットを追加して、実機でビルドできることを確認する
- XcodeGen対応 - 手動で追加したwatchOSアプリのターゲット(WatchApp / WatchApp Extension)をXcodeGenでの導入に以降
- Carthage対応 - 導入ライブラリをwatchOSにも対応させる
- Embedded Frameworkの利用 - iOSと共有するソースをマルチモジュールwatchOS用のターゲットとしてマルチモジュール化してApple Watch側から利用できるようにする
Xcodegenで最初にプロジェクト作成した後、storyboardのリファレンスを繋ぎなおす
ターゲットの追加後project.ymlに加筆してビルドすると、以下のようなエラーを吐いて、ランタイムエラーになることがありました。
"NO". Couldn't instantiate class _TtC7Pokedex19InterfaceController
その際には、apple watch側で用意しているstoryboardのリファレンスが切れているので、Interface.storyboard -> Attributes Inspector
を選択し、Custom Classを再度指定してリファレンスを繋ぎ直すと解消されました。
Carthageのビルド対象となるプラットフォームにwatchOSを追加する
CocoaPodsやCarthageなどのパッケージマネージャーを利用して、watchOSのターゲットにもライブラリを導入したい場合、iOSに加えてwatchOSもプラットフォーム指定しなければなりません。
CocoaPods
// watchOSアプリ
target '[watchOS Extensionのターゲット]' do
platform :watchos, '2.0'
pod 'Hoge'
end
Carthage
carthage bootstrap --platform iOS,watchOS
Carthage + Mint
今回のOSSではMintを使ってCarthageを導入しているのでこちら
mint run Carthage/Carthage carthage bootstrap --platform iOS,watchOS --cache-builds
生成されるCarthage/Build
にwatchOS
が追加されていればOK
watchOSApp ExtensionにFrameworkを関連づけた際に、実機デバッグでランタイムエラーになる
PokedexではEmbededd Frameworkを使用してappとは別にPresentation, Domain, DataStoreを用意してマルチモジュール化していました。
このうち例えば、DataStoreのソースをベースにwatchOS用にビルドして、AppleWatch側のターゲットと関連づけすれば、通信処理を共通化して利用できますし、依存関係もスッキリ保てます。
この際XcodeGenを採用しているので、project.ymlからソースコードベースでwatchOS用にFrameworkを関連づけなければなりませんが、僕の場合以下のランタイムエラーになりました。
WatchDataStoreはframework、watchOSApp Extensionはframeworkを関連づけるtargetです。
dependent dylib '@rpath/WatchDataStore.framework/WatchDataStore' not found for '[watchOSApp Extensionのpath]', tried but didn't find: '[WatchDataStoreのpath]' ...
FrameworkのEmbed設定が正しくないために発生しているエラーのようでした。また、シミュレーターだと通常通り動くので、実機で初めて発覚したバグでした。
これは、WatchOSAppExtensionとWatchDataStoreの関連づけにembed: true
のオプションを追加することで解消しました。
今回のwatchOSターゲットの追加では以下のようにproject.ymlに追加しました。
targets:
# iOS app
Pokedex:
type: application
platform: iOS
sources: [Pokedex]
settings:
# some setting
dependencies:
# some target
- target: WatchOSApp
codeSign: false
embed: true
# iOS framework
# some framework
# watchOS
WatchOSApp:
type: application.watchapp2
platform: watchOS
sources: [WatchOSApp]
settings:
PRODUCT_NAME: Pokedex
PRODUCT_BUNDLE_IDENTIFIER: pokedex.watchkitapp
CODE_SIGN_STYLE: Automatic
dependencies:
- target: WatchOSAppExtension
WatchOSAppExtension:
type: watchkit2-extension
platform: watchOS
sources: [WatchOSApp Extension]
settings:
PRODUCT_NAME: WatchOSApp Extension
PRODUCT_BUNDLE_IDENTIFIER: pokedex.watchkitapp.watchkitextension
CODE_SIGN_STYLE: Automatic
dependencies:
- target: WatchDataStore
embed: true
WatchDataStore:
type: framework
platform: watchOS
sources: [DataStore]
settings:
PRODUCT_BUNDLE_IDENTIFIER: pokedex.WatchDataStore
CODE_SIGN_STYLE: Automatic
dependencies:
- carthage: Alamofire
- carthage: Nuke
その他
iOSアプリとwatchOSアプリでソースを共有する
iOSアプリと共有したいソースにwatchOSに対応していないライブラリを使用している場合、以下のように条件付きコンパイルを行う必要があります。
今回はSpotlight検索機能を提供するCoreSpotlight
がwatchOSに対応していないので、watchOS用のターゲットに共有するソースコードから以下のようにOSごとの制御を行います。
#if os(iOS) || os(macOS)
import CoreSpotlight
#endif
// 以下watchOSでも使うライブラリ
import Foundation
import MobileCoreServices
複数のsimulatorを同時に起動しない
iPhoneと連動して動作するwatchOSのアプリを起動する場合、一度の起動でiPhoneとApple Watchの二つのシミュレーターを起動することになります。シミュレーターの端末を変えて確かめたくなった時に、iPhoneのシミュレータも異なるモデルに変わるので、試したい端末の倍のシミュレーターが動作しPCがフリーズしやすくなるので注意が必要です。
早めに実機で確認する
試験用watch端末を登録したプロビジョニングプロファイルを用意しなければいけないので後回しにしがちですが、watchターゲットを追加した段階で実機で確認するのが良いと思います。シミュレーターでは動くのに実機では起動できないなどシミュレーターではわからないことがたくさんある印象でした。
ただ、実機だとiPhone経由でビルドする分時間がかかるので、早期にまず実機でビルドできることを確認して、その後はシミュレーターで動作確認しつつ開発すると良いかと思います。
リンク
PokéAPIを利用してMVP+CleanArchitectureのiOSアプリを作ったので解説する
最後に
watchOSアプリの導入で詰まったところをまとめました。
情報が少なく、かなり試行錯誤した感があるので、もし間違えているところなどありましたら、そっと教えていただけますと幸いです。