iOSでBluetooth通信するサンプルコード

# 概要
* iPhoneでBluetooth通信を動かしてみたかったので、かんたんなサンプルを書いてみました
* **基本的に、`MultipeerConnectivity`で用意されている各クラスの初期化とデリゲートの実装だけ行えば、簡単にBluetooth通信ができました**

# つくったもの(Bluetooth通信のサンプル)
* 画面をタップすると、自分の端末に円がアニメーション表示されます
* Bluetooth通信で座標を送り、相手の端末にも同じ円が表示します
* 長押しで円がでかくなります
* スプラトゥーン風にしたかったのですが、**だいぶコレジャナイ**

<img width="150" alt="sample.png" src="https://qiita-image-store.s3.amazonaws.com/0/25820/05ad9902-a276-c698-60ac-f6448d4d051e.png">

# サンプルコード
* [BluetoothGameSample](https://github.com/ohkawa/BluetoothGameSample)
 * (エラー処理などいろいろ足りないです。とりあえずのサンプルになってます)

# 実行時の注意
* 端末のBluetooth設定をONにする必要があります(「設定」アプリ > Bluetooth > オン)
* アプリ起動すると、周囲に同じアプリが起動していないか探しに行きます(60秒タイムアウト)
* Bluetooth通信は[シミュレータでは試せないようです](https://www.facebook.com/iOSDevTips/posts/875931699094759)

# 要点
## 0.使用したクラスについて
* [参考サイト](http://dev.classmethod.jp/references/ios-multipeer-apiusage/)にもあるように、`MCBrowserViewController`を使うと簡単に接続先の選択~接続の確立までをUI付きで提供してくれます。
* ただ、今回は接続先の **選択画面を挟みたくなかった** ので、`MCBrowserViewController`の代わりに`MCNearbyServiceBrowser`を使用し、最初に見つけた端末に即接続しに行っています

## 1.Bluetooth通信準備
* サンプルではちょっと処理を加えていますが、Bluetooth通信に最低限必要な初期化処理は以下のはず。
* `MCNearbyServiceAdvertiser`や`MCNearbyServiceBrowser`の役割については、[参考サイト](http://www.gaprot.jp/pickup/ios7/vol3/)の招待状の例えがわかりやすかったです

```swift
    func initialize() {
        self.peerID = MCPeerID(displayName: UIDevice.currentDevice().name)
        self.session = MCSession(peer: peerID)
        self.session.delegate = self
        
        self.advertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: nil, serviceType: serviceType)
        self.advertiser.delegate = self
        self.advertiser.startAdvertisingPeer()
        
        self.browser = MCNearbyServiceBrowser(peer: peerID, serviceType: serviceType)
        self.browser.delegate = self
        self.browser.startBrowsingForPeers()
    }
```

```swift
    // MARK: - MCNearbyServiceAdvertiserDelegate
    func advertiser(advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: NSData?, invitationHandler: (Bool, MCSession) -> Void) {
        invitationHandler(true, self.session);
    }
```


```swift
    // MARK: - MCNearbyServiceBrowserDelegate
    func browser(browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
        self.browser.invitePeer(peerID, toSession: session, withContext: nil, timeout: 60)
    }
```

## 2.データの送信
* `ViewController`のタッチイベントで、「相手端末への座標の送信」と「自分の座標への円の描画」を行っています
* 以下はデータ送信部分

```swift:NearbyManager.swift
    func sendMessage(point : PointModel) {
        do {
            let data = NSKeyedArchiver.archivedDataWithRootObject(point)
            try self.session.sendData(data,
                toPeers: self.session.connectedPeers,
                withMode: MCSessionSendDataMode.Unreliable)
        } catch {
            print(error)
        }
    }
```

## 3.データの受信
* 受け取った座標データに従って、相手の円を描画しています
* 以下はデータ受信部分

```swift:NearbyManager.swift
    // MARK: - MCSessionDelegate
    func session(session: MCSession, didReceiveData data: NSData,
        fromPeer peerID: MCPeerID)  {
            dispatch_async(dispatch_get_main_queue()) {
                let point = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! PointModel
                self.canvasVC?.paintEnemyPuddle(point)
            }
    }
```

# まとめと課題
* `MultipeerConnectivity`で用意されている各クラスのデリゲートを実装すれば、Bluetooth通信は簡単に実装できる
* いろいろ考慮が足りていないので、実際に使うとなるといろいろ手を入れる必要がありそう
 * この実装だと通信のタイムラグで2端末間の表示にズレが出ることがあるので、少なくともユーザのアクションの実行時間はデータとして持たせる必要がある
 * エラー処理全般を入れていないので、相手が遠く離れた時やアプリを終了したときなど対応が必要
 * 上記に関連するが、状況によってデータが相手に届かないことがある。ロストしたデータのフォローも必要

# 参考
* [Multipeer Connectivity Framework Reference](https://developer.apple.com/library/ios/documentation/MultipeerConnectivity/Reference/MultipeerConnectivityFramework/index.html)
* [[iOS 7] P2P 通信を手軽に実現する Multipeer Connectivity Framework を使ってみる ｜ Developers.IO](http://dev.classmethod.jp/references/ios-multipeer-apiusage/) ←よくまとまっていて一番参考になりました
* [第 3 回・iOSでMultipeerConnectivityを実装してみよう！ | ピックアップ | ギャップロ](http://www.gaprot.jp/pickup/ios7/vol3/)