【Swift UI】Core BluetoothでCentralの実装方法!ServiceとCharacteristic

この記事からわかること
- Swift UIでCore BluetoothでBluetooth接続アプリの実装方法
- Central側の実装
- ServiceとCharacteristicの取得方法
- CBCentralManagerDelegateやCBPeripheralDelegateの使い方
- peripheralから値を取得したり書き込むには?
- peripheralから更新通知を受け取る(notify)には?
index
[open]
\ アプリをリリースしました /
今回はiOSアプリでBluetooth機能を実装するためにCore Bluetoothを使用してCentral側の実装方法をまとめていきます。この記事とPeripheral側も実装することで実際にBluetooth機能をテストできるアプリが完成できるのでハンズオンで試してみてください。
Core Bluetoothとは?
Core BluetoothはアプリからBLE(Bluetooth Low Energy)を使用するための機能を提供するライブラリです。デフォルトで組み込まれているので導入作業は必要なくすぐに使用することができます。
Bluetooth接続を用いる場合は例えばPCとキーボードなど接続しあう2つの登場人物が必要になります。2つのうちBluetoothの機能を使用する親側を「Central(セントラル)」、機能を提供する子側を「Peripheral(ペリフェラル)」と呼びます。例で言うとPCがセントラル、キーボードがペリフェラルです。
そしてペリフェラルが持っている機能のまとまりをサービス、機能1つ1つをキャラクタリスティックと呼びます。
iOSアプリ側もセントラルであることが多いですが、もちろんペリフェラルとして実装することも可能になっています。この記事は前回(↓)の記事でペリフェラル側を実装した後に接続できるようにセントラル側を実装していますので先にペリフェラル側の実装から読んでください。
実装するにあたって
Xcodeで実装するにあたってシミュレーターではBluetooth機能をサポートしていないのでテストする際には実機が必要になります。ペリフェラル側との連携を試したい場合は実機が2台に必要になるので注意してください。
また今回の全体のコードはGitHubに掲載しています。
Central機能の実装方法
セントラル側を実装する際はペリフェラル側の仕様などが決まってから実装することのが多いと思います。今回は前回の記事で実装した機能を使用できるようにしていきます。
流れ
- Privacy - Bluetooth Always Usage Descriptionキーの追加
- Bluetooth機能管理クラスの作成
- CBCentralManagerDelegateへの準拠
- スキャンの実装
- ペリフェラルの検出と接続
- サービス一覧を取得する
- キャラクタリスティック一覧を取得する
- キャラクタリスティックから値を取得する
Core Bluetoothではスキャンや探索などの処理を実行すると結果がデリゲートメソッドから取得できる仕組みが多用されています。デリゲートについて詳しく知りたい方は以下の記事を参考にしてください。
Privacy - Bluetooth Always Usage Descriptionキーの追加
iOSアプリでBluetooth機能を使用するためには「info.plist」に以下のキーを追加します。
value
には「アプリ内でBluetooth機能を使用するため」など、Bluetooth機能を使用する理由を記述しておきます。
Bluetooth機能管理クラスの作成
Bluetooth機能を管理するためのBlueCentralManager
クラスを作成します。この際にimport CoreBluetooth
も合わせて記述しておきます。Centralとしての機能を使用する提供するCBCentralManager
型のcentralManager
プロパティをとシングルトン用のプロパティを定義しておきます。
また接続したいペリフェラル情報を各プロパティに保持しておきます。後で使用するのでinit
もオーバーライドしておきます。
CBCentralManagerDelegateへの準拠
公式リファレンス:CBCentralManagerDelegate
続いてCBCentralManagerDelegate
にBlueCentralManager
クラスを準拠させていきます。CBCentralManagerDelegate
のデリゲートメソッドからCentralとしての状態や周辺機器の検出結果などを取得することが可能です。準拠させることでcentralManagerDidUpdateState(_: CBCentralManager)
デリゲートメソッドの実装が必須になります。これはCentralの状態が変化するタイミングで呼ばれます。
引数のcentral
のstate
プロパティから現在の状態をCBManagerState型で取得できます。以下は状態に応じて分岐させているコードです。
最後にCBCentralManagerDelegate
に準拠したことでcentralManager
プロパティにCBCentralManager
インスタンスを格納できるようになります。引数delegate
にはself(自身)
をqueue
にはnil
を渡しておきます。
スキャンの実装
Bluetoothで接続できるペリフェラルをスキャンするにはscanForPeripherals
メソッドを使用します。これは周辺にあるペリフェラルを全てスキャンします。withServices
にnil
を渡すと無条件でマッチするものを検索しますが、サービスのUUIDを渡すことでスキャンを絞ることが可能です。使用するサービスが確定している場合は指定した方が効率が上がります。
またscanForPeripherals
メソッドは明示的にstop
するまでスキャンし続けてしまい電池を無駄に消耗してしまうので、後述するstopScan
メソッドを使用して適切なスキャンの停止の実装が必要です。(例えば10秒のタイマーを設置するなど)
ペリフェラルの検出と接続
検出されたペリフェラルをクラス内で保持しておくために新しくプロパティを定義しておきます。
scanForPeripherals
を実行してペリフェラルが検出されるとcentralManager(_:,didDiscover peripheral:,advertisementData:,rssi)
メソッドが呼ばれます。引数のperipheral
から検出されたペリフェラルを参照することができます。ここでは定義しておいたペリフェラルの名前と一致するペリフェラルがあれば接続処理をconnect
メソッドを使用して実装しています。
また接続処理を実行したら明示的にstopScan
メソッドでスキャンを停止させておきます。
ペリフェラルの接続結果を取得する
connect
メソッドで接続リクエストを送信し、接続が成功したらcentralManager(_:,didConnect peripheral:)
が失敗したらcentralManager(_:, didFailToConnect peripheral:,error:)
が呼ばれます。
サービス一覧を取得する
ペリフェラルとの接続が完了したらそのペリフェラルが持っているサービス一覧とさらにその中のキャラクタリスティック一覧を取得します。そのためにはCBPeripheralDelegate
に準拠させる必要があります。さらにextension
していきます。
少しコードを戻ってペリフェラルの接続が成功した際にperipheral.delegate
にデリゲートのセットとサービスの探索を開始するdiscoverServices
メソッドを実装します。引数に探索対象のUUIDを渡して絞り込んでおきます。
discoverServices
メソッドが実行されサービスが見つかるとperipheral(_:didDiscoverServices error:)
デリゲートメソッドが呼ばれます。services
プロパティから配列形式でサービス一覧が取得できるのでサービス1つ1つに対してdiscoverCharacteristics
メソッドを実行してキャラクタリスティックの探索開始します。ここでも明示的にUUIDを指定することで電池消費を抑えることが可能です。
キャラクタリスティック一覧を取得する
discoverCharacteristics
メソッドが実行されキャラクタリスティックが見つかるとperipheral(_:, didDiscoverCharacteristicsFor service:, error:)
デリゲートメソッドが呼ばれます。characteristics
プロパティから配列形式でキャラクタリスティック一覧が取得できます。
キャラクタリスティックの一覧から1つずつUUIDを比較しマッチすればそれぞれのプロパティへ格納しておきます。
キャラクタリスティックから値を取得する
キャラクタリスティックから値を取得するにはreadData
メソッドを新しく定義し、その中で保持しているペリフェラルからreadValue
メソッドを呼び出します。引数には読み出し対象のキャラクタリスティックを渡します。
値が取得できるとperipheral(_:, didUpdateValueFor characteristic:, error:)
デリゲートメソッドが呼ばれcharacteristic
からUUIDや値(Data型)など参照することができます。
取得した値はData型なので適切に変換して使用する必要があります。
ペリフェラルに値を書き込む
キャラクタリスティックに値を書き込むにregisterData
メソッドを新しく定義し、その中で保持しているペリフェラルからwriteValue
メソッドを呼び出します。引数には対象のデータ、書き込み対象のキャラクタリスティックを渡します。
書き込みが成功するとperipheral(_:, didWriteValueFor characteristic:, error:)
が呼ばれます。
Notifyの通知を受け取る
Notifyはセントラルではなくペリフェラルから値の発火のタイミングが操作されています。セントラル側ではNotifyを検知する状態にしておくことでペリフェラル側からの値の更新を通知します。setNotifyValue
メソッドにtrue
を渡せば検知状態、false
なら検知を停止させることができます。
Notifyで受け取った値はread
と同じくperipheral(_:, didUpdateValueFor characteristic:, error:)
デリゲートメソッドが呼ばれるのでここから取得できます。
おすすめ記事
【Swift UI】Core Bluetoothでペリフェラルの接続を切断する方法!cancelPeripheralConnection
おすすめ参考書:iOS×BLE Core Bluetoothプログラミング
iOSアプリでBLEを使用した機能を実装したいなら一度は読んでおくことをおすすめする参考書です。iOSでのCore Bluetoothを使用した実装だけでなく、Bluetoothに関する細かい知識やノウハウも詰まっているので網羅的に理解したい方にはバッチリだと思います。
少し古い参考書であり、Objective-CとSwift両方のコードで実装方法が記述されています。Swift UIでの実装方法は載っていませんが、基本的なコードは昔からあまり変わっていないのでつまづくところはなく実装できると思います。
BLEを食材や店員などに例えて解説してくれるので素人でもBLEの概念がつかみやすく記述されています。約500ページくらいあるのでボリュームがすごいですが、ここから得られる知識は数知れませんでした。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。