import SwiftUI struct ContentView: View { @StateObject private var setup = SetupCoordinator() @ObservedObject private var runtimeMode = ProxyRuntimeModeStore.shared @State private var phase: AppPhase = .splash @State private var verifiedDuringInitialSetup = false @AppStorage("setupCompleted") private var setupCompleted = false enum AppPhase { case splash, setup, map } var body: some View { Group { switch phase { case .splash: VStack(spacing: 16) { Image(systemName: "location.fill") .font(.system(size: 48)).foregroundStyle(.blue) ProgressView() Text(runtimeMode.hasSelectedMode && runtimeMode.mode == .localWiFi ? "正在初始化地图与本地代理…" : "正在初始化地图…") .font(.subheadline).foregroundStyle(.secondary) } case .setup: FirstSetupView(setup: setup, onComplete: finishInitialSetup) case .map: NavigationView { MapHomeView(setup: setup) } .fullScreenCover(isPresented: $setup.needsSetup) { FirstSetupView(setup: setup, onComplete: { setupCompleted = true setup.completeSetup() }) } } } .task { await bootstrap() } } @MainActor private func bootstrap() async { guard runtimeMode.hasSelectedMode else { ProxyManager.shared.stop() RuntimeLogger.info("APP", "Startup", "尚未选择运行模式,跳过本地 CA 和代理初始化") setup.requestModeSelection() phase = .setup return } let launchMode = runtimeMode.mode guard setupCompleted else { if launchMode == .localWiFi { await setup.prepareLocalServices() setup.requestSetup() } else { ProxyManager.shared.stop() BackgroundKeepAlive.shared.stop() setup.requestThirdPartySetup() } phase = .setup return } if launchMode == .localWiFi { await setup.prepareLocalServices() } else { ProxyManager.shared.stop() setup.completeSetup() RuntimeLogger.info("APP", "Startup", "第三方代理测试模式:跳过本地 CA、代理和环境检测") } do { try CoordinateStorageMigration.migrateIfNeeded(favorites: FavoriteLocationStore()) } catch { RuntimeLogger.error("APP", "Startup", "旧坐标数据迁移失败,将在下次启动重试", error: error) } // MapHomeView is intentionally constructed only after this required // coordinate-system gate resolves, so cached pins are never replayed // into an unknown Apple Maps coordinate system. let mapCoordinateSystem = await CoordinateConverter.resolveInitialMapCoordinateSystem() guard !Task.isCancelled else { return } RuntimeLogger.info("APP", "Startup", "地图坐标标准初始化完成,开始后续启动流程", details: [ "地图标准": mapCoordinateSystem.rawValue, "使用兜底": String(CoordinateConverter.initialMapCoordinateSystemUsedFallback) ]) // Resolve the first map center before constructing MapHomeView. This // prevents a Shenzhen/cache frame followed by a second realtime frame. if LastCoordinateStore.load() == nil { RuntimeLogger.info("APP", "Startup", "没有持久化图钉,地图创建前请求实时定位") if let realtime = await RealtimeLocationManager.shared.requestLocation() { let mapCoordinateSystemChange = CoordinateConverter.correctMapCoordinateSystemUsingRealtime(realtime) let pair = CoordinateConverter.coordinatePair( lat: realtime.latitude, lon: realtime.longitude, mapCoordinateSystem: .wgs84 ) LastCoordinateStore.save(coordinatePair: pair, zoomMeters: 1_000) RuntimeLogger.info("APP", "Startup", "已使用实时定位准备唯一初始地图状态", details: [ "地图标准": CoordinateConverter.currentMapCoordinateSystem.rawValue, "修正兜底标准": String(mapCoordinateSystemChange != nil), "缩放米": "1000" ]) RealtimeLocationTrace.coordinate( "地图创建前取得的初始实时位置(WGS-84)", coordinate: realtime ) } else { RuntimeLogger.warning("APP", "Startup", "地图创建前无法取得实时定位,唯一初始位置使用深圳", details: [ "地图标准": mapCoordinateSystem.rawValue, "缩放米": "1000" ]) } } else { RuntimeLogger.info("APP", "Startup", "已找到持久化图钉,直接准备唯一初始地图状态") } guard !Task.isCancelled else { return } if launchMode == .thirdParty { setup.completeSetup() } else if verifiedDuringInitialSetup { verifiedDuringInitialSetup = false setup.completeSetup() } else if setupCompleted { let result = await setup.runVerificationTest() setup.applyVerificationResult(result) } else { setup.requestSetup() } RuntimeLogger.info("APP", "Startup", "启动门禁全部完成,现在创建 MapHomeView") phase = .map } private func finishInitialSetup() { verifiedDuringInitialSetup = runtimeMode.mode == .localWiFi setupCompleted = true setup.completeSetup() phase = .splash Task { await bootstrap() } } }