所属阶段:第四阶段「语言与框架」(第 17-22 课) 前置条件:第 17 课(后端语言) 本课收获:了解移动端 Skill 体系,能分析 Anti-Patterns
一、本课概述
移动端开发有自己独特的世界 --- UI 线程安全、设备资源限制、平台审核规范、离线支持。ECC 为 Swift/SwiftUI 和 Dart/Flutter 两大生态提供了深度 Skill 支持,甚至覆盖了最前沿的设备端 LLM 集成。
本课回答三个问题:
- Swift 生态有哪些 Skill? --- 从 SwiftUI 到 Swift 6.2 并发模型
- Flutter/Dart 生态有哪些 Skill? --- 跨平台架构与代码审查
- 移动端 Skill 的 Anti-Patterns 是什么? --- 从反面学习最佳实践
二、Swift 生态 Skill 全景
2.1 Skill 清单
Swift 是 ECC 中移动端 Skill 最丰富的语言,共 5 个专用 Skill:
| Skill | 定位 | 核心主题 |
|---|---|---|
swiftui-patterns |
SwiftUI 架构 | @Observable 状态管理、视图组合、导航 |
swift-concurrency-6-2 |
Swift 6.2 并发 | 单线程默认、@concurrent、actor 隔离 |
swift-actor-persistence |
Actor 持久化 | Actor 线程安全持久化、CoreData/SwiftData |
swift-protocol-di-testing |
协议与测试 | 协议 DI、可测试性设计、Mock 策略 |
foundation-models-on-device |
设备端 AI | 设备端 LLM、@Generable 宏 |
2.2 Skill 关系图
csharp
swiftui-patterns
(UI 层:视图 + 状态 + 导航)
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
swift-concurrency swift-actor swift-protocol
-6-2 -persistence -di-testing
(并发模型) (持久化层) (可测试性)
│
▼
foundation-models
-on-device
(设备端 AI)
这些 Skill 形成了一条从 UI 到底层的完整链:SwiftUI 视图 → 并发安全 → 数据持久化 → 协议抽象 → 设备端 AI。
三、swiftui-patterns 深入
3.1 @Observable 状态管理
Swift 5.9 引入了 @Observable 宏,取代了 ObservableObject 协议。swiftui-patterns Skill 强调新模式:
swift
// 旧模式(Swift 5.8 及之前)--- 不再推荐
class UserViewModel: ObservableObject {
@Published var name: String = ""
@Published var email: String = ""
}
// 新模式(Swift 5.9+)--- 推荐
@Observable
class UserViewModel {
var name: String = ""
var email: String = ""
}
关键区别 :@Observable 实现了属性级别 的变更追踪,而不是整个对象级别。这意味着当 name 变化时,只有用到 name 的视图会重新渲染,用到 email 的视图不受影响。
3.2 视图组合原则
swiftui-patterns 推荐的视图组织方式:
css
视图层级:
Screen(屏幕) → 顶层容器,处理导航和数据获取
Section(区块) → 逻辑分组
Component(组件)→ 可复用的 UI 单元
Element(元素)→ 最小 UI 原语
Anti-Pattern:巨型视图
swift
// WRONG --- 一个视图做了太多事情(God View)
struct UserProfileScreen: View {
var body: some View {
ScrollView {
// 头像区域 ... 50 行
// 个人信息 ... 80 行
// 设置列表 ... 100 行
// 底部操作 ... 30 行
}
}
}
// CORRECT --- 拆分为子组件
struct UserProfileScreen: View {
var body: some View {
ScrollView {
AvatarSection(user: user)
InfoSection(user: user)
SettingsSection(settings: settings)
ActionBar(onLogout: handleLogout)
}
}
}
3.3 导航模式
SwiftUI 的导航经历了多次演进。swiftui-patterns 推荐 NavigationStack 模式:
swift
// 推荐:NavigationStack + NavigationPath
@Observable
class Router {
var path = NavigationPath()
func push(_ destination: Destination) {
path.append(destination)
}
func pop() {
path.removeLast()
}
func popToRoot() {
path.removeLast(path.count)
}
}
四、Swift 6.2 并发模型
4.1 swift-concurrency-6-2 核心变化
Swift 6.2 带来了并发模型的重大变化。swift-concurrency-6-2 Skill 是理解这些变化的关键:
核心变化:默认单线程
perl
Swift 6.1 及之前:
nonisolated 函数 → 可能在任意线程执行
Swift 6.2:
nonisolated 函数 → 默认在 caller 的 actor 上执行
@concurrent 标注 → 显式声明"可以在其他线程执行"
为什么这样设计?
大多数代码不需要并发执行。默认单线程减少了数据竞争的风险,需要并发时显式标注 @concurrent。
4.2 Actor 隔离
swift
actor DatabaseManager {
private var cache: [String: Data] = [:]
func fetch(key: String) -> Data? {
// 这里是 actor 隔离的 --- 线程安全
return cache[key]
}
func store(key: String, value: Data) {
// 同一时刻只有一个任务能执行
cache[key] = value
}
}
4.3 swift-actor-persistence
swift-actor-persistence Skill 处理一个棘手问题:如何在 Actor 隔离的约束下进行数据持久化。
问题:
CoreData/SwiftData 的 context 不是线程安全的
Actor 保证了内部状态的线程安全
如何让两者协作?
解决方案:
ModelActor 宏 --- 创建一个绑定到特定 context 的 Actor
swift
@ModelActor
actor PersistenceActor {
func createUser(name: String) throws -> User {
let user = User(name: name)
modelContext.insert(user)
try modelContext.save()
return user
}
}
五、设备端 LLM 集成
5.1 foundation-models-on-device
这是 ECC 中最前沿的 Skill 之一。Apple 在 iOS 26 / macOS 26 中引入了 Foundation Models 框架,允许在设备上运行 LLM。
@Generable 宏:
swift
@Generable
struct RecipeSuggestion {
var title: String
var ingredients: [String]
var steps: [String]
var estimatedTime: Int
}
// 使用
let session = LanguageModelSession()
let suggestion: RecipeSuggestion = try await session.respond(
to: "Suggest a quick pasta recipe",
generating: RecipeSuggestion.self
)
设备端 vs 云端 LLM:
| 维度 | 设备端 | 云端 |
|---|---|---|
| 隐私 | 数据不离开设备 | 数据上传到服务器 |
| 延迟 | 无网络延迟 | 受网络影响 |
| 能力 | 受限于设备算力 | 几乎无限 |
| 离线 | 可用 | 不可用 |
| 成本 | 免费 | 按 token 计费 |
六、Flutter / Dart 生态
6.1 Skill 清单
| Skill | 定位 | 核心主题 |
|---|---|---|
dart-flutter-patterns |
Dart + Flutter 架构 | 空安全、状态管理、Widget 架构、GoRouter |
flutter-dart-code-review |
代码审查 | Widget 最佳实践、性能陷阱 |
compose-multiplatform-patterns |
KMP 共享 UI | Kotlin Multiplatform 共享 UI 层 |
android-clean-architecture |
Android 架构 | Clean Architecture、KMP 模块划分 |
6.2 dart-flutter-patterns 核心
空安全(Null Safety):
Dart 的空安全系统是类型系统的一部分,dart-flutter-patterns 强调:
dart
// 类型系统保证
String name; // 不可空 --- 必须有值
String? nickname; // 可空 --- 可以是 null
String title = ''; // 不可空 + 有默认值
// 空安全操作符
nickname?.toUpperCase() // 空时返回 null
nickname ?? 'Anonymous' // 空时用默认值
nickname!.toUpperCase() // 断言非空(谨慎使用)
状态管理方案对比:
| 方案 | 复杂度 | 适用场景 | ECC 推荐度 |
|---|---|---|---|
setState |
低 | 局部状态 | 仅限简单场景 |
| Provider | 中 | 中型应用 | 推荐 |
| Riverpod | 中高 | 中大型应用 | 强烈推荐 |
| BLoC | 高 | 大型应用 | 企业级推荐 |
Widget 架构:
dart
// Anti-Pattern: 深层嵌套
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Container(
decoration: BoxDecoration(...),
child: Row(
children: [
// 已经 6 层嵌套了...
],
),
),
],
),
),
),
);
}
// 正确做法:提取子 Widget
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: EdgeInsets.all(16),
child: _ContentColumn(),
),
),
);
}
6.3 GoRouter 导航
dart-flutter-patterns 推荐 GoRouter 作为导航方案:
dart
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
routes: [
GoRoute(
path: 'users/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return UserDetailScreen(userId: id);
},
),
],
),
],
);
七、跨平台方案对比
7.1 Flutter vs Compose Multiplatform
ECC 同时提供了两种跨平台方案的 Skill:
| 维度 | Flutter (dart-flutter-patterns) |
KMP (compose-multiplatform-patterns) |
|---|---|---|
| 语言 | Dart | Kotlin |
| UI 引擎 | 自绘引擎(Skia/Impeller) | 平台原生 + Compose |
| 代码共享 | UI + 逻辑全共享 | 逻辑共享,UI 可选共享 |
| 平台 | iOS, Android, Web, Desktop | iOS, Android, Desktop, Web |
| 学习曲线 | 中等 | 已有 Kotlin 经验则较低 |
| ECC Agent | flutter-reviewer, dart-build-resolver |
kotlin-reviewer, kotlin-build-resolver |
7.2 android-clean-architecture
android-clean-architecture Skill 定义了 Android 项目的分层架构:
scss
┌─────────────────────────┐
│ Presentation Layer │ ← UI + ViewModel
│ (Android/Compose) │
├─────────────────────────┤
│ Domain Layer │ ← Use Cases + Entities
│ (Pure Kotlin) │ 无框架依赖
├─────────────────────────┤
│ Data Layer │ ← Repository 实现
│ (Room/Retrofit/etc.) │ + Data Sources
└─────────────────────────┘
关键原则:Domain Layer 是纯 Kotlin,不依赖任何 Android 框架。这使得业务逻辑可以在 KMP 项目中跨平台共享。
八、移动端 Anti-Patterns 分析
8.1 SwiftUI Anti-Patterns
| Anti-Pattern | 问题 | 正确做法 |
|---|---|---|
| God View | 一个视图超过 300 行 | 拆分为 Screen → Section → Component |
| 过度使用 @State | 所有状态都放在视图里 | 提取到 @Observable ViewModel |
| 忽略 Actor 隔离 | 在主线程做耗时操作 | 使用 Actor 或 Task.detached |
| 强制解包 | 到处使用 ! |
使用 guard let 或 if let |
| 忽略生命周期 | 不清理 Task | 使用 .task modifier 自动管理 |
8.2 Flutter Anti-Patterns
| Anti-Pattern | 问题 | 正确做法 |
|---|---|---|
| 深层嵌套 | Widget 嵌套超过 5 层 | 提取子 Widget 或自定义 Widget |
| setState 滥用 | 在大型应用中用 setState | 使用 Riverpod 或 BLoC |
| 阻塞 UI 线程 | 在 build 方法中做计算 | 使用 compute() 或 Isolate |
| 不使用 const | 每次 build 创建新 Widget | 尽可能使用 const 构造器 |
| 忽略 Key | 列表不使用 Key | 为列表项提供唯一 Key |
九、本课练习
练习 1:查看移动端 Skill(10 分钟)
bash
# Swift 生态
ls skills/swiftui-patterns/
ls skills/swift-concurrency-6-2/
# Flutter 生态
ls skills/dart-flutter-patterns/
回答问题:
swiftui-patterns中关于 @Observable 的章节在哪里?dart-flutter-patterns推荐了哪些状态管理方案?
练习 2:分析 Anti-Patterns(20 分钟)
这是本课最重要的练习。
选择一个移动端 Skill(如 swiftui-patterns 或 dart-flutter-patterns),找到其中描述的 Anti-Patterns 部分。
对每个 Anti-Pattern:
- 用自己的话解释为什么它是问题
- 写出正确做法的伪代码
- 思考在你的项目中是否存在类似问题
练习 3:对比导航方案(15 分钟)
对比 SwiftUI 的 NavigationStack 和 Flutter 的 GoRouter:
- 它们的路由定义方式有什么相似之处?
- 深层链接(Deep Link)的处理方式有什么差异?
练习 4(选做):思考题
设备端 LLM(foundation-models-on-device)适合哪些移动应用场景?不适合哪些?限制因素是什么?
十、本课小结
| 你应该记住的 | 内容 |
|---|---|
| Swift 生态 | 5 个 Skill,从 SwiftUI 到设备端 LLM |
| 核心变化 | Swift 6.2 默认单线程,@concurrent 显式并发 |
| Flutter 生态 | 4 个 Skill,含跨平台 KMP 方案 |
| Anti-Patterns | SwiftUI 的 God View,Flutter 的深层嵌套 |
| 跨平台选择 | Flutter 全共享 vs KMP 逻辑共享 |
十一、下节预告
第 20 课:数据库模式 --- 设计、迁移与优化
下节课我们将进入数据层。你将学习 PostgreSQL 查询优化、零停机数据库迁移、以及 ECC 的 database-reviewer Agent 如何帮你避免 N+1 查询和不安全的 Schema 变更。
预习建议 :提前浏览 skills/postgres-patterns 和 skills/database-migrations 目录。