LoginSignup
7
4

More than 3 years have passed since last update.

XcodeGen、Embedded Frameworkを導入しているOSSにwatchOSアプリを導入する

Posted at

watchOS用アプリ作成を学ぶために、既にある程度設計や機能が確立しているOSSを題材にしてみました。
その際に既存の採用技術や設計にフィットさせていく際に詰まったところや困ったことをまとめてみました。
watchOSの開発は情報がかなり少ないので、同じく困った人の助けになれば幸いです。

前提

PokedexというOSSを題材にさせていただきました。
watchOSアプリを入れるにあたって、以下の技術を採用していることを前提に本記事を書いております。
- Embedded Framework
- Carthage
- XcodeGen
- Mint

watchOSアプリの導入がメインなので詳しくは割愛しますが、アプリ概要や採用している技術についての詳細はこちらをご覧ください。

また、今回はAppleWatch独立ではなく、Watch App for iOS Appのターゲットによって追加される、iPhoneと連携して動作するwatchOSアプリを前提としております。

WatchOSアプリ導入の手順

上記のような技術を採用しているPokedexに対して、以下の手順でwatchOSアプリを導入しました。

  1. 既存プロジェクトにまず手動でwatchOSアプリのターゲットを追加して、実機でビルドできることを確認する
  2. XcodeGen対応 - 手動で追加したwatchOSアプリのターゲット(WatchApp / WatchApp Extension)をXcodeGenでの導入に以降
  3. Carthage対応 - 導入ライブラリをwatchOSにも対応させる
  4. 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/BuildwatchOSが追加されていれば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に追加しました。

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アプリの導入で詰まったところをまとめました。
情報が少なく、かなり試行錯誤した感があるので、もし間違えているところなどありましたら、そっと教えていただけますと幸いです。

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4