Core Bluetooth with Swift （ObjCのおまけ付き）

**Core Bluetooth の Swift コード + Objective-C のおまけ付きスニペット集**です。

Core Bluetooth の各種メソッドでは実行キューを指定できたりオプションを指定できたりとか色々ありますが、**シンプルにするためここでは省けるものはできるだけ省く**ようにしています。

###更新メモ

- 2015.10.12 Swift 2 対応


##（本題に入る前に・・・）

このスニペット集は、下記書籍の執筆にあたりまとめたものです。セントラルとペリフェラルの実装の基礎的なところは、**ObjC・Swift両方**のサンプルを載せています。 

<div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/51XxjzdT%2BKL._SL160_.jpg" alt="iOS×BLE Core Bluetoothプログラミング" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/" name="amazletlink" target="_blank">iOS×BLE Core Bluetoothプログラミング</a><div class="amazlet-powered-date" style="font-size:80%;margin-top:5px;line-height:120%">posted with <a href="http://www.amazlet.com/" title="amazlet" target="_blank">amazlet</a> at 15.03.11</div></div><div class="amazlet-detail">堤 修一 松村 礼央 <br />ソシム <br />売り上げランキング: 1,106<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>

**本記事はコードだけですが、書籍内ではかなり丁寧に解説**してあります！

**iOS x BLE というニッチな内容で480ページ！** どうぞよろしくお願いします。

