Core Bluetooth with Swift (ObjCのおまけ付き)

  • 224
    いいね
  • 0
    コメント

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

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

更新メモ

  • 2015.10.12 Swift 2 対応

(本題に入る前に・・・)

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

iOS×BLE Core Bluetoothプログラミング
堤 修一 松村 礼央
ソシム
売り上げランキング: 1,106

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

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

インポート

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

objc
@import CoreBluetooth;
swift
import CoreBluetooth

セントラルマネージャ編

スキャン、接続

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

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

objc
@interface SomeClass () <CBCentralManagerDelegate>
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@end
swift
class SomeClass: SomeSuperclass, CBCentralManagerDelegate {
    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!

CBCentralManagerを初期化する

objc
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
swift
centralManager = CBCentralManager(delegate: self, queue: nil)

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

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

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

    NSLog(@"state:%ld", (long)central.state);
}
swift
func centralManagerDidUpdateState(central: CBCentralManager) {

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

スキャンを開始する

objc
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
swift
centralManager.scanForPeripheralsWithServices(nil, options: nil)

スキャン結果を受け取る

objc
- (void)   centralManager:(CBCentralManager *)central
    didDiscoverPeripheral:(CBPeripheral *)peripheral
        advertisementData:(NSDictionary *)advertisementData
                     RSSI:(NSNumber *)RSSI
{
    NSLog(@"peripheral:%@", peripheral);
}
swift
func centralManager(central: CBCentralManager,
    didDiscoverPeripheral peripheral: CBPeripheral,
    advertisementData: [String : AnyObject],
    RSSI: NSNumber!)
{
    print("peripheral: \(peripheral)")
}

スキャンを停止する

objc
[self.centralManager stopScan];
swift
centralManager.stopScan()

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

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

swift
centralManager.connectPeripheral(peripheral, options: nil)

接続結果を取得する

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

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

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

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

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

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

objc
@interface SomeClass () <CBCentralManagerDelegate, CBPeripheralDelegate>
swift
class SomeClass: SomeSuperclass, CBCentralManagerDelegate, CBPeripheralDelegate {

サービス探索を開始する

objc
peripheral.delegate = self;
[peripheral discoverServices:nil];
swift
peripheral.delegate = self
peripheral.discoverServices(nil)

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

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
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
[peripheral discoverCharacteristics:nil forService:service];
swift
peripheral.discoverCharacteristics(nil, forService: service)

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

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
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
[peripheral readValueForCharacteristic:characteristic];
swift
peripheral.readValueForCharacteristic(characteristic)

Read結果を取得する

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
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
[peripheral writeValue:data
     forCharacteristic:characteristic
                  type:CBCharacteristicWriteWithResponse];
swift
peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithResponse)

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

objc
NSData *data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
swift
let data = "hello".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true)

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

objc
unsigned char value = 0x01;
NSData *data = [[NSData alloc] initWithBytes:&value length:1];
swift
var value: CUnsignedChar = 0x01
let data = NSData(bytes: &value, length: 1)

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

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

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

    print("Write成功!")
}

Notify

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

objc
[peripheral setNotifyValue:YES
         forCharacteristic:characteristic];
swift
peripheral.setNotifyValue(true, forCharacteristic: characteristic)

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

objc
[peripheral setNotifyValue:NO
         forCharacteristic:characteristic];
swift
peripheral.setNotifyValue(false, forCharacteristic: characteristic)

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

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
func peripheral(peripheral: CBPeripheral,
    didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic,
    error: NSError?)
{
    if let error = error {        
        print("Notify状態更新失敗...error: \(error)")
    } else {
        print("Notify状態更新成功! isNotifying: \(characteristic.isNotifying)")
    }
}

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

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
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
@interface SomeClass () <CBPeripheralManagerDelegate>
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@end
swift
class SomeClass: SomeSuperclass, CBPeripheralManagerDelegate {
    var peripheralManager: CBPeripheralManager!

CBPeripheralManager を初期化する

objc
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                                 queue:nil
                                                               options:nil];
swift
peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)

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

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

    NSLog(@"state:%ld", (long)peripheral.state);
}
swift
func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager) {

    print("state: \(peripheral.state)")
}

アドバタイズを開始する

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

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

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

    NSLog(@"Succeeded!");
}
swift
func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager, error: NSError?) {

    if let error = error {
        print("Failed... error: \(error)")
        return
    }

    print("Succeeded!")
}

アドバタイズを停止する

objc
[self.peripheralManager stopAdvertising];
swift
peripheralManager.stopAdvertising()

サービス追加

サービスを作成する

objc
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
CBMutableService *service = [[CBMutableService alloc] initWithType:serviceUUID
                                                           primary:YES];
swift
let serviceUUID = CBUUID(string: kServiceUUID)
let service = CBMutableService(type: serviceUUID, primary: true)

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

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
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
service.characteristics = @[characteristic];
swift
service.characteristics = [characteristic]

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

objc
[self.peripheralManager addService:service];
swift
peripheralManager.addService(service)

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

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

    NSLog(@"サービス追加成功! service:%@", service);
}
swift
func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?) {

    if let error = error {
        print("サービス追加失敗! error: \(error)")
        return
    }

    print("サービス追加成功!")
}

Readリクエストに応答

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
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
- (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
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
- (void)       peripheralManager:(CBPeripheralManager *)peripheral
                         central:(CBCentral *)central
    didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
    NSLog(@"Subscribeリクエストを受信");
    NSLog(@"Subscribe中のセントラル: %@", self.characteristic.subscribedCentrals);
}
swift
func peripheralManager(peripheral: CBPeripheralManager, central: CBCentral, didSubscribeToCharacteristic characteristic: CBCharacteristic)
{
    print("Subscribeリクエストを受信")
    print("Subscribe中のセントラル: \(characteristic.subscribedCentrals)")
}

Unsubscribeリクエストを受け取る

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

値を更新し、通知する

objc
self.characteristic.value = data;

[self.peripheralManager updateValue:data
                  forCharacteristic:self.characteristic
               onSubscribedCentrals:nil];
swift
characteristic.value = data

peripheralManager.updateValue(data, forCharacteristic: characteristic, onSubscribedCentrals: nil)
この投稿は Bluetooth Low Energy Advent Calendar 201410日目の記事です。