BIG FIX: Provisioners were calling centralManager.connect() but BLEManager is the CBCentralManagerDelegate — provisioners never received didConnect/didFailToConnect callbacks, so connections ALWAYS timed out after 5s regardless. This is why provisioning kept failing. Fixed by: 1. Adding didConnect/didFailToConnect/didDisconnect to BLEManager 2. Provisioners register connection callbacks via bleManager 3. Increased connection timeout from 5s to 10s DIAGNOSTICS: Added ProvisionLog system so failures show a timestamped step-by-step log of what happened (with Share button). Every phase is logged: init, API calls, connect attempts, service discovery, auth, write commands, and errors.
77 lines
2.7 KiB
Swift
77 lines
2.7 KiB
Swift
import Foundation
|
|
import CoreBluetooth
|
|
|
|
/// Tries KBeacon → DXSmart → BlueCharm in sequence for unknown beacon types.
|
|
/// Matches Android's fallback behavior when beacon type can't be determined.
|
|
final class FallbackProvisioner: BeaconProvisioner {
|
|
|
|
private let peripheral: CBPeripheral
|
|
private let centralManager: CBCentralManager
|
|
private var activeProvisioner: (any BeaconProvisioner)?
|
|
|
|
private(set) var isConnected: Bool = false
|
|
var diagnosticLog: ProvisionLog?
|
|
var bleManager: BLEManager?
|
|
|
|
init(peripheral: CBPeripheral, centralManager: CBCentralManager) {
|
|
self.peripheral = peripheral
|
|
self.centralManager = centralManager
|
|
}
|
|
|
|
func connect() async throws {
|
|
let provisioners: [() -> any BeaconProvisioner] = [
|
|
{ [self] in
|
|
var p = KBeaconProvisioner(peripheral: peripheral, centralManager: centralManager)
|
|
p.diagnosticLog = diagnosticLog
|
|
p.bleManager = bleManager
|
|
return p
|
|
},
|
|
{ [self] in
|
|
var p = DXSmartProvisioner(peripheral: peripheral, centralManager: centralManager)
|
|
p.diagnosticLog = diagnosticLog
|
|
p.bleManager = bleManager
|
|
return p
|
|
},
|
|
{ [self] in
|
|
var p = BlueCharmProvisioner(peripheral: peripheral, centralManager: centralManager)
|
|
p.diagnosticLog = diagnosticLog
|
|
p.bleManager = bleManager
|
|
return p
|
|
},
|
|
]
|
|
|
|
let typeNames = ["KBeacon", "DXSmart", "BlueCharm"]
|
|
var lastError: Error = ProvisionError.connectionTimeout
|
|
|
|
for (index, makeProvisioner) in provisioners.enumerated() {
|
|
await diagnosticLog?.log("fallback", "Trying \(typeNames[index]) provisioner…")
|
|
let provisioner = makeProvisioner()
|
|
do {
|
|
try await provisioner.connect()
|
|
activeProvisioner = provisioner
|
|
isConnected = true
|
|
await diagnosticLog?.log("fallback", "\(typeNames[index]) connected successfully")
|
|
return
|
|
} catch {
|
|
provisioner.disconnect()
|
|
lastError = error
|
|
await diagnosticLog?.log("fallback", "\(typeNames[index]) failed: \(error.localizedDescription)", isError: true)
|
|
}
|
|
}
|
|
|
|
throw lastError
|
|
}
|
|
|
|
func writeConfig(_ config: BeaconConfig) async throws {
|
|
guard let provisioner = activeProvisioner else {
|
|
throw ProvisionError.notConnected
|
|
}
|
|
try await provisioner.writeConfig(config)
|
|
}
|
|
|
|
func disconnect() {
|
|
activeProvisioner?.disconnect()
|
|
activeProvisioner = nil
|
|
isConnected = false
|
|
}
|
|
}
|