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>
48 lines
1.2 KiB
Swift
48 lines
1.2 KiB
Swift
import SwiftUI
|
|
|
|
/// Central app state — drives navigation between Login → Business Select → Scan
|
|
@MainActor
|
|
final class AppState: ObservableObject {
|
|
|
|
enum Screen {
|
|
case login
|
|
case businessList
|
|
case scan(business: Business)
|
|
}
|
|
|
|
@Published var currentScreen: Screen = .login
|
|
@Published var token: String?
|
|
@Published var userId: String?
|
|
|
|
init() {
|
|
// Restore saved session
|
|
if let saved = SecureStorage.loadSession() {
|
|
self.token = saved.token
|
|
self.userId = saved.userId
|
|
self.currentScreen = .businessList
|
|
}
|
|
}
|
|
|
|
func didLogin(token: String, userId: String) {
|
|
self.token = token
|
|
self.userId = userId
|
|
SecureStorage.saveSession(token: token, userId: userId)
|
|
currentScreen = .businessList
|
|
}
|
|
|
|
func selectBusiness(_ business: Business) {
|
|
AppPrefs.lastBusinessId = business.id
|
|
currentScreen = .scan(business: business)
|
|
}
|
|
|
|
func backToBusinessList() {
|
|
currentScreen = .businessList
|
|
}
|
|
|
|
func logout() {
|
|
token = nil
|
|
userId = nil
|
|
SecureStorage.clearSession()
|
|
currentScreen = .login
|
|
}
|
|
}
|