iPhone 7 を入手したので早速 Taptic Engine の API を試してみました

iPhone 7 から以下のクラスを使うことでプログラム側から Taptic Engine を操作できるようになりました。早速実機で動作を確認してみました。

## API の種類

クラスとしては以下の4つです。`UIFeedbackGenerator` は抽象クラスとして用意されているので実質2つのクラスを利用する感じかと思います。

- UINotificationFeedbackGenerator
    - 既定の動作シーケンスを表す enum UINotificationFeedbackType を指定して動かせる
        - success
        - warning
        - error
- UIImpactFeedbackGenerator
    - 単発の振動の強さを表す enum UIImpactFeedbackStyle を指定して動かせる
        - light
        - medium
        - heavy
- UISelectionFeedbackGenerator
    - UIImpactFeedbackGenerator の light よりも弱いフィードバック
    - 選択が変更されたときに使うことを想定されている
    - UIDatePicker はデフォルトでこれが使われているようです。
- UIFeedbackGenerator
    - 上記3つのクラスのベースクラスで抽象クラス

## 実装

### UINotificationFeedbackGenerator を使う

既定シーケンスを実行できる UINotificationFeedbackGenerator であれば以下のコードだけで動かせます。

```swift
let generator = UINotificationFeedbackGenerator()
generator.prepare()
generator.notificationOccurred(.error)
```

`UINotificationFeedbackGenerator` を生成したあと、そのインスタンスに対して notificationOccurred メソッドを呼び出し、その引数でどのような通知振動を起こすかを指定します。notificationOccurred を呼び出す前に prepare を呼んでおきます。フィードバックが発生する待機時間を最小限に抑えることができるようですが、prepare() を呼ばなくても体感としてはかわりませんでした。

振動の違いは以下の通りです。

- .error
    - トゥトゥトゥ という感じで3回振動
- .warning
    - トントン と2回振動
- .success
    - トントン と2回振動

`.warning` と `.success` の違いがほとんど感じられませんでした。耳を当ててみると `.warning` は『カンカン』と甲高い感じの2回振動に感じます。`.success` は『グ カン』という感じで1発目の振動が重い感じです。この感じ方は人によって差が出そうですが些細な違いでしょう。

### UIImpactFeedbackGenerator を使う

こちらのクラスは振動の強弱だけの指定で振動は組み合わせでなく単発です。

```swift
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.prepare()
generator.impactOccurred()
```

UINotificationFeedbackGenerator と同様に enum で区別はするのですが、こちらは generator の生成時に style を指定して、`impactOccurred()` で振動をさせる感じです。

振動の違いは書くほどでもなく名称通りの強弱がついた振動になっています。個人的には `.light` になると振動したのに気付かないくらいです。`.heavy` でもそこまで強い振動には感じませんでした。

### UISelectionFeedbackGenerator を使う

使い方は1番簡単です。`UISelectionFeedbackGenerator` のインスタンスを作って、`selectionChanged()` を呼ぶだけです。

```swift
let generator = UISelectionFeedbackGenerator()
generator.prepare()
generator.selectionChanged()
```

ソースコードコメントには「選択が変わったときに使う」という趣旨のことが書かれていますがわざわざテーブルビューやその他の "選択状態" がある UI じゃなくても呼び出すだけなら可能です。おそらくはお作法としてこう使って欲しいという意図なのかと思います。

### UIImpactFeedbackGenerator を複合的に実行してみる

結論から言うとうまくいかないです。

まず、GCD を使って Background Thread でタイミングをずらしつつ実行しようと思うと以下のようなエラーが出てきました。

```
[Feedback] trying to activate the feedback engine from a non-main thread, this is unsupported.
```

現状は Main Thread でしか実行できないようです。

GCD で `DispatchQueue.main.async` で少しずらしつつ実行すると、感触として1回しか振動していないように感じます。`DispatchQueue.main.asyncAfter` でほんの少しずらすと2回振動させることは出来ました。ただしこれで `UINotificationFeedbackGenerator` のような複数の振動を組み合わせたものを自作するのは現実的ではないでしょう。

## 考察

`UINotificationFeedbackGenerator` は何らかのタスク実行のフィードバックとして使う感じでしょうか。

- Web API との通信失敗時にアラートなどの UI を表示させると同時に `.error` を使う
- フォームの入力時に不正文字種が入力された時に `.warning` を使う

などが浮かびます。

`UIImpactFeedbackGenerator` は単発なので

- ボタンタップ時に `.medium` や `.light` で演出
    - 特に指でボタンが隠れて見た目上のフィードバックが返せないような小さいボタンに有効かも
    - 設定.app の UISwitch の操作時に Apple はこれを使っているようです
- ~~（実現できるか未検証ですが）複数の振動を組み合わせて独自のシーケンスを作ってみる~~
    - 現実的ではなかった

というような利用が考えられるのではないでしょうか。

`UISelectionFeedbackGenerator` はヘッダーコメントに書かれているように選択状態が変わったときに使うことが推奨されてそうです。UIDatePicker の動きを参考にしてみると良いと思います。

## サンプルコード

これらの動作を検証したサンプルコードを github に置いてあるので興味のある方はどうぞ。

[griffin-stewie/TapTicFeedbackSample: Sample code for UINotificationFeedbackGenerator and UIImpactFeedbackGenerator on iPhone 7 and iPhone 7 Plus](https://github.com/griffin-stewie/TapTicFeedbackSample "griffin-stewie/TapTicFeedbackSample: Sample code for UINotificationFeedbackGenerator and UIImpactFeedbackGenerator on iPhone 7 and iPhone 7 Plus")