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.
56 lines
1.7 KiB
Swift
56 lines
1.7 KiB
Swift
import Foundation
|
|
|
|
/// Timestamped diagnostic log for beacon provisioning.
|
|
/// Captures every step so we can diagnose failures.
|
|
@MainActor
|
|
final class ProvisionLog: ObservableObject {
|
|
struct Entry: Identifiable {
|
|
let id = UUID()
|
|
let timestamp: Date
|
|
let phase: String // "connect", "discover", "auth", "write", "verify"
|
|
let message: String
|
|
let isError: Bool
|
|
|
|
var formatted: String {
|
|
let t = Self.formatter.string(from: timestamp)
|
|
let prefix = isError ? "❌" : "✅"
|
|
return "\(t) [\(phase)] \(prefix) \(message)"
|
|
}
|
|
|
|
private static let formatter: DateFormatter = {
|
|
let f = DateFormatter()
|
|
f.dateFormat = "HH:mm:ss.SSS"
|
|
return f
|
|
}()
|
|
}
|
|
|
|
@Published private(set) var entries: [Entry] = []
|
|
private var startTime: Date?
|
|
|
|
/// Clear log for a new provisioning attempt
|
|
func reset() {
|
|
entries = []
|
|
startTime = Date()
|
|
}
|
|
|
|
/// Add a log entry
|
|
func log(_ phase: String, _ message: String, isError: Bool = false) {
|
|
let entry = Entry(timestamp: Date(), phase: phase, message: message, isError: isError)
|
|
entries.append(entry)
|
|
}
|
|
|
|
/// Elapsed time since reset
|
|
var elapsed: String {
|
|
guard let start = startTime else { return "0.0s" }
|
|
let seconds = Date().timeIntervalSince(start)
|
|
return String(format: "%.1fs", seconds)
|
|
}
|
|
|
|
/// Full log as shareable text
|
|
var fullText: String {
|
|
let header = "Payfrit Beacon Diagnostic Log"
|
|
let time = "Session: \(elapsed)"
|
|
let lines = entries.map { $0.formatted }
|
|
return ([header, time, "---"] + lines).joined(separator: "\n")
|
|
}
|
|
}
|