とりあえずSkyWayでメッセージアプリ

# SkyWayを使ってメッセージアプリ
こんにちは．3回目の投稿です．

今回はWebRTCのプラットフォームSkyWayを使ってビデオ通話ではなくメッセージアプリを簡単に紹介します．
（firebaseを使うとメッセージ通信も簡単にできるのに，なぜskywayを選んだかについては，メッセージ機能を作りたくなった時にあまりfirebaseの知識がなく，調べているうちにたまたまたどり着いたのがSkywayだったんです．）

##　利点/欠点
SkyWayを使うことでの利点は
- 無料で使える（有料なものあるけど使うなら調べてね）
- 簡単に実装できる
- メッセージアプリからビデオ通話アプリにも簡単に実装変更できる．
- 利用ユーザが登録が必要ない
- サーバレス（P2P通信）
だと思います．
これらの利点を活かした使い方としては，一時的なゲームアプリ内でのルームチャットや匿名チャットにはちょうどいい感じかと思います．
（実際，私が使おうと調べた理由は，刑ドロアプリ内でのチーム内チャットの実装のためです）

欠点として感じたのは
- WiFi環境からcellular環境に移った時には通信が行えない（逆も然り，cellular環境→WiFi環境）
- 多人数チャット用にRoomを作りメッセージの送受信ができるが，Roomは接続している人がいなくなると消えてしまう
が挙げられると思います．実際に使用するユースケースも考え，この辺りの対策が必要だと思います．

## 今回の目的
複数人でのメッセージ交換アプリの作成．

