fix(ble): restore peripheral state and keep advertising

This commit is contained in:
2026-01-08 22:27:11 +00:00
parent d89aedd5af
commit 214af08625
2 changed files with 45 additions and 7 deletions

View File

@@ -16,6 +16,7 @@ final class BlePeripheralManager: NSObject, ObservableObject {
static let serviceUUID = CBUUID(string: "A0B0C0D0-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
static let feedTxUUID = CBUUID(string: "A0B0C0D1-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
static let controlRxUUID = CBUUID(string: "A0B0C0D2-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
private static let restoreIdentifier = "iris.ble.peripheral.v1"
@Published private(set) var bluetoothState: CBManagerState = .unknown
@Published var advertisingEnabled: Bool = true
@@ -31,7 +32,13 @@ final class BlePeripheralManager: NSObject, ObservableObject {
@Published private(set) var lastDataAt: Date? = nil
private let queue = DispatchQueue(label: "iris.ble.peripheral.queue")
private lazy var peripheral = CBPeripheralManager(delegate: self, queue: queue)
private lazy var peripheral = CBPeripheralManager(
delegate: self,
queue: queue,
options: [
CBPeripheralManagerOptionRestoreIdentifierKey: Self.restoreIdentifier,
]
)
private var service: CBMutableService?
private var feedTx: CBMutableCharacteristic?
@@ -111,8 +118,6 @@ final class BlePeripheralManager: NSObject, ObservableObject {
guard peripheral.state == .poweredOn else { return }
guard service == nil else { return }
peripheral.removeAllServices()
let feedTx = CBMutableCharacteristic(
type: Self.feedTxUUID,
properties: [.notify, .read],
@@ -139,7 +144,7 @@ final class BlePeripheralManager: NSObject, ObservableObject {
stopAdvertising()
return
}
if advertisingEnabled, subscribedCentralIds.isEmpty {
if advertisingEnabled {
startAdvertising()
} else {
stopAdvertising()
@@ -147,16 +152,21 @@ final class BlePeripheralManager: NSObject, ObservableObject {
}
private func startAdvertising() {
guard !isAdvertising else { return }
guard !peripheral.isAdvertising else {
publish { self.isAdvertising = true }
return
}
peripheral.startAdvertising([
CBAdvertisementDataLocalNameKey: "GlassNow",
CBAdvertisementDataServiceUUIDsKey: [Self.serviceUUID],
])
publish { self.isAdvertising = true }
}
private func stopAdvertising() {
guard isAdvertising else { return }
guard peripheral.isAdvertising else {
publish { self.isAdvertising = false }
return
}
peripheral.stopAdvertising()
publish { self.isAdvertising = false }
}
@@ -313,6 +323,27 @@ final class BlePeripheralManager: NSObject, ObservableObject {
}
extension BlePeripheralManager: CBPeripheralManagerDelegate {
func peripheralManager(_ peripheral: CBPeripheralManager, willRestoreState dict: [String: Any]) {
queue.async { [weak self] in
guard let self else { return }
if let services = dict[CBPeripheralManagerRestoredStateServicesKey] as? [CBService],
let restoredService = services.first(where: { $0.uuid == Self.serviceUUID }),
let restoredMutableService = restoredService as? CBMutableService,
let characteristics = restoredService.characteristics {
self.service = restoredMutableService
self.feedTx = characteristics.first(where: { $0.uuid == Self.feedTxUUID }) as? CBMutableCharacteristic
self.controlRx = characteristics.first(where: { $0.uuid == Self.controlRxUUID }) as? CBMutableCharacteristic
}
self.publish {
self.isAdvertising = peripheral.isAdvertising
}
self.applyAdvertisingPolicy()
}
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
queue.async { [weak self] in
guard let self = self else { return }