fix(ble): restore peripheral state and keep advertising
This commit is contained in:
@@ -16,6 +16,7 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
|||||||
static let serviceUUID = CBUUID(string: "A0B0C0D0-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
static let serviceUUID = CBUUID(string: "A0B0C0D0-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
||||||
static let feedTxUUID = CBUUID(string: "A0B0C0D1-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
static let feedTxUUID = CBUUID(string: "A0B0C0D1-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
||||||
static let controlRxUUID = CBUUID(string: "A0B0C0D2-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 private(set) var bluetoothState: CBManagerState = .unknown
|
||||||
@Published var advertisingEnabled: Bool = true
|
@Published var advertisingEnabled: Bool = true
|
||||||
@@ -31,7 +32,13 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
|||||||
@Published private(set) var lastDataAt: Date? = nil
|
@Published private(set) var lastDataAt: Date? = nil
|
||||||
|
|
||||||
private let queue = DispatchQueue(label: "iris.ble.peripheral.queue")
|
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 service: CBMutableService?
|
||||||
private var feedTx: CBMutableCharacteristic?
|
private var feedTx: CBMutableCharacteristic?
|
||||||
@@ -111,8 +118,6 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
|||||||
guard peripheral.state == .poweredOn else { return }
|
guard peripheral.state == .poweredOn else { return }
|
||||||
guard service == nil else { return }
|
guard service == nil else { return }
|
||||||
|
|
||||||
peripheral.removeAllServices()
|
|
||||||
|
|
||||||
let feedTx = CBMutableCharacteristic(
|
let feedTx = CBMutableCharacteristic(
|
||||||
type: Self.feedTxUUID,
|
type: Self.feedTxUUID,
|
||||||
properties: [.notify, .read],
|
properties: [.notify, .read],
|
||||||
@@ -139,7 +144,7 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
|||||||
stopAdvertising()
|
stopAdvertising()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if advertisingEnabled, subscribedCentralIds.isEmpty {
|
if advertisingEnabled {
|
||||||
startAdvertising()
|
startAdvertising()
|
||||||
} else {
|
} else {
|
||||||
stopAdvertising()
|
stopAdvertising()
|
||||||
@@ -147,16 +152,21 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startAdvertising() {
|
private func startAdvertising() {
|
||||||
guard !isAdvertising else { return }
|
guard !peripheral.isAdvertising else {
|
||||||
|
publish { self.isAdvertising = true }
|
||||||
|
return
|
||||||
|
}
|
||||||
peripheral.startAdvertising([
|
peripheral.startAdvertising([
|
||||||
CBAdvertisementDataLocalNameKey: "GlassNow",
|
CBAdvertisementDataLocalNameKey: "GlassNow",
|
||||||
CBAdvertisementDataServiceUUIDsKey: [Self.serviceUUID],
|
CBAdvertisementDataServiceUUIDsKey: [Self.serviceUUID],
|
||||||
])
|
])
|
||||||
publish { self.isAdvertising = true }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopAdvertising() {
|
private func stopAdvertising() {
|
||||||
guard isAdvertising else { return }
|
guard peripheral.isAdvertising else {
|
||||||
|
publish { self.isAdvertising = false }
|
||||||
|
return
|
||||||
|
}
|
||||||
peripheral.stopAdvertising()
|
peripheral.stopAdvertising()
|
||||||
publish { self.isAdvertising = false }
|
publish { self.isAdvertising = false }
|
||||||
}
|
}
|
||||||
@@ -313,6 +323,27 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension BlePeripheralManager: CBPeripheralManagerDelegate {
|
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) {
|
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
|
||||||
queue.async { [weak self] in
|
queue.async { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ import SwiftUI
|
|||||||
|
|
||||||
@main
|
@main
|
||||||
struct irisApp: App {
|
struct irisApp: App {
|
||||||
|
@Environment(\.scenePhase) private var scenePhase
|
||||||
@StateObject private var ble: BlePeripheralManager
|
@StateObject private var ble: BlePeripheralManager
|
||||||
@StateObject private var orchestrator: ContextOrchestrator
|
@StateObject private var orchestrator: ContextOrchestrator
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let bleManager = BlePeripheralManager()
|
let bleManager = BlePeripheralManager()
|
||||||
|
bleManager.start()
|
||||||
_ble = StateObject(wrappedValue: bleManager)
|
_ble = StateObject(wrappedValue: bleManager)
|
||||||
_orchestrator = StateObject(wrappedValue: ContextOrchestrator(ble: bleManager))
|
_orchestrator = StateObject(wrappedValue: ContextOrchestrator(ble: bleManager))
|
||||||
}
|
}
|
||||||
@@ -23,6 +25,11 @@ struct irisApp: App {
|
|||||||
ContentView()
|
ContentView()
|
||||||
.environmentObject(ble)
|
.environmentObject(ble)
|
||||||
.environmentObject(orchestrator)
|
.environmentObject(orchestrator)
|
||||||
|
.onChange(of: scenePhase) { phase in
|
||||||
|
if phase == .active || phase == .background {
|
||||||
|
ble.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user