
# 任意の JSON 値の送受信

シグナリングメッセージの一部には、ユーザーが任意の JSON 値を送受信するためのフィールドが含まれます。
本章では JSON 値の送受信について説明します。
なお、シグナリングメッセージの詳細は [WebRTC SFU Sora ドキュメント - WebSocket 経由のシグナリング](https://sora-doc.shiguredo.jp/SIGNALING) をご確認ください。

## シグナリングメッセージ

シグナリングメッセージは `Signaling` 列挙型と種別ごとのクラスで表されます。
メッセージの種別と対応する `Signaling` 列挙子の定義を次に示します。
詳細は [API リファレンス](api.html#72dc36) をご確認ください。

- type: connect- `Signaling.connect(SignalingConnect)`
- type: offer- `Signaling.offer(SignalingOffer)`
- type: answer- `Signaling.answer(SignalingAnswer)`
- type: update- `Signaling.update(SignalingUpdate)`
- type: candidate- `Signaling.candidate(SignalingCandidate)`
- type: notify(SignalingNotify)- `Signaling.notify(SignalingNotify)`
- type: ping- `Signaling.ping(SignalingPing)`
- type: pong- `Signaling.pong(SignalingPong)`
- type: disconnect- `Signaling.disconnect`
- type: push- `Signaling.push(SignalingPush)`

上記のうち、任意の JSON 値を含むことができるメッセージとプロパティを次に示します。

- type: connect (送信のみ)- `metadata`
  - `notifyMetadata`
- type: notify (受信のみ)- `authnMetadata`
  - `authzMetadata`
  - `metadata`
- type: push (受信のみ)- `data`

## 任意のJSON 値を送信する

任意の JSON 値を含むことができる送信用のメッセージは type: connect のみです。
`metadata` と `notifyMetadata` のメタデータを指定できます。

メタデータは `Configuration` の次のプロパティで指定できます。
どちらも `Encodable` プロトコルに準拠した値を指定します。

- `metadata` -> `Configuration.signalingConnectMetadata`
- `notifyMetadata` -> `Configuration.signalingConnectNotifyMetadata`

これらのプロパティの値は JSON に変換されて送信されます。
基本的なデータ型 (`Bool`, `Int`, `Float`, `String`, `Array`, `Dictionary`) は `Encodable` に対応しているので、
これらの値の組み合わせであればエンコード処理を実装する必要はありません。

上記のプロパティの内容と生成される JSON を次に示します。

```swift
// {"type":"connect", "metadata":"contents", ...}
config.signalingConnectMetadata = "contents"

// {"type":"connect", "metadata":{"key":"value"}, ...}
config.signalingConnectMetadata = ["key": "value"]
```

送信されるメタデータの内容を確認するには、デバッグログを有効にしてシグナリングの内容をコンソールに出力してください。
詳細は [SDK のログをコンソールに出力する](devguide.html#e94c1e) をご確認ください。

## 受信した任意の JSON 値を取得する

受信用のメッセージの type: notify と type: push の一部のプロパティは任意の JSON 値を含むことができます。
任意の JSON 値の型は `Any?` です。
受信した JSON 値は `JSONSerialization` クラスで解析されます。
解析後のオブジェクトは `NSString`, `NSNumber`, `NSArray`, `NSDictionary`, `NSNull` のいずれかです。

プロパティはシグナリング用のイベントハンドラで取得できます。
受信したメッセージ及び同メッセージを含む WebSocket メッセージを取得できるイベントハンドラを次に示します。
WebSocket メッセージはテキストなので、手動で JSON を解析する必要があります。

- `MediaChannelHandlers.onReceiveSignaling`: 受信したメッセージを取得します。
- `WebSocketChannelHandlers.onReceive`: 受信したメッセージを含む WebSocket メッセージを取得します。

イベントハンドラで JSON 値を取得する例を次に示します。

```swift
// MediaChannelHandlers でメッセージを取得する
mediaChannel.handlers.onReceiveSignaling = { signaling in
    switch signaling {
    
    // type: notify
    case .notify(let notify):
        switch notify.eventType {
        case "connection.created":
            // メタデータに含まれる JSON 値 (Any) を任意の型にキャストする
            if let metadata = notify.metadata as? [String: Any] {
                print("metadata => \(metadata)")
            }
            if let authnMetadata = notify.authnMetadata as? [String: Any] {
                print("authn => \(authnMetadata)")
            }
            if let authzMetadata = notify.authzMetadata as? [String: Any] {
                print("authz => \(authzMetadata)")
            }
            ...
        
        // 他のイベント種別でも同様に処理する
        case "connection.created":
            ...

        default:
            ...
        }

    // type: push
    case .push(let push):
        if let data = push.data as? [String: Any] {
            print("data => \(data)")
        }
        ...

    default:
        ...
    }
}

// WebSocket メッセージを手動で解析する場合
mediaChannel.webSocketChannel.handlers.onReceive = { message in
    switch message {
    case .text(let text):
        // 受信したシグナリングのテキストデータを JSON データとして解析する
        if let data = text.data(using: .utf8) {
            do {
                if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                    switch json["type"] as? String {
                    case "notify":
                        // type: notify
                        print("notify metadata => \(json["metadata"])")
                        ...
                    case "push":
                        // type: push
                        print("push data => \(json["data"])")
                        ...
                    default:
                        ...
                    }
                }
            } catch let error {
                // JSON の解析に失敗した場合のエラー処理
                // ここに来ることはないので無視してよい
            }
        }
        ...
    case .binary(_):
        // シグナリングでバイナリデータは来ないので何もしなくてよい
        ...
    }
}
```
