【Swift/Core Bluetooth】バックグラウンドモードの有効化!処理を継続させる
この記事からわかること
- Swift × Core BluetoothでBluetooth接続アプリの実装方法
- バックグラウンドモードとは?
- セントラル側で処理を継続させるには?
- 動作する時間は?
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:15.0.1
- iOS:17.0
- Swift:5.9
- macOS:Sonoma 14.1
バックグラウンドでもBLE機能の処理を継続させる方法
Swiftでバックグラウンドに移行しても処理を継続させる方法はbeginBackgroundTask
を使用した方法などいくつかありますが、Bluetooth接続を行うアプリではもっと簡単にバックグラウンド操作が可能になっています。
今回紹介する方法はセントラル側がペリフェラルからデータを取得した後の処理をバックグラウンドでも継続させる方法です。
バックグラウンドモードの設定方法
Bluetooth機能をバックグラウンドでも有効にするためには「Signing & Capabilities」から「+Capability」>「Background Modes」を追加します。
追加できたら「Uses Bluetooth LE accessories」にチェックを入れるだけです。
これでバックグラウンドでも操作が可能になります。
検証してみる
実際にバックグラウンドモードを有効にした際の違いを確認してみます。Bluetooth機能の実装方法などについては以下の記事を参考にしてください。
ReadやNotifyなどペリフェラルからデータが送信され、セントラル側で受け取るためのperipheral(_:, didUpdateValueFor characteristic:, error:)
デリゲートメソッドの中で動きを確認していきます。分かりやすいように実行しているアプリの状態を取得して出力するようにしておきました。
// ReadやNotifyのデータを受信する
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("ペリフェラルから値を取得しました。")
let state = UIApplication.shared.applicationState
switch state {
case .active:
print("Active")
case .inactive:
print("Inactive")
case .background:
print("Background")
@unknown default:
print("unknown")
}
}
ペリフェラル側の実装にはNotifyが定期的に送信されるようにタイマーを使用して3秒ごとに発火するようにしておきます。
var timerPublisher: AnyCancellable?
// Notifyを3秒ごとに送信
public func sendNotify() {
timerPublisher = Timer.publish(every: 3, on: .current, in: .common)
.autoconnect()
.receive(on: DispatchQueue(label: "subthread", qos: .background))
.sink {[weak self] _ in
guard let self = self else { return }
if let data = "Notify".data(using: .utf8) {
self.notifyCharacteristic.value = data
self.peripheralManager.updateValue(data, for: notifyCharacteristic, onSubscribedCentrals: nil)
}
}
}
これでコネクトした後にセントラル側がNotifyを観測した状態でアプリをバックグラウンドに移行させても問題なく動作することが確認できました。
ペリフェラルから値を取得しました。
Background
バックグラウンドでの動作時間
バックグラウンドで処理を実行できる時間はペリフェラルからデータが送られ続ける限り、半永久的に動作できそうな感じでした。
Notifyを一回だけ送信し、バックグラウンドで受けた場合にタイマーを利用してどこまで処理が継続されるか検証してみましたが、400秒を超えても出力されていたので、こちらも半永久的なのでしょうか?
// ReadやNotifyのデータを受信する
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("ペリフェラルから値を取得しました。\n")
var count = 0
timerPublisher = Timer.publish(every: 1, on: .current, in: .common)
.autoconnect()
.receive(on: DispatchQueue(label: "subthread", qos: .background))
.sink { _ in
count += 1
print(count)
}
}
今回の検証はバックグラウンドモードが無効になっている場合はバックグラウンド状態では以下が出力されることもなく、またタイマーのカウントが始まることもありませんでした。
ペリフェラルから値を取得しました。
Background
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。