Core Bluetoothで周辺機器とBLE通信をしてみて分かったこと

Core Bluetoothで周辺機器とBLE通信をしてみて分かったこと 学習記録

Notification Service Extensionでリッチ通知を実装した流れで、以前から気になっていたCore BluetoothによるBLE周辺機器との通信にも挑戦してみました。

1. 初期化してすぐスキャンを開始してエラーになった

最初、CBCentralManagerを生成した直後にすぐスキャンを開始するコードを書いていました。

let centralManager = CBCentralManager(delegate: self, queue: nil)
centralManager.scanForPeripherals(withServices: nil, options: nil)

このコードを実行すると、コンソールに警告が出るだけでスキャンが正しく始まりませんでした。調べてみると、CBCentralManagerはBluetoothの状態確認が非同期で行われる仕組みで、centralManagerDidUpdateStateデリゲートメソッドで状態が.poweredOnになったことを確認してからでないとスキャンを開始できないと分かりました。

2. スキャンを開始したまま止め忘れていた

状態確認の仕組みを理解し、スキャンできるようになった後、今度はアプリを起動している間ずっとバッテリー消費が激しいという問題に気づきました。

centralManager.stopScan()

目的の周辺機器を発見した後もスキャンを止める処理を書いておらず、無限にスキャンを継続してしまっていたことが原因でした。目的のデバイスを発見した時点、あるいは一定時間経過した時点でstopScan()を呼ぶ処理を追加し、無駄なスキャンを止めるようにしました。

3. サービス・キャラクタリスティックの探索順序を誤解していた

周辺機器に接続した直後、いきなりデータの読み書きができると思い込んでいました。

必要な手順:
接続 → サービスの探索 → キャラクタリスティックの探索 → 読み書き

実際には、接続が完了しただけではまだ何もできず、discoverServicesでサービスを探索し、さらにそのサービスに対してdiscoverCharacteristicsでキャラクタリスティックを探索して初めて、読み書き対象を特定できる仕組みだと分かりました。それぞれの探索結果はデリゲートメソッドで非同期に返ってくるため、段階を踏んだ実装が必要でした。

4. バックグラウンドでのスキャン制約に気づかず戸惑った

アプリをバックグラウンドに移した状態でも周辺機器を検出し続けたいと考えていましたが、想定通りに動作しませんでした。

Info.plistへの追加:
UIBackgroundModes に bluetooth-central を追加

バックグラウンドでBLE通信を継続するには、Info.plistのUIBackgroundModesにbluetooth-centralを追加する必要があることが分かりました。それでもフォアグラウンド時と比べてスキャン間隔や検出精度に制約があり、常に即座に検出できるわけではないという点も併せて理解しておく必要がありました。

まとめ

  • CBCentralManagerはBluetoothの状態が.poweredOnになってからでないとスキャンできない
  • 目的のデバイスを発見したらスキャンを止め、バッテリー消費を抑える
  • 接続後は「サービス探索→キャラクタリスティック探索→読み書き」という順序を踏む必要がある
  • バックグラウンドでの継続利用にはInfo.plistへの設定追加が必要で、動作にも制約がある

非同期のデリゲート呼び出しが多く、状態管理をきちんと設計しないと動かない部分でつまずきやすい実装だと感じました。


本記事は生成AI(Claude)を活用して執筆・編集しています。内容は実体験に基づき、執筆者本人が確認・監修しています。

コメント

タイトルとURLをコピーしました