前提
- 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 で「アレ」は本当にできるようになったのか?
- https://qiita.com/shu223/items/31b1b453ee1740d3eaf5
- 簡単な Apple Watch アプリを初めて作ってみる
- https://ez-net.jp/article/8C/ylktGR5J/5ZZKjSNrNQac/
- swiftでモーションセンサーの値を取得する方法でエラーになるのを解決した
- http://yatsurecreate.com/2017/03/31/swift%E3%81%A7%E3%83%A2%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%81%AE%E5%80%A4%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%A7%E3%82%A8/
- The Watch App Architecture
- https://developer.apple.com/library/archive/documentation/General/Conceptual/WatchKitProgrammingGuide/DesigningaWatchKitApp.html#//apple_ref/doc/uid/TP40014969-CH3-SW1