Refactor AppServices into grouped bundles and document DI conventions.

Centralize dependency wiring with Session/Context/Network/UI/Runtime bundles, unify leaf ViewControllers on appServices, and add a test initializer with AppServicesTests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 15:24:25 +08:00
parent d99a5b1bf8
commit 24a7339b68
10 changed files with 385 additions and 114 deletions

View File

@ -12,46 +12,55 @@ import Foundation
final class AppServices {
static let shared = AppServices()
let appSession = AppSession()
let accountContext = AccountContext()
let permissionContext = PermissionContext()
let scenicSpotContext = ScenicSpotContext()
let session: SessionBundle
let context: AppContextBundle
let network: NetworkBundle
let ui: UIFeedbackBundle
let runtime: AppRuntimeBundle
let appRouter = AppRouter()
let toastCenter = ToastCenter()
let globalLoading = GlobalLoadingCenter()
let scenicQueueRuntime = ScenicQueueRuntime()
let apiClient: APIClient
let authAPI: AuthAPI
let profileAPI: ProfileAPI
let uploadAPI: UploadAPI
let ossUploadService: OSSUploadService
let accountContextAPI: AccountContextAPI
let ordersAPI: OrdersAPI
let statisticsAPI: StatisticsAPI
let paymentAPI: PaymentAPI
let walletAPI: WalletAPI
let pushAPI: PushAPI
let scenicPermissionAPI: ScenicPermissionAPI
let scenicSettlementAPI: ScenicSettlementAPI
let messageCenterAPI: MessageCenterAPI
let scenicQueueAPI: ScenicQueueAPI
let liveAPI: LiveAPI
let operatingAreaAPI: OperatingAreaAPI
let pilotCertificationAPI: PilotCertificationAPI
let taskAPI: TaskAPI
let projectAPI: ProjectAPI
let scheduleAPI: ScheduleAPI
let inviteAPI: InviteAPI
let assetsAPI: AssetsAPI
let punchPointAPI: PunchPointAPI
let locationReportAPI: LocationReportAPI
let authSessionCoordinator: AuthSessionCoordinator
let sessionBootstrapper: SessionBootstrapper
private let tokenStore: SessionTokenStore
private let snapshotStore: AccountSnapshotStore
// MARK: - 访
var appSession: AppSession { session.appSession }
var accountContext: AccountContext { context.accountContext }
var permissionContext: PermissionContext { context.permissionContext }
var scenicSpotContext: ScenicSpotContext { context.scenicSpotContext }
var toastCenter: ToastCenter { ui.toastCenter }
var globalLoading: GlobalLoadingCenter { ui.globalLoading }
var scenicQueueRuntime: ScenicQueueRuntime { runtime.scenicQueueRuntime }
var apiClient: APIClient { network.apiClient }
var authAPI: AuthAPI { network.authAPI }
var profileAPI: ProfileAPI { network.profileAPI }
var uploadAPI: UploadAPI { network.uploadAPI }
var ossUploadService: OSSUploadService { network.ossUploadService }
var accountContextAPI: AccountContextAPI { network.accountContextAPI }
var ordersAPI: OrdersAPI { network.ordersAPI }
var statisticsAPI: StatisticsAPI { network.statisticsAPI }
var paymentAPI: PaymentAPI { network.paymentAPI }
var walletAPI: WalletAPI { network.walletAPI }
var pushAPI: PushAPI { network.pushAPI }
var scenicPermissionAPI: ScenicPermissionAPI { network.scenicPermissionAPI }
var scenicSettlementAPI: ScenicSettlementAPI { network.scenicSettlementAPI }
var messageCenterAPI: MessageCenterAPI { network.messageCenterAPI }
var scenicQueueAPI: ScenicQueueAPI { network.scenicQueueAPI }
var liveAPI: LiveAPI { network.liveAPI }
var operatingAreaAPI: OperatingAreaAPI { network.operatingAreaAPI }
var pilotCertificationAPI: PilotCertificationAPI { network.pilotCertificationAPI }
var taskAPI: TaskAPI { network.taskAPI }
var projectAPI: ProjectAPI { network.projectAPI }
var scheduleAPI: ScheduleAPI { network.scheduleAPI }
var inviteAPI: InviteAPI { network.inviteAPI }
var assetsAPI: AssetsAPI { network.assetsAPI }
var punchPointAPI: PunchPointAPI { network.punchPointAPI }
var locationReportAPI: LocationReportAPI { network.locationReportAPI }
var authSessionCoordinator: AuthSessionCoordinator { session.authSessionCoordinator }
var sessionBootstrapper: SessionBootstrapper { session.sessionBootstrapper }
/// ID
var currentScenicId: Int? {
accountContext.currentScenic?.id
@ -67,50 +76,74 @@ final class AppServices {
accountContext.profile?.userId
}
///
///
private init() {
let client = APIClient()
let tokenStore = SessionTokenStore()
let snapshotStore = AccountSnapshotStore()
let preferencesStore = AppPreferencesStore()
let apiClient = APIClient()
self.tokenStore = tokenStore
self.snapshotStore = snapshotStore
apiClient = client
authAPI = AuthAPI(client: client)
profileAPI = ProfileAPI(client: client)
uploadAPI = UploadAPI(client: client)
ossUploadService = OSSUploadService(configService: uploadAPI)
accountContextAPI = AccountContextAPI(client: client)
ordersAPI = OrdersAPI(client: client)
statisticsAPI = StatisticsAPI(client: client)
paymentAPI = PaymentAPI(client: client)
walletAPI = WalletAPI(client: client)
pushAPI = PushAPI(client: client)
scenicPermissionAPI = ScenicPermissionAPI(client: client)
scenicSettlementAPI = ScenicSettlementAPI(client: client)
messageCenterAPI = MessageCenterAPI(client: client)
scenicQueueAPI = ScenicQueueAPI(client: client)
liveAPI = LiveAPI(client: client)
operatingAreaAPI = OperatingAreaAPI(client: client)
pilotCertificationAPI = PilotCertificationAPI(client: client)
taskAPI = TaskAPI(client: client)
projectAPI = ProjectAPI(client: client)
scheduleAPI = ScheduleAPI(client: client)
inviteAPI = InviteAPI(client: client)
assetsAPI = AssetsAPI(client: client)
punchPointAPI = PunchPointAPI(client: client)
locationReportAPI = LocationReportAPI(client: client)
authSessionCoordinator = AuthSessionCoordinator(
tokenStore: tokenStore,
snapshotStore: snapshotStore,
preferencesStore: AppPreferencesStore()
context = AppContextBundle(
accountContext: AccountContext(),
permissionContext: PermissionContext(),
scenicSpotContext: ScenicSpotContext()
)
sessionBootstrapper = SessionBootstrapper(
tokenStore: tokenStore,
snapshotStore: snapshotStore
network = NetworkBundle(apiClient: apiClient)
ui = UIFeedbackBundle(
toastCenter: ToastCenter(),
globalLoading: GlobalLoadingCenter()
)
apiClient.bindAuthTokenProvider { [unowned self] in
appSession.token
}
runtime = AppRuntimeBundle(scenicQueueRuntime: ScenicQueueRuntime())
session = SessionBundle(
appSession: AppSession(),
authSessionCoordinator: AuthSessionCoordinator(
tokenStore: tokenStore,
snapshotStore: snapshotStore,
preferencesStore: preferencesStore
),
sessionBootstrapper: SessionBootstrapper(
tokenStore: tokenStore,
snapshotStore: snapshotStore
)
)
bindAuthTokenProvider()
}
/// mock `APIClient`
init(
apiClient: APIClient,
tokenStore: SessionTokenStore = SessionTokenStore(),
snapshotStore: AccountSnapshotStore = AccountSnapshotStore(),
preferencesStore: AppPreferencesStore = AppPreferencesStore()
) {
self.tokenStore = tokenStore
self.snapshotStore = snapshotStore
context = AppContextBundle(
accountContext: AccountContext(),
permissionContext: PermissionContext(),
scenicSpotContext: ScenicSpotContext()
)
network = NetworkBundle(apiClient: apiClient)
ui = UIFeedbackBundle(
toastCenter: ToastCenter(),
globalLoading: GlobalLoadingCenter()
)
runtime = AppRuntimeBundle(scenicQueueRuntime: ScenicQueueRuntime())
session = SessionBundle(
appSession: AppSession(),
authSessionCoordinator: AuthSessionCoordinator(
tokenStore: tokenStore,
snapshotStore: snapshotStore,
preferencesStore: preferencesStore
),
sessionBootstrapper: SessionBootstrapper(
tokenStore: tokenStore,
snapshotStore: snapshotStore
)
)
bindAuthTokenProvider()
}
/// UIKit
@ -121,4 +154,11 @@ final class AppServices {
router: appRouter
)
}
/// `APIClient` `AppSession` token
private func bindAuthTokenProvider() {
apiClient.bindAuthTokenProvider { [unowned self] in
appSession.token
}
}
}