Files
suixinkan_ios_new/suixinkan/App/App.md
汉秋 5bdf4a7dbf 同步 Android 启动页到 iOS,并在冷启动期间静默恢复登录态。
新增 SplashView、SplashCoordinator 与 Launch Screen 资源,移除冷启动 Lottie;LoginView 加载 App 配置。同时将 token 存储迁移至 UserDefaults,并更新相关测试与文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 14:59:44 +08:00

5.7 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. iOS 系统 Launch Screen 展示白底 + SplashLogo
  4. SplashView 覆盖根视图,展示与 Android 对齐的品牌启动页。
  5. SplashCoordinator.start 并行执行最短 1.5 秒展示和 SessionBootstrapper.restoreUI Test 模式跳过 1.5 秒延迟。
  6. APIClient 绑定 AppSession.token 作为默认 token provider。
  7. SessionBootstrapper.restore 尝试从 UserDefaults 读取正式 token。
  8. 无 token 时保持 loggedOutSplash 结束后展示 LoginView
  9. 有 token 时进入 restoring,先恢复本地账号快照。
  10. AccountContextLoader 并行请求用户资料和角色权限,再补全景区与门店。
  11. 校验成功后进入 loggedInSplash 结束后展示 MainTabsView
  12. ScenicSpotContext 按当前景区懒加载景点/打卡点。
  13. 明确 token 失效时清空 token 和账号快照,回到登录页。
  14. 普通网络失败时保留本地登录态,使用账号快照进入主界面。
  15. 冷启动 bootstrap 不使用 Lottie 全局 LoadingSplash 即启动等待层。
  16. LoginView 首屏出现后调用 /api/app/config 加载远程配置(如 enable_register),失败静默。

初始化分层

阶段 职责 主要对象
App 入口 UI Test 状态清理、后续可扩展 SDK 初始化 suixinkanAppAppUITestLaunchState
Splash 品牌展示 + 登录态恢复 SplashViewSplashCoordinatorSessionBootstrapper
首屏出现后 页面级远程配置 LoginViewAppConfigAPI
登录后 推送、景点列表、排队 WebSocket PushNotificationManagerScenicSpotContext

UI Test 启动约定

  • AppUITestLaunchState 在收到 -suixinkan-ui-tests 时跳过推送注册和排队 WebSocket避免系统弹窗干扰自动化。
  • -suixinkan-ui-tests-reset-state 用于冷启动清理 UserDefaults 中的 token、账号快照和偏好。
  • -suixinkan-ui-tests-open-menu <菜单标题> 登录后直达首页调试目录中的目标页。
  • -suixinkan-ui-tests-open-profile <路由名> 登录后直达个人中心二级页(如 settingsrealNameAuth)。
  • AppUITestRouteDriver 仅在 DEBUG 构建下解析上述直达参数,供 XCUITest 逐页验证。
  • 详细运行方式见 suixinkanUITests/README.md

登录和退出

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

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

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

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

Toast

全局 Toast 由 RootView 挂载在页面最上层,业务页面只调用 toastCenter.show(...) 发出提示命令。

Toast 展示为顶部全宽横幅,背景使用不透明主色并延伸到屏幕顶部、左边和右边;文案居中展示,不提供关闭按钮,默认 2.2 秒后自动消失。连续展示新 Toast 时会覆盖旧文案并重新计时,旧的自动隐藏任务不会影响新的 Toast。

导航规则

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

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

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