## 利用
### 開発者登録
こちらのサイトから簡単に新規登録が行えます．→ [SkyWay：新規登録](https://webrtc.ecl.ntt.com/signup.html)
登録すると仮登録状態になるので，送られてくるメールで本登録にアクティベートしましょう．

### アプリケーション作成
ダッシュボードから新しくアプリケーションを追加するを選択
<img width="787" alt="スクリーンショット 2020-02-01 15.15.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203831/1436cd6f-acf7-9436-faf8-bd5796ef50c6.png">
公式のチュートリアルがかなり詳しく書かれているので参考に作成します．→[チュートリアル](https://webrtc.ecl.ntt.com/js-tutorial.html)
今回は何も入力しなくても大丈夫です．作成すると次のような画面になりAPIキーが表示されます．
<img width="1215" alt="スクリーンショット 2020-02-01 15.51.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203831/ce59bbf4-78fc-bbf6-55a2-ad243213d323.png">
APIキーはあとで使います．


### Xcode
サンプルプロジェクトを適当に作成してpodfileを作成, SkyWay.frameworkを導入してください．

### Swiftプログラム
接続先のRoomは開発者アカウントごとに独立しています．IDを指定してRoomに接続した場合，Roomが存在する場合は接続し，存在しない場合はRoomが生成されます．
Roomの識別子として今回はUUIDを使用しますが，文字列ならば何でもいいみたいです．
#### Peer取得
P2P通信と言っても接続するためには，アドレスなどがわからないと接続できません．
SkywayではP2P接続およびルーム接続機能を操作するためのクラスとしてPeerクラスのインスタンスを最初に作成します．

``` swift:サンプルクラス

import Foundation
import SkyWay

class Sample {
    let APIkey = "APIキー"
    let domain = "localhost"
    var peer: SKWPeer?
    var peerID: String?
    var connectedRoom: SKWRoom?
    
    init() {
        //peerインスタンスの作成
        let option: SKWPeerOption = SKWPeerOption.init()
        option.key = APIkey
        option.domain = domain
        peer = SKWPeer(options: option)
        if let _peer = peer {
            setupPeerCallback(peer: _peer)
        }
    }
   　// peerに対してコールバック処理の設定
    private func setupPeerCallback(peer: SKWPeer) {
        peer.on(.PEER_EVENT_ERROR, callback: { (obj) in
            if let error = obj as? SKWPeerError {
                //エラーの処理．．．
            }
        })
        
        peer.on(.PEER_EVENT_OPEN, callback: {(obj) in
            if let id = obj as? String {
　　　　　　　　　//peer接続がopenした．
                print("your PeerID: \(id)")
            }
        })
        
        peer.on(.PEER_EVENT_CLOSE, callback:{ obj in
            //peer接続がcloseした．
            print("closed your peer")
        })
    }
　　　　・・・
 ```

#### Room接続
RoomのIDさえわかってしまえば誰でも入れます．
（パスワード付きに出来るのかは調べてないです．ざっと見た感じなかったと思います）

```swift:room接続
  open func roomIn(_ roomID: String) -> Bool {
        let option = SKWRoomOption()
        option.mode = .ROOM_MODE_MESH
        guard let peer = peer else {
            print("peerが確立されていません．")
            return
        }
        
        if let roomConnection = peer.joinRoom(withName: roomID, options: option) {
            print("connected in Room")
            self.connectedRoom = roomConnection
            setupRoomCallback(roomConnection)
            
        } else {
            print("Roomへの接続が失敗しました．")
        }
    }
    //Roomのコールバック処理の設定
    private func setupRoomCallback(_ room: SKWRoom) {
        room.on(.ROOM_EVENT_ERROR, callback: { obj in
           //Roomイベントのエラー時の処理を
        })
        
        room.on(.ROOM_EVENT_OPEN, callback: { obj in
        　　//Roomに入った時の処理を
        })
        
        room.on(.ROOM_EVENT_CLOSE, callback: { obj in
            //Roomから出た時の処理を
        })
        
        room.on(.ROOM_EVENT_DATA, callback: { obj in
            //データを受信した時の処理を
        })
        
        room.on(.ROOM_EVENT_LOG, callback: { obj in
            //Roomのイベントログが返ってきた時の処理を
        })
    }
```

#### メッセージの送信
この関数を呼び出すことでデータの送信ができます．

```swift:メッセージの送信
    open func send(strData: String){
        guard let room = connectedRoom else {
            print("おそらく接続していません")
            return
        }
        room.send(strData as NSString)
    }
    
    open func send(data: NSData) {
        guard let room = connectedRoom else {
            print("おそらく接続していません")
            return
        }
        room.send(data)
    }

```



#### メッセージの受信

```swift:受信
        room.on(.ROOM_EVENT_DATA, callback: { obj in
              if let data = obj as? SKWRoomDataMessage {
              let src = data.str //送信者
              let mess = data.data as! String　//受信データ
　　　　　　　　　//ここに処理を書く 
              }
        })
```


### コード全体
下のようなクラスを作成してインスタンスとして使うのが無難な気がします．

``` swift:サンプルコード
//
//  QIita記事用サンプルコード.swift
//
//  Created by KEI on 2020/02/04.
//  Copyright © 2020 KEI. All rights reserved.
//

import Foundation
import SkyWay

class Sample {
    private　let APIkey = "APIキー"
    private let domain = "localhost"
    private var peer: SKWPeer?
    var connectedRoom: SKWRoom?
    
    init() {
        let option: SKWPeerOption = SKWPeerOption.init()
        option.key = APIkey
        option.domain = domain
        peer = SKWPeer(options: option)
        if let _peer = peer {
            setupPeerCallback(peer: _peer)
        }
    }
    
    private func setupPeerCallback(peer: SKWPeer) {
        peer.on(.PEER_EVENT_ERROR, callback: { (obj) in
            if let error = obj as? SKWPeerError {
                //エラーの処理．．．
            }
        })
        
        peer.on(.PEER_EVENT_OPEN, callback: {(obj) in
            if let id = obj as? String {
                print("your PeerID: \(id)")
            }
        })
        
        peer.on(.PEER_EVENT_CLOSE, callback:{ obj in
            print("peer 接続がcloseしました．")
            
        })
    }
    
    open func roomIn(_ roomID: String) -> Bool {
        let option = SKWRoomOption()
        option.mode = .ROOM_MODE_MESH
        guard let peer = peer else {
            print("peerが確立されていません．")
            return
        }
        
        if let roomConnection = peer.joinRoom(withName: roomID, options: option) {
            print("connected in Room")
            self.connectedRoom = roomConnection
            setupRoomCallback(roomConnection)
            
        } else {
            print("Roomへの接続が失敗しました．")
        }
    }
    
    private func setupRoomCallback(_ room: SKWRoom) {
        room.on(.ROOM_EVENT_ERROR, callback: { obj in
            
        })
        
        room.on(.ROOM_EVENT_OPEN, callback: { obj in
        
        })
        
        room.on(.ROOM_EVENT_CLOSE, callback: { obj in
            
        })
        
        room.on(.ROOM_EVENT_DATA, callback: { obj in
            
        })
        
        room.on(.ROOM_EVENT_LOG, callback: { obj in
            
        })
    }
    
    open func send(strData: String){
        guard let room = connectedRoom else {
            print("おそらく接続していません")
            return
        }
        room.send(strData as NSString)
    }
    
    open func send(data: NSData) {
        guard let room = connectedRoom else {
            print("おそらく接続していません")
            return
        }
        room.send(data)
    }
}


 ````

## 最後
初心者には使いやすいライブラリだと思います．
また，開発はNTTの社内会社（？？）が行なっているみたいなので公式ドキュメントも日本語でわかりやすかったです．


## 参考サイト
[公式ドキュメント](https://webrtc.ecl.ntt.com/developer.html) <-　日本語なのでわかりやすい！！
[SkyWay初心者からステップアップしよう](https://qiita.com/yusuke84/items/13fab1d0f97c466e4d4b) <-　おそらく開発者さまの記事です！