BLEのペリフェラルを今更実装してみた(iOS編)

# BLEのペリフェラルを今更実装してみた(iOS編)

## 準備

### (1) BLE用のUUIDを作成する

ターミナルを起動して以下コマンドを入力

```
$ uuidgen
```

すると以下のようなUUIDが自動生成されるのでコピーしておく

```
71DA3FD1-7E10-41C1-B16F-4430B506CDE7
```

CCUUIDとして定義

```swift:BLEDefine.swift

/** BLEで用いるサービス用のUUID */
let BLEServiceUUID = CBUUID(string:"71DA3FD1-7E10-41C1-B16F-4430B506CDE7")

/** BLEで用いるキャラクタリスティック用のUUID */
let BLECharacteristicUUID = CBUUID(string:"E977DFF0-36D0-4B3B-89D5-575AAE17B6B3")
```

### (2) CoreBluetooth Frameworkを追加

![add_corebluetooth_framework.png](https://qiita-image-store.s3.amazonaws.com/0/46751/024bac92-24d6-f1c8-21f6-fcd1b6150a2b.png)

### (3) CoreBluetoothをインポート

使うファイル内の先頭辺りでインポート文を書く

```swift

import CoreBluetooth

```

## CoreBluetoothのEnum値

ログを出すときに楽なので name プロパティを追加しておきます

```swift:BLEEnumExtension.swift
extension CBCentralManagerState
{
    var name : NSString {
        get{
            var enumName = "CBCentralManagerState"
            var valueName = ""
            
            switch self {
            case .PoweredOff:
            valueName = enumName + "PoweredOff"
            case .PoweredOn:
            valueName = enumName + "PoweredOn"
            case .Resetting:
            valueName = enumName + "Resetting"
            case .Unauthorized:
            valueName = enumName + "Unauthorized"
            case .Unknown:
            valueName = enumName + "Unknown"
            case .Unsupported:
            valueName = enumName + "Unsupported"
            }
            
            return valueName
        }
    }
}

extension CBPeripheralState
{
    var name : NSString {
        get{
            var enumName = "CBPeripheralState"
            var valueName = ""
            
            switch self {
            case .Connected:
                valueName = enumName + "Connected"
            case .Connecting:
                valueName = enumName + "Connecting"
            case .Disconnected:
                valueName = enumName + "Disconnected"
            }
            
            return valueName
        }
    }
}

extension CBPeripheralManagerState
{
    var name : NSString {
        get{
            var enumName = "CBPeripheralManagerState"
            var valueName = ""
            
            switch self {
            case .PoweredOff:
                valueName = enumName + "PoweredOff"
            case .PoweredOn:
                valueName = enumName + "PoweredOn"
            case .Resetting:
                valueName = enumName + "Resetting"
            case .Unauthorized:
                valueName = enumName + "Unauthorized"
            case .Unknown:
                valueName = enumName + "Unknown"
            case .Unsupported:
                valueName = enumName + "Unsupported"
            }
            
            return valueName
        }
    }
}
```

## 実装

```swift:BLEPeripheralManager.swift

class BLEPeripheralManager : NSObject {
    
    // MARK: - Property
    
    /** シングルトンインスタンス */
    class var sharedManager : BLEPeripheralManager {
        struct Static {
            static let instance : BLEPeripheralManager = BLEPeripheralManager()
        }
        return Static.instance
    }
    
    /** BLEのペリフェラルマネージャー、BLE発信側としての機能をサポートする */
    private var peripheralManager : CBPeripheralManager?
    
    /** 発信するキャラクタリスティック */
    private var characteristic = CBMutableCharacteristic(
        type:BLECharacteristicUUID,
        properties:CBCharacteristicProperties.Notify,
        value:nil,
        permissions:CBAttributePermissions.Readable)
    
    /** 発信するデータ */
    var dataToSend = NSData()
    
    /** 発信するデータのインデックス */
    private var sendDataIndex = 0
    
    /** フラグ値 */
    private var sendingEOM : Bool = false
    
    // MARK: - Public
    
    /** BLEのペリフェラルを使用開始できる状態にセットアップ */
    func setup()
    {
        self.peripheralManager = CBPeripheralManager(delegate:self, queue:nil)
    }
    
    /** BLEのペリフェラルを使用できない状態にする */
    func teardown()
    {
        self.peripheralManager = nil
    }
    
    /** アドバタイズを開始 */
    func startAdvertising()
    {
        self.peripheralManager?.startAdvertising([CBAdvertisementDataServiceUUIDsKey:[BLEServiceUUID]])
    }
    
    /** アドバタイズを停止 */
    func stopAdvertising()
    {
        self.peripheralManager?.stopAdvertising()
    }
    
    // MARK: Private
    
    /** データを送信する */
    func sendData()
    {
        if self.sendingEOM {
            
            var data = "EOM".dataUsingEncoding(NSUTF8StringEncoding)
            
            var didSend : Bool = self.peripheralManager!.updateValue(
                data,
                forCharacteristic:self.characteristic,
                onSubscribedCentrals: nil)
            
            if didSend {
                self.sendingEOM = false
                return
            }
        }
        
        var didSend = true
        
        while didSend {
            
            var amountToSend = self.dataToSend.length - self.sendDataIndex
            
            if amountToSend > 20 {
                amountToSend = 20
            }
            
            var chunk = NSData(bytes:self.dataToSend.bytes+self.sendDataIndex, length: amountToSend)
            
            didSend = self.peripheralManager!.updateValue(
                chunk,
                forCharacteristic:self.characteristic,
                onSubscribedCentrals: nil)
            
            if !didSend {
                return
            }
            
            var stringFromData = NSString(data:chunk, encoding:NSUTF8StringEncoding)
            
            self.sendDataIndex += amountToSend
            
            if self.sendDataIndex >= self.dataToSend.length {
                
                self.sendingEOM = false
                
                let eomSent : Bool = self.peripheralManager!.updateValue(
                    "EOM".dataUsingEncoding(NSUTF8StringEncoding),
                    forCharacteristic:self.characteristic,
                    onSubscribedCentrals: nil)
                
                if eomSent {
                    sendingEOM = false
                }
                
                return
            }
        }
    }
}

// MARK: - CBPeripheralManagerDelegate

extension BLEPeripheralManager : CBPeripheralManagerDelegate
{
    func peripheralManager(
        peripheral: CBPeripheralManager!,
        central: CBCentral!,
        didSubscribeToCharacteristic characteristic: CBCharacteristic!)
    {
        self.sendDataIndex = 0
        self.sendData()
    }
    
    func peripheralManager(
        peripheral: CBPeripheralManager!,
        central: CBCentral!,
        didUnsubscribeFromCharacteristic characteristic: CBCharacteristic!)
    {
        println("didUnsubscribeFromCharacteristic")
    }
    
    func peripheralManager(
        peripheral: CBPeripheralManager!,
        didAddService service: CBService!,
        error: NSError!)
    {
        if error == nil {
            println("正常にサービスの追加が完了しました")
        } else {
            println("サービスの追加に失敗しました")
        }
    }
    
    func peripheralManager(
        peripheral: CBPeripheralManager!,
        didReceiveReadRequest request: CBATTRequest!)
    {
        println("didReceiveReadRequest")
    }
    
    func peripheralManager(
        peripheral: CBPeripheralManager!,
        didReceiveWriteRequests requests: [AnyObject]!)
    {
        println("didReceiveWriteRequests")
    }
    
    func peripheralManager(
        peripheral: CBPeripheralManager!,
        willRestoreState dict: [NSObject : AnyObject]!)
    {
        println("willRestoreState")
    }
    
    func peripheralManagerDidStartAdvertising(
        peripheral: CBPeripheralManager!,
        error: NSError!)
    {
        if error == nil {
            println("PeripheralがAdvertisingを開始しました")
        } else {
            println("PeripheralがAdvertisingの開始に失敗しました\(error)")
        }
    }
    
    func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!)
    {
        println("PeripheralのStateが変更されました(現在のState:\(peripheral.state.name))")
        
        if peripheral.state != CBPeripheralManagerState.PoweredOn {
            println("異常なStateのため処理を終了します")
            return;
        }
        
        var service = CBMutableService(type:BLEServiceUUID, primary:true)
        service.characteristics = [self.characteristic]
        self.peripheralManager?.addService(service)
    }
    
    func peripheralManagerIsReadyToUpdateSubscribers(peripheral: CBPeripheralManager!)
    {
        self.sendData()
    }
}

```