前提
- watchOS 4.3.2
- Xcode 10.1
- iPhone 12.1.2
- Apple Watch series 1
初版のApple watchが出た頃にジャイロの値を取ろうとしたが失敗した(APIは存在するが必ず固定値が返っていた)件の再チャレンジです。
watchOS 3 で「アレ」は本当にできるようになったのか?によれば、watchOS 3以降ではジャイロの値が取れるようになっているらしいです。
tl;dr
実装
WatchOS1の頃触ってから久しぶりにWatch Appの開発だったので、そもそもの作り方や考え方からやりなおします。
基本的なApple watchアプリの作り方
記事はだいぶ昔ですが内容は今でも通用します。
その他、だいたい「アレ」の記事の通りですが、躓いたポイントをメモしておきます。
ハンドラのエラーの型が違う
CMDeviceMotionHandlerの第2引数の型はErrorです。
時代も移り変わり、Objective-Cの遺産とも言えるNSErrorは使わなくなったみたいです。
override func willActivate() {
super.willActivate()
let handler: CMDeviceMotionHandler = {(motion: CMDeviceMotion?, error: Error?) -> Void in
self.rotLabelX.setText(String(format: "%.2f", motion!.rotationRate.x))
self.rotLabelY.setText(String(format: "%.2f", motion!.rotationRate.y))
self.rotLabelZ.setText(String(format: "%.2f", motion!.rotationRate.z))
}
if motionManager.isDeviceMotionAvailable {
motionManager.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler: handler)
}
else
{
rotLabelX.setText("not available gyro")
rotLabelY.setText("not available gyro")
rotLabelZ.setText("not available gyro")
}
}
詳しくはこちら → モーションセンサーの値が取得できない
githubのコードは直ってたので、プロはこんなところでは躓かないと思いますが、一応。
ビルドバージョンが高すぎて実行時にコケる
Deployment Targetをちゃんと4.3にしているのに、5系でビルドされてるからApple watchにインストールできないよと怒られる。
よく見るとExtensionだった。
ちゃんとWatch Appのターゲットを適切な値にしましょう。
クラスが足りなくて実行時にコケる
Watchアプリを作ってる人からしたら常識なのかもしれませんが、GyroscopeInterfaceController.swift
という新しいInterfaceControllerクラスを作ったのに、Extensionに追加してなくてコケました。
2019-02-21 15:18:09.519384+0900 WatchKitApp Extension[678:3114357] [default] -[SPRemoteInterface createViewController:className:properties:contextID:info:gestureDescriptions:clientIdentifier:]:3418: Couldn't instantiate class _TtC21WatchKitApp_Extension28GyroscopeInterfaceController
2019-02-21 15:18:09.521782+0900 WatchKitApp Extension[678:3114357] [default] -[SPRemoteInterface createViewController:className:properties:contextID:info:gestureDescriptions:clientIdentifier:]:3419: Critical failure. Simulating crash: Condition failed:"NO". Couldn't instantiate class _TtC21WatchKitApp_Extension28GyroscopeInterfaceController
(lldb)
解決するには、自分で作ったクラスをExtensionのCompile Sourcesに追加します。
クラスが足りなくて実行時にコケる 2
実は上記だけではだめで、storyboardのCustom classのロード元をextensionにしないといけません。
このModuleが非Extensionだと、同じように実行時にクラスが足りないと言ってコケます。
上のようにExtentionのCompile Sourcesに新しいInterfaceControllerを追加すると、ここでExtentionが選択できるので、そちらを選びましょう。
これで動きます。たぶん。
ていうかExtensionって何…
そもそもApple watch AppというのはiPhoneアプリのおまけのようなもので、処理はほとんどせずに、iPhoneアプリのViewとなることが想定されています。
その関係性を前提として、Apple watchから見たiPhoneの方(自分がViewであって、実処理をする側)の方をExtensionという、みたいです。
https://noumenon-th.net/webstrategy/2014/12/11/watchkit/
...というのが初期の思想だったみたいですが、watch自体の性能もだんだん上がってきているためか、現在の相関図ではwatch側にExtensionが存在してますね。
The Watch app bundle contains your app’s storyboards, while the WatchKit extension contains your app’s code and additional resources
いずれにせよ、Watch Appのstoryboardは表示のため、Extensionは実処理のためという考え方であって、アプリケーションのコードはExtensionに含まれるべきだということみたいです。
ゆえに、storyboardに紐づくソースは、Watch App側とExtension側でcompileされるよう設定すべきということですね。
謝辞
- watchOS 3 で「アレ」は本当にできるようになったのか?
- 簡単な Apple Watch アプリを初めて作ってみる
- swiftでモーションセンサーの値を取得する方法でエラーになるのを解決した
- The Watch App Architecture