# suixinkan ## 项目概览 **Swift + UIKit** 工程,**MVVM** 架构。iOS 版 **随心瞰商家版**,与 Android **ZhiFlyFollow** 功能对齐——以 Android 工程为参考同步实现业务能力,UI 按 UIKit 习惯编写,不逐行翻译 Compose。 **Android 参考:** `/Users/hanqiu/Desktop/android_project/zhiflyfollow`(`com.zhifly.follow`) **文档:** `CLAUDE.md`、`docs/home/`、`docs/排队叫号业务流程梳理.md` **后端:** 生产 `api.zhifly.cn`,测试 `api-test.zhifly.cn` **主要功能域:** 首页/订单/数据/我的 Tab;登录注册与实名认证;订单/任务/核销;相册与云存储;直播与钱包;景区排队叫号、位置上报;消息、设置、扫码、语音通话等。 **同步约定:** 接口与业务流程以 Android `NetworkApi` / DTO / ViewModel 为准;UI 用 SnapKit 独立设计;OTG 相机、DJI 飞控、厂商推送等 Android 专属能力按需评估。 ## 平台与设备 iOS 16+ · 仅 iPhone · 仅竖屏。不做 iPad / 横屏 / 低版本 API 适配。 ## 架构(MVVM) | 层级 | 职责 | |------|------| | **View** | `UIViewController` / 自定义 `UIView`,UI 展示与交互,不含业务逻辑 | | **ViewModel** | 业务逻辑与状态;通过方法、delegate 或明确回调与 View 通信 | | **Model** | 数据模型、网络/本地数据源 | - 新增功能:对应 Model + ViewModel + ViewController,同步单元测试 - ViewModel 不依赖 UIKit 视图类型 ## 开发风格 **命令式 UIKit**,不用 `@Observable` / `ObservableObject` / `@Published`、不用 Combine 驱动 UI、不堆泛化 `onChanged` 回调。 - 在明确时机(点击、网络回调、页面出现)主动刷新 UI(`apply(snapshot)` 或更新子视图) - 通信用 delegate、target-action 或语义明确的单一回调 - 对照 Android **行为** 用 UIKit 重写,不翻译 Compose 语法 - ViewModel 属性供读取,UI 更新由 View 在回调中主动触发 ## Swift 并发与 Actor 工程 **Default Actor Isolation** 配置为 **`nonisolated`**(`suixinkan` 与 `suixinkanTests` 均为 `SWIFT_DEFAULT_ACTOR_ISOLATION = nonisolated`)。新类型默认不隐式绑定 `@MainActor`,与命令式 UIKit + 显式刷新风格一致。 | Target | Default Actor Isolation | |--------|-------------------------| | `suixinkan` | `nonisolated` | | `suixinkanTests` | `nonisolated` | 编写约定: - **ViewModel / Model**:保持 `nonisolated`,不依赖默认 MainActor;纯逻辑与校验可用 `nonisolated static`。 - **需要主线程的类型**:在类型上**显式**标注 `@MainActor`(如 `APIClient`、`AuthAPI`、`ProfileAPI`、`NetworkServices`、`OSSUploadService`、`AppRouter`)。 - **UI 更新**:在 `UIViewController` 内通过 `Task { @MainActor in ... }` 或已在主线程的回调中刷新 UI,不假设 ViewModel 调用方一定在主 Actor;`onStateChange` 回调应切回主线程再改 UI。 - **网络与可发送类型**:请求/环境等跨边界类型保持 `nonisolated` + `Sendable`(如 `APIRequest`),避免无意引入 actor 隔离冲突。 - **单元测试**:target 默认为 `nonisolated`;`MockURLSession` 等替身保持 `nonisolated`。需要构造 `@MainActor` 类型(`APIClient`、`AuthAPI` 等)的测试类应显式标 `@MainActor`。 新增代码不要为「凑隔离」给 ViewModel 或普通 model 加 `@MainActor`;仅 UI 入口、共享网络单例等确需主线程协调处显式标注。 ## 代码注释 `class` / `struct` / `enum`、协议及公开 API 必须写 `///` 文档注释,说明职责与用途;复杂内部逻辑按需补充。不写废话注释、不写过时注释。 ## 技术栈与库 Swift · UIKit · SnapKit(代码布局,约束写在 `setupUI()` / `setupConstraints()`)· Kingfisher(`kf.setImage(with:)` 加载网络图,列表利用缓存) ## 列表展示 - 简单列表 → `UITableView`;复杂列表(网格、卡片、不等高)→ `UICollectionView` + Compositional Layout - 数据更新用 Diffable Data Source + `apply(_:animatingDifferences: true)`,Item 遵循 `Hashable`,避免 `reloadData()` - ViewModel 提供数据,View 构建 snapshot 并 apply ## 单元测试 每增一功能必须附带单元测试且全部通过(`Cmd + U` / `xcodebuild test`),与功能同次提交。 | 层级 | 优先级 | |------|--------| | ViewModel / Model | ✅ 优先 | | View | 按需 | 测试放 `suixinkanTests/`,独立可重复,mock 网络与外部依赖。 ## 开发注意 - SnapKit 布局、Kingfisher 图片、Diffable 列表带动画 - 以 Android 工程为功能参考,命令式 UIKit,不用 Observable / Combine - 新增功能必测、类型与公开 API 必注释、保持简洁