Files
ios-client/CLAUDE.md
2026-09-15 14:35:52 +08:00

103 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md — iOS 客户端项目 AI 协作规范
## 项目概览
SwiftUI + MVVM 单向数据流 + SPM 多 target 模块化的 iOS「打卡/计划管理」客户端,功能对齐 `wechat-clock-mini`(小程序,生产参照)与 `android-client`(Android 端,原生简化参照)。架构对齐 Android NIA(`core/*` + `feature/*`)。
四个主入口 Tab(顺序):**首页 / 打卡 / 计划 / 我的**;另有独立**登录页**(手机号 + 短信验证码)与**提交计划页**(计划 Tab 内推入)。
## 目录结构
- `ios-client.xcodeproj` / `ios-client/`:App 宿主(薄壳,只含 `@main` 挂载 `AppRootView`)
- `IosClientKit/`:本地 SPM 包,承载所有模块
- `Sources/App`:组合根(`AppContainer` DI + `AppRootView` 导航)
- `Sources/CoreModel`:领域模型(`Sendable` 值类型,叶子)
- `Sources/CoreCommon`:通用工具(叶子)
- `Sources/CoreDomain`:Repository 协议 + UseCase(纯 Swift,不 import SwiftUI)
- `Sources/CoreRepository`:Repository 实现(Fake + 未来 OfflineFirst)
- `Sources/CoreNetwork`:DTO + APIClient(URLSession)
- `Sources/CoreDatabase`:SwiftData(`@Model` + `ModelContainer`)
- `Sources/DesignSystem`:设计 tokens
- `Sources/CoreUI`:可复用 SwiftUI 组件
- `Sources/FeatureHome`:首页(落地页:Hero + 模块卡 + 产品介绍)
- `Sources/FeatureCheckin`:打卡(状态卡 + 状态 pill + 小组/个人记录)
- `Sources/FeaturePlan`:计划(周/月/年 + 小组统计 + 提交计划页)
- `Sources/FeatureProfile`:我的(资料 + 小组分组 + 退出登录)
- `Sources/FeatureAuth`:登录(手机号 + 短信验证码 + 60s 倒计时)
## 依赖方向(硬约束,违反即编译不过)
单向无环:`App → feature → core`;`App` 额外 → `CoreRepository`(装配具体实现)。
- 领域模型只在 `CoreModel`,其余模块只能 import `CoreModel`。
- Repository 协议只定义在 `CoreDomain`,实现在 `CoreRepository`。
- `CoreDomain` 纯 Swift:禁止 `import SwiftUI` / 数据框架。
- DTO 只在 `CoreNetwork`,领域 Model 只在 `CoreModel`,二者分离。
- **禁止**命名任何模块为 `CoreData`(与苹果 `CoreData` 框架撞名,已统一为 `CoreRepository`)。
## 状态管理约定
- ViewModel:`@MainActor @Observable final class`,持有 `uiState`。
- UiState:`enum` 关联值(对应 Kotlin `sealed interface`)。
- Kotlin → Swift 映射:
| Android (Kotlin) | iOS (Swift) |
|---|---|
| `Flow<T>` | `AsyncThrowingStream<T, Error>` |
| `StateFlow` + `stateIn` | `@Observable` + `@MainActor` |
| `flatMapLatest` | `Task` 取消自动 switch-to-latest |
| `sealed interface UiState` | `enum UiState`(关联值)|
| Hilt `@Inject` / AppContainer | 手写 `AppContainer` |
| Retrofit + kotlinx.serialization | URLSession + Codable |
| Room | SwiftData |
| DataStore(会话持久化) | Combine `CurrentValueSubject`(进程内 Fake 会话)|
## 会话 / 登录态
- `SessionRepository` 协议(CoreDomain)+ `FakeSessionRepository`(CoreRepository,`final class` + Combine `CurrentValueSubject<Bool, Never>`,默认未登录,**AppContainer 持单例共享**)。
- 登录态驱动页面:`HomeViewModel` 双源切换(已登录 `observeHome()` / 游客 `observeGuestHome()`);checkin/plan/profile 四态含 `loggedOut`。
- flatMapLatest 的 iOS 等价:外层 `Task` 观察 `observeIsLoggedIn()`,登录态变化时取消内层数据 `Task` 并重新订阅。
- 登录 mock 凭据:手机号 `13812341234` + 验证码 `123456`。
## 代码风格
- 跨模块符号一律 `public`,并加 `///` 文档注释。
- 领域模型标记 `Sendable`。
- 命名:类型 PascalCase,变量/函数 camelCase;UI 文案与注释可用中文。
## 明确禁止
- ❌ TCA、Swinject 等 DI 框架、Moya 等网络封装、任何第三方大框架
- ❌ UIKit(除非 SwiftUI 确无法覆盖的极少数场景)
- ❌ 循环依赖 / 反向 import
## 构建 / 验证命令
```bash
# 编译包(同时做依赖环检测,SPM 遇环即报错)
swift build --package-path IosClientKit
# 模拟器编译 App
xcodebuild -project ios-client.xcodeproj -scheme ios-client \
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO build
# Lint
swiftlint lint --strict
```
## 新增 Feature 清单
1. 在 `IosClientKit/Package.swift` 的 `targets` 加 `FeatureXxx` 及依赖。
2. 新建 `Sources/FeatureXxx/`:Screen + ViewModel + UiState(enum)。
3. UseCase 放 `CoreDomain`,Repository 实现放 `CoreRepository`。
4. 在 `App` 的 `AppContainer` 装配依赖、`AppRootView` 接入导航。
## 文档地图
| 主题 | 文档 |
|---|---|
| 架构与数据流 | [docs/architecture.md](docs/architecture.md) |
| 工作状态与遗留项 | [docs/progress.md](docs/progress.md) |
| 贡献指南 | [CONTRIBUTING.md](CONTRIBUTING.md) |