watch OS1 専用
**ここで書いていることはwatch OS1のためのものです。watch OS2の場合はWKExtensionDelegate
というクラスがあるので、そちらでハンドリング可能だと思います(未検証)。
AppleWatchにはAppDelegateがない
WatchKitで画面を作成するとこんな感じになると思います(GlanceとNotificationはオプションなのでない場合もある)。
ユーザーの状態などによってWatchAppに表示する画面を切り替えたい場合があったりします。ユーザーがログイン済だったらタイムラインを表示、未ログインだったらログインページを表示する、みたいな場合です。
iPhoneだったらAppDelegateなどでうまく処理して表示の切り替えを行うことになるのですが、残念ながらAppleWatchにはAppDelegateがないので、Storyboardでis Initial Controller
がつけられたWKInterfaceController
が最初に呼ばれるようになります。
動的にコントローラーを変更する役割のWKInterfaceControllerを用意する
最初、これを解決する方法が分からず「どうしようもないFrameworkだな」くらいに思っていたのですが、WKInterfaceController
にAppDelegateに似た役割を持たせれば解決可能でした。
WKInterfaceController
にはreloadRootControllersWithNames
というクラスメソッドが用意されており、第一引数にStoryboardで設定した各コントローラーのidentifierの配列を、第二引数のcontexts
には各コントローラーに渡したいデータを渡します。
class func reloadRootControllersWithNames(_ names: [AnyObject],
contexts contexts: [AnyObject]?)
このメソッドを利用すると、ルートのコントローラーから全て置き換わるため、ユーザーの状態によって異なる画面構成を提供することも可能になります。
サンプル
以下はEntryPointInterfaceController
をinitial Controllerにし、iOS8.2のiPhoneからプリインストールされているApple Watchアプリに設定項目を追加します(Settings BundleをiPhoneアプリのターゲットに追加して設定項目を整えればOK)。
Settings Bundleの設定はこんな感じです(最下部の段が空欄になっていますがAppGroupsの設定を忘れないようにしてください1)。
iPhone側のAppleWatchアプリではこのように表示されます。
iPhoneのAppleWatchアプリで設定した項目をNSUserDefaults
から読み込んでWatchAppの画面を切り替えるためのサンプルコードがこちらです。
class EntryPointInterfaceController: WKInterfaceController {
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let defaults: NSUserDefaults = NSUserDefaults(suiteName: "/* AppGroupsのidentifierを入れてください */")!
let typeString: NSString? = defaults.valueForKey("screen") as? NSString
if let type = typeString {
if type.isEqualToString("login-screen") {
WKInterfaceController.reloadRootControllersWithNames(["login"], contexts: nil)
} else {
WKInterfaceController.reloadRootControllersWithNames(["userInfo"], contexts: nil)
}
} else {
WKInterfaceController.reloadRootControllersWithNames(["login"], contexts: nil)
}
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
}
なお、このEntryPointInterfaceController
はawakeWithContext
メソッド内でルートコントローラーを切り替える処理を行っているので画面に表示されることはありません。
もしなにかしら表示させたいのであればwillActivate()
メソッド内でNSTimerなど少し時間をおいてreloadRootControllersWithNames
メソッドを呼び出せば良いと思います。
そのほかベストプラクティスとAppleがうたっている手法についてもまとめてあるので、AppleWatch開発をしている方はご覧になってみてください。
AppleWatch開発のベストプラクティス 〜WatchKit Development Tipsに書かれていることをまとめた〜
まとめ
現時点で実機でのテストができませんし、なかなか挙動が見えないのが辛いところですが、いろいろ開発していると足りない部分をうまく補って自然なように見せる工夫がAppleWatchには必要だと感じます。
バッテリー問題が足を引っ張っていろいろな制限がかかっていると思うので、将来的にはこういった苦しい工夫をしなくても良くなるのではないかと思いますが、新しいデバイスのアプリを開発する醍醐味ってこういうところにあるんじゃないかな〜なんて思って楽しんで開発しています。
デベロッパーそれぞれが色々な工夫を施して出してくるはずなので(というかせざるを得ないのですが)、発売するまでにどれくらいのアプリが対応されるか楽しみですね。
-
WatchAppはiPhoneアプリのExtensionなのでAppGroupsの設定が必須になります。プロジェクト内の各ターゲットはもちろん、Settings Bundle、
NSUserDefaults
を生成する際もAppGroupsの設定を忘れないようにしてください。 ↩