ミライスタート TECH系ブログ

株式会社ミライスタートのエンジニア達が気になったTECH系の記事等をアップしています!

BluetoothKit使ってみる

エンジニアの横田です。
今回、iOSバイス間でBluetooth通信をするために、BluetoothKitというライブラリを試してみたので、その使い方について簡単に紹介します。
https://github.com/rhummelmose/BluetoothKit
このライブラリですが、Appleで用意されているBluetoothライブラリCoreBluetoothが様々なBluetooth機器に対応出来るよう作られている分、簡単な処理をさせるのにもおまじない的な設定が煩雑すぎる、という問題に対して作られたものだそうです。iOS間の通信に特化させた分、シンプルなコードでBluetooth通信が出来るようになるよう作られてるみたいです。

まずBluetooth初心者だと知らなかったのが、機器間での役割分担があるとのこと。周囲に電波を飛ばして自分がサービス提供可能であることを宣伝する機器-ペリフェラルと、この電波を拾ってペリフェラルを見つけて接続を開始する機器-セントラルとの2つです。

今回はセントラルからペリフェラルにテキストデータを飛ばすまでの処理のセントラル側についてです。

セントラル処理を担うのはBKCentralクラスです。

private let central = BKCentral()

このセントラルの起動処理を実行します。

  private func startCentral() {
    do {
      central.delegate = self //①
      central.addAvailabilityObserver(self) //②
      let dataServiceUUID = NSUUID(UUIDString: "6E6B5C64-FAF7-40AE-9C21-D4933AF45B23")!
      let dataServiceCharacteristicUUID = NSUUID(UUIDString: "477A2967-1FAB-4DC5-920A-DEE5DE685A3D")!
      let configuration = BKConfiguration(dataServiceUUID: dataServiceUUID, dataServiceCharacteristicUUID: dataServiceCharacteristicUUID)
      try central.startWithConfiguration(configuration)
    } catch let error {
      print("Error while starting: \(error)")
    }
  }

ここで①と②でセントラルにdelegateを渡しています。①で渡すのがBKCentralDelegate、②で渡すのが BKAvailabilityObserverです。
BKCentralDelegateでは、
func central(central: BKCentral, remotePeripheralDidDisconnect remotePeripheral: BKRemotePeripheral)
で接続切断時の処理を記述する必要があります。

BKAvailabilityObserverの方では、
func availabilityObserver(availabilityObservable: BKAvailabilityObservable, availabilityDidChange availability: BKAvailability)
func availabilityObserver(availabilityObservable: BKAvailabilityObservable, unavailabilityCauseDidChange unavailabilityCause: BKUnavailabilityCause)

Bluetooth機能が使用可能(不能)な状態になったタイミングでの処理を記述します。この使用可能になったタイミングで、セントラルのペリフェラルを探すスキャン処理を実行させるとよいです。使用可能でない時にスキャンを行うとエラーになってしまうからです。
続いて乱雑なコードではありますが、availabilityObserverで呼び出すスキャン開始処理です。

  private func startScan() {
    central.scanContinuouslyWithChangeHandler({ changes, discoveries in
      // Handle changes to "availabile" discoveries, [BKDiscoveriesChange].
      // Handle current "available" discoveries, [BKDiscovery].
      // This is where you'd ie. update a table view.
      if (0 < discoveries.count) {
        if (self.discoveries.count == 0) {
          self.central.connect(remotePeripheral: discoveries[0].remotePeripheral) { remotePeripheral, error in
            guard error == nil else {
              print("Error connecting peripheral: \(error)")
              return
            }
            self.remotePeripheral = discoveries[0].remotePeripheral
          }
        }
      }
      self.discoveries = discoveries
      self.updateStatus()
      }, stateHandler: { newState in
        // Handle newState, BKCentral.ContinuousScanState.
        // This is where you'd ie. start/stop an activity indicator.
      }, duration: 3, inBetweenDelay: 3, errorHandler: { error in
        print("Error scanning: \(error)")
    })
  }

この記述で接続相手に1端末しか想定していません。新しく接続相手が見つかった時に捕まえてメンバ変数remotePeripheralに設定しています。
この接続相手を捕まえた状態で、

private func centerSendData(message: String) {
    let data = message.dataUsingEncoding(NSUTF8StringEncoding)
    central.sendData(data!, toRemotePeer: remotePeripheral!) { data, remotePeripheral, error in
      guard error == nil else {
        print("Failed sending to \(remotePeripheral)")
        return
      }
      print("Sent to \(remotePeripheral)")
    }
  }

このような処理を呼び出すとStringを接続先のペリフェラルに送信できます。次回、ペリフェラル側で送られたデータを受け取るまでの処理について紹介します。