100% fresh codebase — no legacy code carried over. Built against the Android beacon app as the behavioral spec. Architecture: - App: SwiftUI @main, AppState-driven navigation, Keychain storage - Views: LoginView (OTP + biometric), BusinessListView, ScanView (provisioning hub) - Models: Business, ServicePoint, BeaconConfig, BeaconType, DiscoveredBeacon - Services: APIClient (actor, async/await), BLEManager (CoreBluetooth scanner) - Provisioners: KBeacon, DXSmart (2-step auth + flashing), BlueCharm - Utils: UUIDFormatting, BeaconBanList, BeaconShardPool (64 shards) Matches Android feature parity: - 4-screen flow: Login → Business Select → Scan/Provision - 3 beacon types with correct GATT protocols and timeouts - Namespace allocation via beacon-sharding API - Smart service point naming (Table N auto-increment) - DXSmart special flow (connect → flash → user confirms → write) - Biometric auth, dev/prod build configs, DEV banner overlay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
32 lines
1,019 B
Swift
32 lines
1,019 B
Swift
import Foundation
|
|
|
|
extension String {
|
|
|
|
/// Strip dashes, uppercase: "e2c56db5-..." → "E2C56DB5..."
|
|
var normalizedUUID: String {
|
|
replacingOccurrences(of: "-", with: "").uppercased()
|
|
}
|
|
|
|
/// Format 32 hex chars → standard UUID (8-4-4-4-12)
|
|
var uuidWithDashes: String {
|
|
let clean = normalizedUUID
|
|
guard clean.count == 32 else { return self }
|
|
let c = Array(clean)
|
|
return "\(String(c[0..<8]))-\(String(c[8..<12]))-\(String(c[12..<16]))-\(String(c[16..<20]))-\(String(c[20..<32]))"
|
|
}
|
|
|
|
/// Convert hex string to byte array
|
|
var hexToBytes: [UInt8] {
|
|
let clean = normalizedUUID
|
|
var bytes: [UInt8] = []
|
|
var i = clean.startIndex
|
|
while i < clean.endIndex {
|
|
let next = clean.index(i, offsetBy: 2, limitedBy: clean.endIndex) ?? clean.endIndex
|
|
if let byte = UInt8(clean[i..<next], radix: 16) {
|
|
bytes.append(byte)
|
|
}
|
|
i = next
|
|
}
|
|
return bytes
|
|
}
|
|
}
|