watchOS 3 で「アレ」は本当にできるようになったのか？

##自己紹介

- フリーランスiOSエンジニア
- iOS / watchOS Sampler シリーズをつくっています

https://github.com/shu223

<img width="640" alt="Screen Shot 2016-06-04 at 16.41.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/31f2bb0e-b332-bfe4-4bc4-ed83a0c40bcb.png">

---

##2015年にお手伝いしたwatchOS案件

- [watchOS 1 app × 3](http://d.hatena.ne.jp/shu223/20150424/1429831590)
- [watchOS 2 app × 2](http://d.hatena.ne.jp/shu223/20150815/1439611420)

（一部だけお手伝い、非公開プロジェクトも含む）


---

##「それ、残念ながらできないんです・・・」

（企画の詳細は書けませんが・・・）

- ジャイロが必要な機能 × 2
- アプリが非アクティブになってもセンサの値を取り続ける必要がある機能 × 2
- タップ位置を知る必要があるインタラクション × ?
- ジョイスティック的な引っ張る系のUI × 2
- ウォッチ本体から音を鳴らす × ?

---

##watchOS 3 ではできるようになった！

- ジャイロが必要な機能 - Gyroscope access
- アプリが非アクティブになってもセンサの値を取り続ける必要がある機能 - Background support
- タップ位置を知る必要があるインタラクション - UITapGestureRecognizer
- ジョイスティック的な引っ張る系のUI - UIPanGestureRecognizer
- ウォッチ本体から音を鳴らす - Speaker Audio

（参考：[API Diffsから見る watchOS 3 の新機能 - Qiita](http://qiita.com/shu223/items/c3a0e28c7b4a3085966a)）

---

##本当にできるようになったのか？

- キーノート、あるいはドキュメントで見かけただけ
- 実際に試してみないと思わぬ制限があるかもしれない

---

##watchOS 3 を入れた Apple Watch 実機で「本当にできるようになったのか」試してみる

---

##検証1: Gyroscope access

---

##API自体は前からあった

```swift:watchOS-2-Samplerのコード
let motionManager = CMMotionManager()

override func willActivate() {
    super.willActivate()

    let handler:CMGyroHandler = {(data: CMGyroData?, error: NSError?) -> Void in
        // データ取得時の処理
    }
    
    if motionManager.isGyroAvailable {
        motionManager.startGyroUpdates(to: OperationQueue.current()!, withHandler: handler)
    } else {
        // 利用不可の場合の処理
    }
}
```

---

##が、

`motionManager.isGyroAvailable` の戻り値が常に `false` だった。

→ **事実上の非サポート** だった

---

##WWDC16のキーノート

<img width="487" alt="Screen_Shot_2016-07-01_at_01_21_46.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/51476d18-0ee4-0310-e5a6-d8ea8520a2fd.png">

はっきり "Gyroscope Access" と書かれている

---

##watchOS 3 実機で試してみた

`print("\(CMMotionManager().isGyroAvailable)")`

---

##実行結果

---

##`false`

---

##→ watchOS 3 でも CMGyroData は Not Available!!!!

---
##？？？？

<img width="487" alt="Screen_Shot_2016-07-01_at_01_21_46.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/51476d18-0ee4-0310-e5a6-d8ea8520a2fd.png">

そんなはずは・・・

---

##WWDC16のwatchOS関連セッションスライドを"gyro"で検索

- What's New in watchOS 3
- Architecting for Performance on watchOS 3
- Building Great Workout Apps
- Keeping Your Watch App Up to Date
- Quick Interaction Techniques for watchOS
- Designing Great Apple Watch Experiences
- Game Technologies for Apple Watch

---

##出てこない・・・

---

##思わぬ伏兵

---

##Health and Fitness with Core Motion (713)

---

<img width="1059" alt="Screen Shot 2016-07-01 at 02.24.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/32d7ed43-6e08-57b7-6d57-c88cfbbcc916.png">

スライドp49、動画では13分ぐらいから。

---

<img width="1059" alt="Screen_Shot_2016-07-01_at_02_24_46.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/d2cc33fc-b312-32da-6ff4-46aff9fbc87a.png">

CMDeviceMotion が Apple Watch で利用可能！

---

##これが取れるならむしろ CMGyroData はいらない

---

##CMDeviceMotion から得られるデータ

---

##デバイスの角加速度

```
public var rotationRate: CMRotationRate { get }
```

<img width="300" alt="Screen Shot 2016-07-01 at 11.07.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/f8edf1bb-34ce-0766-86b5-a7c2b579936d.png">

- 回転の加速度
- **ジャイロセンサより得られる**
- [大昔に試したことがある](http://d.hatena.ne.jp/shu223/20110130/1296314294)が、**CMGyroDataとほぼ同じ値** （小数点第２位ぐらいまで一致）が得られた

---

##利用例：モーション検出

CMDeviceMotionから得られる **姿勢情報/オイラー角（加速度センサとジャイロから求められる）** を利用して、**テニスのフォアハンドとバックハンドのスウィングを自動検出**

<img width="369" alt="Screen Shot 2016-07-01 at 11.10.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/98205a5b-f589-e03c-961c-9f84bee843fc.png">

- 同セッションでサンプルが紹介されている
- どこかで話すか解説記事を書きます

---

##API自体は前からあった

```swift:watchOS-2-Samplerのコード
let motionManager = CMMotionManager()

override func willActivate() {
    super.willActivate()
    
    let handler: CMDeviceMotionHandler = {(motion: CMDeviceMotion?, error: NSError?) -> Void in
        // データ取得時の処理
    }
    
    if motionManager.isDeviceMotionAvailable {
        motionManager.startDeviceMotionUpdates(to: OperationQueue.current()!, withHandler: handler)
    } else {
        // 利用不可の場合の処理
    }
}
```

`motionManager.isDeviceMotionAvailable` が必ず `false` を返してきていた

---

##watchOS 3 実機で試してみた

---

##（デモ）

---

##検証1: Gyroscope access → watchOS 3 では可能！

---

##検証2: ウォッチ本体から音を鳴らす

---

##watchOS 2 からある WKAudioFilePlayer

- オーディオファイルを再生するクラス
- **要Bluetoothヘッドセット**

---

##Bluetooth ヘッドセットを「Apple Watchに」ペアリングして音を聴く！？

---

##WWDC16のキーノート

<img width="627" alt="Screen_Shot_2016-07-01_at_10_28_46.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/4cd37813-c554-3acc-7ed4-2c892a8a0b40.png">

Apple Watch のスピーカーから音を出せるようになった？

---

##AV Foundation on watchOS

---

- watchOS 3 から AVFoudation が使えるように！
- 全部のクラスが移行されるわけではないが、色々入っている
  - AVAudioPlayerNode（オーディオファイル再生）
  - AVAudioEngine（オーディオ処理諸々）
  - AVSpeechSynthesizer（音声合成） 等々

（参考：[API Diffsから見る watchOS 3 の新機能 - Qiita](http://qiita.com/shu223/items/c3a0e28c7b4a3085966a)）

---

##watchOS 3 実機で試してみた

---

```swifT:iOS-9-Samplerのコードを流用
import Foundation
import AVFoundation

struct AudioEngine {
    private let engine = AVAudioEngine()
    private let player = AVAudioPlayerNode()
    private var file: AVAudioFile?

    init() {
        setup()
    }
    
    private mutating func setup() {
        // setup engine and player
        engine.attach(player)
        guard let fileURL = Bundle.main().urlForResource("drumLoop", withExtension: "caf") else {
            fatalError("\"drumLoop.caf\" file not found.")
        }
        do {
            let file = try AVAudioFile(forReading: fileURL)
            self.file = file
            engine.connect(player, to: engine.mainMixerNode, format: file.processingFormat)
        }
        catch {
            fatalError("Could not create AVAudioFile instance. error: \(error).")
        }
    }
    
    private func scheduleLoop() {
        guard let file = file else {
            fatalError("`file` must not be nil in \(#function).")
        }
        
        player.scheduleFile(file, at: nil) {
            self.scheduleLoop()
        }
    }

    // MARK: - Public
    
    func start() {
        // Schedule buffers on the player.
        scheduleLoop()
        
        // Start the engine.
        do {
            try engine.start()
        }
        catch {
            fatalError("Could not start engine. error: \(error).")
        }
        player.play()
    }
    
    // Stop the engine
    func stop() {
        player.stop()
        engine.stop()        
    }
    
    func isPlaying() -> Bool {
        return player.isPlaying
    }
}
```

---

##（デモ）

---

##検証2: ウォッチ本体から音を鳴らす → watchOS 3 では可能！

---

##ここまでのまとめ

---

##watchOS 3 では

CMDeviceMotionでジャイロセンサの値、およびそれを利用した姿勢情報を取れるようになった！

→ ⌚️を利用した**モーション認識**が可能に！

---

##watchOS 3 では

AVAudioEngineが使えるようになった！

→ ⌚️から**「直接」音を出せる**ようになった！

---

次回予定：

- バックグラウンド機能の検証
  - CPU負荷とか、いろいろ制約はあるっぽい

---

##ご清聴ありがとうございました！

---

本記事は、2016.7.1に開催された [WWDC.next](http://connpass.com/event/32130/) でのLT資料です。

※Appleによる公開リソースをもとに作成しています。