Migrate from iOS 17 Observation to iOS 16-compatible Combine architecture.

Lower deployment target to iOS 16, replace @Observable with ObservableObject, add navigation and UI compatibility shims, and include login smoke UI tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 10:16:35 +08:00
parent c32a610ee0
commit 26f4d0e671
127 changed files with 2320 additions and 1465 deletions

View File

@ -10,12 +10,12 @@ import SwiftUI
/// App
struct RootView: View {
@Environment(\.scenePhase) private var scenePhase
@State private var appSession = AppSession()
@State private var accountContext = AccountContext()
@State private var permissionContext = PermissionContext()
@State private var scenicSpotContext = ScenicSpotContext()
@State private var appRouter = AppRouter()
@State private var toastCenter = ToastCenter()
@StateObject private var appSession = AppSession()
@StateObject private var accountContext = AccountContext()
@StateObject private var permissionContext = PermissionContext()
@StateObject private var scenicSpotContext = ScenicSpotContext()
@StateObject private var appRouter = AppRouter()
@StateObject private var toastCenter = ToastCenter()
@State private var globalLoading = GlobalLoadingCenter()
@State private var snapshotStore: AccountSnapshotStore
@State private var apiClient: APIClient
@ -33,7 +33,7 @@ struct RootView: View {
@State private var scenicSettlementAPI: ScenicSettlementAPI
@State private var messageCenterAPI: MessageCenterAPI
@State private var scenicQueueAPI: ScenicQueueAPI
@State private var scenicQueueRuntime = ScenicQueueRuntime()
@StateObject private var scenicQueueRuntime = ScenicQueueRuntime()
@State private var liveAPI: LiveAPI
@State private var operatingAreaAPI: OperatingAreaAPI
@State private var pilotCertificationAPI: PilotCertificationAPI
@ -98,41 +98,41 @@ struct RootView: View {
rootContent
.globalToastOverlay()
.globalLoadingOverlay(loadingCenter: globalLoading)
.environment(appSession)
.environment(accountContext)
.environment(permissionContext)
.environment(scenicSpotContext)
.environment(appRouter)
.environment(toastCenter)
.environmentObject(appSession)
.environmentObject(accountContext)
.environmentObject(permissionContext)
.environmentObject(scenicSpotContext)
.environmentObject(appRouter)
.environmentObject(toastCenter)
.environment(\.globalLoading, globalLoading)
.environment(snapshotStore)
.environment(apiClient)
.environment(authAPI)
.environment(profileAPI)
.environment(uploadAPI)
.environment(ossUploadService)
.environment(accountContextAPI)
.environment(ordersAPI)
.environment(statisticsAPI)
.environment(paymentAPI)
.environment(walletAPI)
.environment(pushAPI)
.environment(scenicPermissionAPI)
.environment(scenicSettlementAPI)
.environment(messageCenterAPI)
.environment(scenicQueueAPI)
.environment(scenicQueueRuntime)
.environment(liveAPI)
.environment(operatingAreaAPI)
.environment(pilotCertificationAPI)
.environment(taskAPI)
.environment(projectAPI)
.environment(scheduleAPI)
.environment(inviteAPI)
.environment(assetsAPI)
.environment(punchPointAPI)
.environment(locationReportAPI)
.environment(authSessionCoordinator)
.environment(\.accountSnapshotStore, snapshotStore)
.environment(\.apiClient, apiClient)
.environment(\.authAPI, authAPI)
.environment(\.profileAPI, profileAPI)
.environment(\.uploadAPI, uploadAPI)
.environment(\.ossUploadService, ossUploadService)
.environment(\.accountContextAPI, accountContextAPI)
.environment(\.ordersAPI, ordersAPI)
.environment(\.statisticsAPI, statisticsAPI)
.environment(\.paymentAPI, paymentAPI)
.environment(\.walletAPI, walletAPI)
.environment(\.pushAPI, pushAPI)
.environment(\.scenicPermissionAPI, scenicPermissionAPI)
.environment(\.scenicSettlementAPI, scenicSettlementAPI)
.environment(\.messageCenterAPI, messageCenterAPI)
.environment(\.scenicQueueAPI, scenicQueueAPI)
.environmentObject(scenicQueueRuntime)
.environment(\.liveAPI, liveAPI)
.environment(\.operatingAreaAPI, operatingAreaAPI)
.environment(\.pilotCertificationAPI, pilotCertificationAPI)
.environment(\.taskAPI, taskAPI)
.environment(\.projectAPI, projectAPI)
.environment(\.scheduleAPI, scheduleAPI)
.environment(\.inviteAPI, inviteAPI)
.environment(\.assetsAPI, assetsAPI)
.environment(\.punchPointAPI, punchPointAPI)
.environment(\.locationReportAPI, locationReportAPI)
.environment(\.authSessionCoordinator, authSessionCoordinator)
.task {
apiClient.bindAuthTokenProvider { appSession.token }
PushNotificationManager.shared.configure(api: pushAPI, session: appSession, router: appRouter)
@ -160,7 +160,7 @@ struct RootView: View {
api: accountContextAPI
)
}
.onChange(of: scenePhase) { _, newPhase in
.onChange(of: scenePhase) { newPhase in
if appSession.isLoggedIn {
scenicQueueRuntime.update(
api: scenicQueueAPI,
@ -172,7 +172,7 @@ struct RootView: View {
scenicQueueRuntime.stop()
}
}
.onChange(of: accountContext.currentScenic?.id) { _, _ in
.onChange(of: accountContext.currentScenic?.id) { _ in
if appSession.isLoggedIn {
scenicQueueRuntime.update(
api: scenicQueueAPI,
@ -182,7 +182,7 @@ struct RootView: View {
)
}
}
.onChange(of: appSession.phase) { _, phase in
.onChange(of: appSession.phase) { phase in
switch phase {
case .loggedIn:
PushNotificationManager.shared.requestAuthorizationAndRegister()