- [技術書『iOS×BLE Core Bluetooth プログラミング』の紹介 - Qiita](http://qiita.com/shu223/items/e1c91f176a48475cd2ad)
  - 書籍の詳しい紹介
- <a href="http://d.hatena.ne.jp/shu223/20150324/1427086780" >BLEと私 - Over&Out その後</a>
  - 書籍を書くまでの経緯

##インポート

何はともあれまずは import から。

```objc:objc
@import CoreBluetooth;
```

```swift:swift
import CoreBluetooth
```

#セントラルマネージャ編

##スキャン、接続

セントラルマネージャを初期化して、スキャン、発見したペリフェラルに接続するところまで。

###プロトコル準拠の宣言とプロパティ定義

```objc:objc
@interface SomeClass () <CBCentralManagerDelegate>
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@end
```

```swift:swift
class SomeClass: SomeSuperclass, CBCentralManagerDelegate {
    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
```

###CBCentralManagerを初期化する

```objc:objc
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
```

```swift:swift
centralManager = CBCentralManager(delegate: self, queue: nil)
```

###セントラルマネージャの状態変化を取得する

※このデリゲートメソッドは `@required` なので必須。

```objc:objc
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {

    NSLog(@"state:%ld", (long)central.state);
}
```

```swift:swift
func centralManagerDidUpdateState(central: CBCentralManager) {

    print("state: \(central.state)")
}
```

###スキャンを開始する


```objc:objc
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
```

```swift:swift
centralManager.scanForPeripheralsWithServices(nil, options: nil)
```

###スキャン結果を受け取る

```objc:objc
- (void)   centralManager:(CBCentralManager *)central
    didDiscoverPeripheral:(CBPeripheral *)peripheral
        advertisementData:(NSDictionary *)advertisementData
                     RSSI:(NSNumber *)RSSI
{
    NSLog(@"peripheral：%@", peripheral);
}
```

```swift:swift
func centralManager(central: CBCentralManager,
    didDiscoverPeripheral peripheral: CBPeripheral,
    advertisementData: [String : AnyObject],
    RSSI: NSNumber!)
{
    print("peripheral: \(peripheral)")
}
```

###スキャンを停止する

```objc:objc
[self.centralManager stopScan];
```

```swift:swift
centralManager.stopScan()
```

###ペリフェラルへの接続を開始する

```objc:objc
[self.centralManager connectPeripheral:peripheral options:nil];

```

```swift:swift
centralManager.connectPeripheral(peripheral, options: nil)
```

###接続結果を取得する

```objc:objc
// ペリフェラルへの接続が成功すると呼ばれる
- (void)  centralManager:(CBCentralManager *)central
    didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"connected!");
}

// ペリフェラルへの接続が失敗すると呼ばれる
- (void)        centralManager:(CBCentralManager *)central
    didFailToConnectPeripheral:(CBPeripheral *)peripheral
                         error:(NSError *)error
{
    NSLog(@"failed...");
}
```

```swift:swift
// ペリフェラルへの接続が成功すると呼ばれる
func centralManager(central: CBCentralManager,
    didConnectPeripheral peripheral: CBPeripheral)
{
    print("connected!")
}

// ペリフェラルへの接続が失敗すると呼ばれる
func centralManager(central: CBCentralManager,
    didFailToConnectPeripheral peripheral: CBPeripheral,
    error: NSError?)
{
    print("failed...")
}
```

##サービス・キャラクタリスティック探索

接続したペリフェラルのサービス・キャラクタリスティックを探索する

###CBPeripheralDelegate プロトコルへの準拠を宣言する

```objc:objc
@interface SomeClass () <CBCentralManagerDelegate, CBPeripheralDelegate>
```

```swift:swift
class SomeClass: SomeSuperclass, CBCentralManagerDelegate, CBPeripheralDelegate {
```

###サービス探索を開始する

```objc:objc
peripheral.delegate = self;
[peripheral discoverServices:nil];
```

```swift:swift
peripheral.delegate = self
peripheral.discoverServices(nil)
```

###サービス探索結果を受け取る

```objc:objc
- (void)     peripheral:(CBPeripheral *)peripheral
    didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"error: %@", error);
        return;
    }
    
    NSArray *services = peripheral.services;
    NSLog(@"Found %lu services! :%@", (unsigned long)services.count, services);
}
```

```swift:swift
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
    
    if let error = error {
        print("error: \(error)")
        return
    }

    let services = peripheral.services
    print("Found \(services.count) services! :\(services)")
}
```

###キャラクタリスティック探索を開始する

```objc:objc
[peripheral discoverCharacteristics:nil forService:service];
```

```swift:swift
peripheral.discoverCharacteristics(nil, forService: service)
```

###キャラクタリスティック探索結果を取得する

```objc:objc
- (void)                      peripheral:(CBPeripheral *)peripheral
    didDiscoverCharacteristicsForService:(CBService *)service
                                   error:(NSError *)error
{
    if (error) {
        NSLog(@"error: %@", error);
        return;
    }
    
    NSArray *characteristics = service.characteristics;
    NSLog(@"Found %lu characteristics! : %@", (unsigned long)characteristics.count, characteristics);
}
```

```swift:swift
func peripheral(peripheral: CBPeripheral,
    didDiscoverCharacteristicsForService service: CBService,
    error: NSError?)
{
    if let error = error {
        print("error: \(error)")
        return
    }
    
    let characteristics = service.characteristics
    print("Found \(characteristics.count) characteristics! : \(characteristics)")
}
```

##Read

###Readを開始する

```objc:objc
[peripheral readValueForCharacteristic:characteristic];
```

```swift:swift
peripheral.readValueForCharacteristic(characteristic)
```

###Read結果を取得する

```objc:objc
- (void)                 peripheral:(CBPeripheral *)peripheral
    didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
                              error:(NSError *)error
{
    if (error) {
        NSLog(@"Failed... error: %@", error);
        return;
    }
    
    NSLog(@"Succeeded！ service uuid:%@, characteristice uuid:%@, value%@",
          characteristic.service.UUID, characteristic.UUID, characteristic.value);
}
```

```swift:swift
func peripheral(peripheral: CBPeripheral,
    didUpdateValueForCharacteristic characteristic: CBCharacteristic,
    error: NSError?)
{
    if let error = error {
        print("Failed... error: \(error)")
        return
    }
    
    print("Succeeded! service uuid: \(characteristic.service.UUID), characteristic uuid: \(characteristic.UUID), value: \(characteristic.value)")
}
```

##Write

###データ書き込みを開始する

```objc:objc
[peripheral writeValue:data
     forCharacteristic:characteristic
                  type:CBCharacteristicWriteWithResponse];
```

```swift:swift
peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithResponse)
```

####NSData オブジェクトの生成例その1：文字列から

```objc:objc
NSData *data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
```

```swift:swift
let data = "hello".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true)
```

####NSData オブジェクトの生成例その2：1バイトのunsignedな整数値

```objc:objc
unsigned char value = 0x01;
NSData *data = [[NSData alloc] initWithBytes:&value length:1];
```

```swift:swift
var value: CUnsignedChar = 0x01
let data = NSData(bytes: &value, length: 1)
```


###データ書き込み結果を取得する

```objc:objc
- (void)                peripheral:(CBPeripheral *)peripheral
    didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
                             error:(NSError *)error
{
    if (error) {
        NSLog(@"Write失敗...error:%@", error);
        return;
    }
    
    NSLog(@"Write成功！");
}
```

```swift:swift
func peripheral(peripheral: CBPeripheral,
    didWriteValueForCharacteristic characteristic: CBCharacteristic,
    error: NSError?)
{
    if let error = error {
        print("Write失敗...error: \(error)")
        return
    }
    
    print("Write成功！")
}
```



##Notify

###データ更新通知の受け取りを開始する

```objc:objc
[peripheral setNotifyValue:YES
         forCharacteristic:characteristic];
```

```swift:swift
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
```

###データ更新通知の受け取りを停止する

```objc:objc
[peripheral setNotifyValue:NO
         forCharacteristic:characteristic];
```

```swift:swift
peripheral.setNotifyValue(false, forCharacteristic: characteristic)
```

###データ更新通知受け取り開始／停止結果を取得する

```objc:objc
- (void)                             peripheral:(CBPeripheral *)peripheral
    didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
                                          error:(NSError *)error
{
    if (error) {
        NSLog(@"Notify状態更新失敗...error:%@", error);
    }
    else {
        NSLog(@"Notify状態更新成功！ isNotifying:%d", characteristic.isNotifying);
    }
}
```

```swift:swift
func peripheral(peripheral: CBPeripheral,
    didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic,
    error: NSError?)
{
    if let error = error {        
        print("Notify状態更新失敗...error: \(error)")
    } else {
        print("Notify状態更新成功！ isNotifying: \(characteristic.isNotifying)")
    }
}
```

###データ更新通知を受け取る

```objc:objc
- (void)                 peripheral:(CBPeripheral *)peripheral
    didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
                              error:(NSError *)error
{
    if (error) {
        NSLog(@"データ更新通知エラー:%@", error);
        return;
    }
    
    NSLog(@"データ更新！ characteristic UUID:%@, value:%@",
          characteristic.UUID, characteristic.value);
}
```

```swift:swift
func peripheral(peripheral: CBPeripheral,
    didUpdateValueForCharacteristic characteristic: CBCharacteristic,
    error: NSError?)
{
    if let error = error {
        print("データ更新通知エラー: \(error)")
        return
    }
    
    print("データ更新！ characteristic UUID: \(characteristic.UUID), value: \(characteristic.value)")
}
```

#ペリフェラル編

##アドバタイズ

###プロトコル準拠の宣言とプロパティ定義

```objc:objc
@interface SomeClass () <CBPeripheralManagerDelegate>
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@end
```

```swift:swift
class SomeClass: SomeSuperclass, CBPeripheralManagerDelegate {
    var peripheralManager: CBPeripheralManager!
```

###CBPeripheralManager を初期化する


```objc:objc
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                                 queue:nil
                                                               options:nil];
```

```swift:swift
peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
```

###ペリフェラルマネージャの状態変化を取得する

```objc:objc
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {

    NSLog(@"state:%ld", (long)peripheral.state);
}
```

```swift:swift
func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager) {
    
    print("state: \(peripheral.state)")
}
```


###アドバタイズを開始する

```objc:objc
NSDictionary *advertisementData = @{CBAdvertisementDataLocalNameKey: @"Test Device"};
[self.peripheralManager startAdvertising:advertisementData];
```

```swift:swift
let advertisementData = [CBAdvertisementDataLocalNameKey: "Test Device"]
peripheralManager.startAdvertising(advertisementData)
```


###アドバタイズ開始処理の結果を取得する

```objc:objc
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
                                       error:(NSError *)error
{
    if (error) {
        NSLog(@"Failed... error:%@", error);
        return;
    }
    
    NSLog(@"Succeeded!");
}
```

```swift:swift
func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager, error: NSError?) {
    
    if let error = error {
        print("Failed... error: \(error)")
        return
    }

    print("Succeeded!")
}
```

###アドバタイズを停止する

```objc:objc
[self.peripheralManager stopAdvertising];
```

```swift:swift
peripheralManager.stopAdvertising()
```

##サービス追加

###サービスを作成する

```objc:objc
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
CBMutableService *service = [[CBMutableService alloc] initWithType:serviceUUID
                                                           primary:YES];
```

```swift:swift
let serviceUUID = CBUUID(string: kServiceUUID)
let service = CBMutableService(type: serviceUUID, primary: true)
```

###キャラクタリスティックを作成する

```objc:objc
CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];

CBCharacteristicProperties properties = (
                                         CBCharacteristicPropertyNotify |
                                         CBCharacteristicPropertyRead |
                                         CBCharacteristicPropertyWrite
                                         );
CBAttributePermissions permissions = (CBAttributePermissionsReadable | CBAttributePermissionsWriteable);
CBMutableCharacteristic *characteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID
                                                                             properties:properties
                                                                                  value:nil
                                                                            permissions:permissions];
```

```swift:swift
let characteristicUUID = CBUUID(string: kCharacteristicUUID)
let properties: CBCharacteristicProperties = [.Notify, .Read, .Write]
let permissions: CBAttributePermissions = [.Readable, .Writeable]
let characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: properties,
    value: nil, permissions: permissions)
```


###サービスにキャラクタリスティックを追加

```objc:objc
service.characteristics = @[characteristic];
```

```swift:swift
service.characteristics = [characteristic]
```

###ペリフェラルにサービスを追加

```objc:objc
[self.peripheralManager addService:service];
```

```swift:swift
peripheralManager.addService(service)
```


###サービス追加結果を取得する

```objc:objc
- (void)peripheralManager:(CBPeripheralManager *)peripheral
            didAddService:(CBService *)service
                    error:(NSError *)error
{
    if (error) {
        NSLog(@"サービス追加失敗！ error:%@", error);
        return;
    }
    
    NSLog(@"サービス追加成功！ service:%@", service);
}
```

```swift:swift
func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?) {
    
    if let error = error {
        print("サービス追加失敗！ error: \(error)")
        return
    }
    
    print("サービス追加成功！")
}
```

##Readリクエストに応答

```objc:objc
- (void)peripheralManager:(CBPeripheralManager *)peripheral
    didReceiveReadRequest:(CBATTRequest *)request
{
    if ([request.characteristic.UUID isEqual:self.characteristic.UUID]) {
        
        // CBMutableCharacteristicのvalueをCBATTRequestのvalueにセット
        request.value = self.characteristic.value;
        
        // リクエストに応答
        [self.peripheralManager respondToRequest:request
                                      withResult:CBATTErrorSuccess];
    }
}
```

```swift:swift
func peripheralManager(peripheral: CBPeripheralManager, didReceiveReadRequest request: CBATTRequest) {
    
    if request.characteristic.UUID.isEqual(characteristic.UUID) {
        
        // CBMutableCharacteristicのvalueをCBATTRequestのvalueにセット
        request.value = characteristic.value
        
        // リクエストに応答
        peripheralManager.respondToRequest(request, withResult: .Success)
    }
}
```

##Writeリクエストに応答

```objc:objc
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests {
    
    for (CBATTRequest *aRequest in requests) {

        if ([aRequest.characteristic.UUID isEqual:self.characteristic.UUID]) {
            
            // CBCharacteristicのvalueに、CBATTRequestのvalueをセット
            self.characteristic.value = aRequest.value;
        }
    }
    
    // リクエストに応答
    [self.peripheralManager respondToRequest:requests[0]
                                  withResult:CBATTErrorSuccess];
}
```

```swift:swift
func peripheralManager(peripheral: CBPeripheralManager, didReceiveWriteRequests requests: [CBATTRequest]) {

    for request in requests {
        
        if request.characteristic.UUID.isEqual(characteristic.UUID) {
                
            // CBCharacteristicのvalueに、CBATTRequestのvalueをセット
            characteristic.value = request.value
        }
    }

    // リクエストに応答
    peripheralManager.respondToRequest(requests[0], withResult: .Success)
}
```

##Notification / Indication への応答

###Subscribeリクエストを受け取る

```objc:objc
- (void)       peripheralManager:(CBPeripheralManager *)peripheral
                         central:(CBCentral *)central
    didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
    NSLog(@"Subscribeリクエストを受信");
    NSLog(@"Subscribe中のセントラル: %@", self.characteristic.subscribedCentrals);
}
```

```swift:swift
func peripheralManager(peripheral: CBPeripheralManager, central: CBCentral, didSubscribeToCharacteristic characteristic: CBCharacteristic)
{
    print("Subscribeリクエストを受信")
    print("Subscribe中のセントラル: \(characteristic.subscribedCentrals)")
}
```

###Unsubscribeリクエストを受け取る

```objc:objc
- (void)           peripheralManager:(CBPeripheralManager *)peripheral
                             central:(CBCentral *)central
    didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
{
    NSLog(@"Unsubscribeリクエストを受信");
    NSLog(@"Subscribe中のセントラル: %@", self.characteristic.subscribedCentrals);
}
```

```swift:swift
func peripheralManager(peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFromCharacteristic characteristic: CBCharacteristic)
{
    print("Notify停止リクエストを受信")
    print("Notify中のセントラル: \(characteristic.subscribedCentrals)")
}
```


###値を更新し、通知する

```objc:objc
self.characteristic.value = data;

[self.peripheralManager updateValue:data
                  forCharacteristic:self.characteristic
               onSubscribedCentrals:nil];
```

```swift:swift
characteristic.value = data

peripheralManager.updateValue(data, forCharacteristic: characteristic, onSubscribedCentrals: nil)
```