iOS14の新機能、Nearby Interaction

iOS14の新機能であるNearby Interactionの簡単な検証。

## 試した環境

- Xcode12.0

## ざっくりどういうものか

iOS14 SDKから追加されるFramework。

> Locate and interact with nearby devices using identifiers, distance, and direction.

ということで、識別子/距離/方角を使い近くのデバイスを特定し、インタラクトすることが可能になる。

## 利用条件

iOS14 (2020年秋にリリースされるはず) にするだけではダメで、U1チップを搭載したiPhoneでないと使えないという制限がある。
2020年9月時点だと、iPhone11系の端末 (11, 11 Pro, 11 Pro Max) でのみ利用可能。

近接したデバイスと位置情報/識別子を共有することに同意すると、インタラクションが可能になる。
※今回シミュレータでのみ試したが、認可のダイアログなどは出なかった。要調査

### U1チップとは

Ultra Wide Band (UWB) = 超広帯域無線 と言われる無線通信が使えるチップ。この通信技術は、もともと産業用や軍事用には使われていたが、コンシューマ向けの製品に乗るのは初めてらしい。
現時点でのリリース版ではAirDropの高速化くらいにしか使われていないっぽいが、忘れ物防止タグとの協調や車のキー用途などで使われる？

- [独自の無線チップ「U1」がもつ知られざる価値：アップルの未来（6） \| WIRED\.jp](https://wired.jp/2020/06/21/u1-chip-future-of-apple-6/)
- [ASCII\.jp：アップルiPhone今年の大注目は「U1チップ」 \(1/4\)](https://ascii.jp/elem/000/002/006/2006892/)

## ユースケース by Appleのドキュメント

- マルチユーザのARで、それぞれの参加者の手に水風船を配置
- ライドシェアアプリなどで、運転手と乗客の相対的な位置をリアルタイムで特定
- ホッケー的なゲーム (下図)

![NIイメージ1](https://docs-assets.developer.apple.com/published/e0800fbbe2/rendered2x-1592331768.png)

画像出典 : https://developer.apple.com/documentation/nearbyinteraction

## Nearby Interactionフレームワークの使い方

近くのデバイスとインタラクトするのには、サーバ・Bluetooth・P2P通信 (MultipeerConnectivity) などを使う。
各デバイスは `NISession` を作成し、上記のネットワークを使用してディスカバリートークン (`NIDiscoveryToken`) を交換しあう。トークンを相手から受け取ると、アプリが実際にNearbyInteraction (以下 NI) セッションを開始する。デバイスのU1チップがデータの交換を管理し、NIが他デバイスとの相対位置を継続的に提供することを可能にする。

```swift
let niSession = NISession()
```

セッションのインスタンス作成時に、ユーザに対して許可プロンプトが出る（らしい）。

通信相手のディスカバリトークンを得られたら、セッションを開始できる。

```swift
let token: NIDiscoveryToken = ...
let config = NINearbyPeerConfiguration(peerToken: token)
niSession.run(config)
```

セッションを実行するとNIがセットした相手のトークンを検証し、有効な場合は `NISessionDelegate` の `session(_:didUpdate:)`を呼び出し、通信相手との相対位置の情報を得ることができる。

```swift
class ViewController: UIViewController {
    func ...() {
        niSession.delegate = self
    }
}

extension ViewController: NISessionDelegate {
    func session(_ session: NISession, didUpdate nearbyObjects: [NINearbyObject]) {
        // NINearbyObject に通信相手の情報が入ってる
    }
}
```

アプリのバックグラウンド移動でセッションが中断した時やセッションが無効になった場合も、デリゲートメソッドに通知がくる。
再開やエラーハンドリングをしたりするのに使う。

[NISessionDelegate](https://developer.apple.com/documentation/nearbyinteraction/nisessiondelegate)

```swift
public protocol NISessionDelegate : NSObjectProtocol {
    optional func session(_ session: NISession, didRemove nearbyObjects: [NINearbyObject], reason: NINearbyObject.RemovalReason)

    optional func sessionWasSuspended(_ session: NISession)

    optional func sessionSuspensionEnded(_ session: NISession)

    optional func session(_ session: NISession, didInvalidateWith error: Error)
}
```

### `NINearbyObject` の内容

Xcodeで中を見てみる。

```swift
@available(iOS 14.0, *)
open class NINearbyObject : NSObject, NSCopying, NSSecureCoding {
    @NSCopying open var discoveryToken: NIDiscoveryToken { get }
}

@available(iOS 14.0, *)
@available(macOS, unavailable)
extension NINearbyObject {

    public var distance: Float? { get }

    public var direction: simd_float3? { get }
}
```

かなり最小限の内容。以下の3つのデータをgetできる。

1. 相手のディスカバリトークン
2. 相手との距離
3. 相手の方角

方角データに使われている `simd_float3`型は、3次元のベクトルのようなもの。ARKitやSceneKitでよく使われるらしい。
ディスカバリトークンがgetできるのは、複数相手と通信状態になった際の相手の特定に必要だから、だろうか。

## 動作に関しての制約

公式ドキュメントによると、正確な動作を提供するには以下の制約が必要らしい。

- 端末同士の距離が9メートル以内
- 端末を縦向きの状態で持っていること
- 端末が背中合わせ
- 端末間に人・車両・壁などの障害物がないこと

下図のようなイメージで相手端末を検証するとのこと。

![NIイメージ2](https://docs-assets.developer.apple.com/published/61e8eff686/rendered2x-1591201856.png)

画像出典 : https://developer.apple.com/documentation/nearbyinteraction/initiating_and_maintaining_a_session

## 実際に動かしてみる (シミュレータを使用)

シミュレータを2つ起動して、P2P通信で相互のディスカバリトークンを交換して相対位置情報のデータを取得してみた。
`NINearbyObject`のdistanceは問題なく取得できたものの、directionは常にnilが返ってくる状態。こちらに関しては実機で動かさないとダメなのかもしれない。

![nearby.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1795/dff1b44c-0889-f91e-26b7-2754efae5c57.gif)

## まとめ

- かなり近距離に限定はされるetc.制約がいくつかあるが、近くの端末との相互位置を正確に取得できそう
- 今のところ高スペック端末でしか使えない
- 相手の位置情報のデータを取得できる"だけ"なので、結局こっちから何かアクションをしたりするのには、別のチャネル (方法) を使うしかなさそう。APIの思想的にはまぁそれで良いのかもしれない。

ミートアップイベントとか、デーティングアプリの待ち合わせの時とかに使ったら何か面白そうとは思う。一方で制限もそれなりに多いので、まだ実用段階に至っていない印象もうける。どういうユースケースが出てくるか (もしくは全く使われないのか?) 気になります。

## 参考

- [Nearby Interaction \| Apple Developer Documentation](https://developer.apple.com/documentation/nearbyinteraction)
- [Initiating and Maintaining a Session \| Apple Developer Documentation](https://developer.apple.com/documentation/nearbyinteraction/initiating_and_maintaining_a_session)
- [Implementing Interactions Between Users in Close Proximity \| Apple Developer Documentation](https://developer.apple.com/documentation/nearbyinteraction/implementing_interactions_between_users_in_close_proximity)
- [Discovering Peers with MultipeerConnectivity \| Apple Developer Documentation](https://developer.apple.com/documentation/nearbyinteraction/discovering_peers_with_multipeerconnectivity)
- [Multipeer Connectivity \| Apple Developer Documentation](https://developer.apple.com/documentation/multipeerconnectivity)