[Swift][初心者]BLE接続するための情報まとめ

Objective-Cをまだよく理解できてない初心者の私が、SwiftでBLE接続するまでの情報をまとめてみました。
個人メモに近いので間違いがあればご容赦くださいませ。

## 実行環境
* Mac OS X Yosemite 10.7.5
* Xcode version 6.0.1
* iPhone6plus + iOS 8.1.2

## 前提知識
BLE(Bluetooth Low Energy)の仕組みについて基本だけでも理解する必要があります。
私はApple公式ドキュメント [Core Bluetooth プログラミングガイド](https://developer.apple.com/jp/documentation/CoreBluetoothPG.pdf) が非常にわかりやすかったです。
まずはひと通り目を通して以下のキーワードを理解するとよいです。

* セントラルとペリフェラル
* サービスとキャラクタリスティック
* アドバタイズ

## やりたかったこと

* [mbed-HRM1017](http://developer.mbed.org/teams/Switch-Science/wiki/mbed-HRM1017%E3%82%92%E3%81%AF%E3%81%98%E3%82%81%E3%82%88%E3%81%86)にBLE接続してLED点灯(Lチカ)

　　→mbed HRM1017側はwrite用のサービス"FFF0"、キャラクタリスティック"FFF1"と仮定。mbed側のプログラムは対象外。

## 概要説明

iOSアプリのおおまかな流れは以下になります。

1-1. CentralManagerの起動
1-2. CentralManager状態の受信

2-1. Peripheral探索開始
2-2. Peripheral探索結果の受信(複数あれば複数回)

3-1. 指定したPeripheralへ接続開始
3-2. Peripheralへの接続結果の受信

4-1. 利用可能Serviceの探索開始
4-2. Service探索結果の受信

5-1. 利用可能Characteristicの探索開始
5-2. Characterristic探索結果の受信

----ここからは用途に応じて----
*＜Writeの場合＞*
6-1. 指定データの書き込み
6-2. 書き込み結果の受信

*＜Notifyの場合＞*
7-1. 指定データの書き込み
7-2. 書き込み結果の受信

## ソースコード

* StoryBoardで作成したためViewControllerのソースコードのみ記載します。
* あくまで流れを理解するためのサンプルです、コピペはオススメしません。
* 引数にnilを設定するなどApple非推奨の内容も含まれます、ご注意を。

```lang:swift
import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {

    var centralManager: CBCentralManager!
    private var peripheralArray = [CBPeripheral]()
    private var serviceArray = [CBService]()
    private var characteristicArray = [CBCharacteristic]()

    @IBOutlet weak var scanStatus: UILabel!
    @IBOutlet weak var scanStatusText: UITextView!
    @IBOutlet weak var connectStatus: UILabel!
    @IBOutlet weak var sendText: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // ボタン押下でCoreBluetoothを起動
    @IBAction func pushStartBtn(sender: AnyObject) {

        // 1-1. CentralManagerの起動
        self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
    }

    // 1-2. CentralManager状態の受信
    func centralManagerDidUpdateState(central: CBCentralManager!) {

        switch (central.state) {
        case .PoweredOff:
            println("BLE PoweredOff")
        case .PoweredOn:
            println("BLE PoweredOn")
            // 2-1. Peripheral探索開始
            central.scanForPeripheralsWithServices(nil, options: nil)
            /* ↑の第1引数はnilは非推奨。
               該当サービスのCBUUIDオブジェクトの配列が望ましい */
        case .Resetting:
            println("BLE Resetting")
        case .Unauthorized:
            println("BLE Unauthorized")
        case .Unknown:
            println("BLE Unknown")
        case .Unsupported:
            println("BLE Unsupported")
        }
    }

    // 2-2. Peripheral探索結果の受信(複数あれば複数回)
    func centralManager(central: CBCentralManager!,
        didDiscoverPeripheral peripheral: CBPeripheral!,
        advertisementData: [NSObject : AnyObject]!,
        RSSI: NSNumber!){
            
            println("name: \(peripheral.name)")
            println("UUID: \(peripheral.identifier.UUIDString)")
            println("advertisementData: \(advertisementData)")
            println("RSSI: \(RSSI)")

            // 配列に追加
            self.peripheralArray.append(peripheral as CBPeripheral)
    }

    // リストから該当Peripheralを選択し接続を開始
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        // 省電力のためスキャンを停止
        self.centralManager.stopScan()
        
        // 3-1. 指定したPeripheralへ接続開始
        self.centralManager.connectPeripheral(peripheralArray[indexPath.row], options: nil)
    }

    // 3-2. Peripheralへの接続結果の受信(成功時)
    func centralManager(central: CBCentralManager!,
        didConnectPeripheral peripheral: CBPeripheral!)
    {
        println("connection success!")
        // デリゲートの設定
        peripheral.delegate = self
        
        // 今回はサービスを"FFF0"に指定
        let UUID = CBUUID(string: "FFF0")
        // 4-1. 利用可能Serviceの探索開始
        peripheral.discoverServices([UUID])
        /* ↑の引数はnilは非推奨。*/
    }

    // 3-2. Peripheralへの接続結果の受信(失敗時)
    func centralManager(central: CBCentralManager!,
        didFailToConnectPeripheral peripheral: CBPeripheral!,
        error: NSError!)
    {
        println("connection failed")
    }

    // 4-2. Service探索結果の受信
    func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
        
        if (error != nil) {
            println("error: \(error)")
            return
        }
        
        for service in peripheral.services
        {
            self.serviceArray.append(service as CBService)

            // 5-1. 利用可能Characteristicの探索開始
            peripheral.discoverCharacteristics(nil, forService:service as CBService)
            /* ↑の第1引数はnilは非推奨。*/
        }
    }

    // 5-2. Characterristic探索結果の受信
    func peripheral(peripheral: CBPeripheral!,
        didDiscoverCharacteristicsForService service: CBService!,
        error: NSError!)
    {
        if (error != nil) {
            println("error: \(error)")
            return
        }

        for characteristic in service.characteristics {
            self.characteristicArray.append(characteristic as CBCharacteristic)
        }
    }
    
    // ボタン押下で書き込み開始
    @IBAction func pushWriteBtn(sender: AnyObject) {
        
        // 書き込むデータを仮で設定
        var value: UInt = 0x0000FF80808080808000
        let data: NSData = NSData(bytes: &value, length: 10)

        // 6-1. 指定データの書き込み
        [peripheral .writeValue(data, forCharacteristic: characteristicArray[0], type: CBCharacteristicWriteType.WithResponse)]
    }

    // 6-2. 書き込み結果の受信
    func peripheral(peripheral: CBPeripheral!,
        didWriteValueForCharacteristic characteristic: CBCharacteristic!,
        error: NSError!)
    {
        if (error != nil) {
            println("Write...error: \(error)")
            return
        }
        
        println("Write success！")
    }
}
```

## 感想
- サンプルは多数出回っていますが基本はどれも同じ流れです。
- 自分が躓いたのは受信したPeripheralやサービスを保存しておく必要がある点です。これさえ抑えておけば後はどうにかなるかなと。