Files
suixinkan_ios_new/suixinkan/App/App.md
2026-06-22 11:28:01 +08:00

3.6 KiB
Raw Blame History

App 模块业务逻辑

模块职责

App 模块负责应用入口、根视图切换、全局状态注入、主导航状态、登录态恢复和全局 Toast。

该模块不直接处理具体页面业务,主要承担 App 级基础设施编排:

  • 根据登录状态展示登录页、恢复态或主 Tab。
  • 创建并注入全局状态和共享服务。
  • 管理每个 Tab 独立的 NavigationStack 路径。
  • 协调登录成功、退出登录、冷启动恢复和本地缓存同步。

核心对象

  • suixinkanAppSwiftUI 应用入口,挂载 RootView
  • RootView:创建 AppSessionAccountContextPermissionContextScenicSpotContextAppRouterToastCenterAPIClient 和业务 API并注入 SwiftUI Environment。
  • AppSession:保存认证阶段和正式 token只负责登录态不承载业务资料。
  • AccountContext:保存当前账号资料、景区作用域和门店作用域。
  • PermissionContext:保存角色权限、当前角色和扁平化权限 URI。
  • ScenicSpotContext:保存当前景区下的景点/打卡点列表和加载状态。
  • AppRouter:保存当前 Tab 和每个 Tab 自己的导航路径。
  • ToastCenter:保存当前全局 Toast 文案。
  • AuthSessionCoordinator:统一处理登录完成、退出登录、偏好读取和账号快照刷新。
  • SessionBootstrapper:冷启动时读取本地 token 和账号快照,并向服务端校验登录态。
  • AccountContextLoader:统一同步用户资料、角色权限、景区和门店。

启动流程

  1. suixinkanApp 创建 RootView
  2. RootView 初始化共享依赖,并把它们注入 Environment。
  3. APIClient 绑定 AppSession.token 作为默认 token provider。
  4. SessionBootstrapper.restore 尝试从 Keychain 读取正式 token。
  5. 无 token 时保持 loggedOut,展示 LoginView
  6. 有 token 时进入 restoring,先恢复本地账号快照。
  7. AccountContextLoader 并行请求用户资料和角色权限,再补全景区与门店。
  8. 校验成功后进入 loggedIn,展示 MainTabsView
  9. ScenicSpotContext 按当前景区懒加载景点/打卡点。
  10. 明确 token 失效时清空 token 和账号快照,回到登录页。
  11. 普通网络失败时保留本地登录态,使用账号快照进入主界面。

登录和退出

登录完成由 AuthSessionCoordinator.completeLogin 统一处理:

  • 正式 token 写入 SessionTokenStore
  • 上次手机号和协议状态写入 AppPreferencesStore
  • 账号资料、景区列表、门店列表写入 AccountContext
  • 角色权限和当前角色写入 PermissionContext
  • 非敏感账号快照写入 AccountSnapshotStore
  • AppSession 切换为 loggedIn

退出登录由 AuthSessionCoordinator.logout 统一处理:

  • 清空 Keychain token。
  • 清空账号快照。
  • 重置账号上下文、权限上下文、景点上下文、导航路径和 Toast。
  • AppSession 切换为 loggedOut
  • 保留上次手机号、协议状态等非敏感偏好。

导航规则

主界面使用 TabView,每个 Tab 内部由单独的 NavigationStack 包裹。AppRouter 为每个 AppTab 持有独立 RouterPath,切换 Tab 不会丢失该 Tab 的内部导航路径。

Tab 根页面显示底部 TabBar。通过 AppRoute push 到子页面时,MainTabsView 会根据 AppRoute.hidesTabBarWhenPushed 统一隐藏 TabBar避免每个业务页面重复处理。

当前路由枚举 AppRoute 仍以占位详情页为主,后续新增真实页面时应优先扩展 AppRoute,再由对应 Tab 的 NavigationStack 处理跳转。