swift-ui-architect
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAirbnb SwiftUI Best Practices
Airbnb SwiftUI最佳实践
Opinionated, strict architectural enforcement for SwiftUI iOS apps. Contains 43 rules across 8 categories, derived from Airbnb Engineering, Apple WWDC sessions, Clean Architecture patterns, and Advanced iOS App Architecture (raywenderlich). This skill mandates a single, non-negotiable architecture stack.
这是一套针对SwiftUI iOS应用的、具有明确立场的严格架构强制规范。包含8个类别共43条规则,源自Airbnb工程实践、Apple WWDC技术分享、Clean Architecture模式以及《Advanced iOS App Architecture》(raywenderlich)。该规范要求采用一套统一且不可妥协的架构栈。
Mandated Architecture Stack
强制架构栈
┌─────────────────────────────────────────────────────┐
│ Presentation Layer (SwiftUI Views) │
│ ├── Views: @Equatable, decomposed, minimal bodies │
│ ├── ViewModels: @Observable classes via @State │
│ └── Coordinators: NavigationStack + enum routes │
├─────────────────────────────────────────────────────┤
│ Domain Layer (Pure Swift) │
│ ├── Use Cases / Interactors (stateless, protocol) │
│ ├── Domain Models (value types, Equatable) │
│ └── Repository Protocols (abstractions only) │
├─────────────────────────────────────────────────────┤
│ Data Layer (Implementation) │
│ ├── Repository Implementations │
│ ├── Network Services │
│ └── Persistence (SwiftData / CoreData) │
└─────────────────────────────────────────────────────┘Dependency Rule: Arrows point inward only. Views -> Domain <- Data. Domain has zero framework imports.
┌─────────────────────────────────────────────────────┐
│ Presentation Layer (SwiftUI Views) │
│ ├── Views: @Equatable, decomposed, minimal bodies │
│ ├── ViewModels: @Observable classes via @State │
│ └── Coordinators: NavigationStack + enum routes │
├─────────────────────────────────────────────────────┤
│ Domain Layer (Pure Swift) │
│ ├── Use Cases / Interactors (stateless, protocol) │
│ ├── Domain Models (value types, Equatable) │
│ └── Repository Protocols (abstractions only) │
├─────────────────────────────────────────────────────┤
│ Data Layer (Implementation) │
│ ├── Repository Implementations │
│ ├── Network Services │
│ └── Persistence (SwiftData / CoreData) │
└─────────────────────────────────────────────────────┘依赖规则:依赖箭头仅指向内部。视图层 -> 领域层 <- 数据层。领域层不导入任何框架。
When to Apply
适用场景
Reference these guidelines when:
- Writing any new SwiftUI view, ViewModel, or coordinator
- Creating or modifying navigation flows
- Adding data fetching, state management, or dependency injection
- Reviewing code for architecture compliance or performance
- Refactoring existing SwiftUI code toward this architecture
在以下场景中参考本规范:
- 编写任何新的SwiftUI视图、ViewModel或协调器
- 创建或修改导航流程
- 添加数据获取、状态管理或依赖注入逻辑
- 评审代码的架构合规性或性能
- 将现有SwiftUI代码重构为该架构
Non-Negotiable Constraints (iOS 17+)
不可妥协的约束条件(iOS 17+)
- everywhere,
@Observable/ObservableObjectnever@Published - + coordinator pattern,
NavigationStackneverNavigationLink(destination:) - macro on every view,
@EquatableneverAnyView - Domain layer has zero SwiftUI/UIKit imports
- Views never access repositories directly
- 所有ViewModel均使用,严禁使用
@Observable/ObservableObject@Published - 采用+ 协调器模式,严禁使用
NavigationStackNavigationLink(destination:) - 每个视图都添加宏,严禁使用
@EquatableAnyView - 领域层不导入任何SwiftUI/UIKit框架
- 视图严禁直接访问仓库(Repository)
Rule Categories by Priority
按优先级划分的规则类别
| Priority | Category | Impact | Prefix | Rules |
|---|---|---|---|---|
| 1 | View Identity & Diffing | CRITICAL | | 6 |
| 2 | State Architecture | CRITICAL | | 7 |
| 3 | View Composition | HIGH | | 6 |
| 4 | Navigation & Coordination | HIGH | | 5 |
| 5 | Layer Architecture | HIGH | | 6 |
| 6 | Dependency Injection | MEDIUM-HIGH | | 4 |
| 7 | List & Collection Performance | MEDIUM | | 4 |
| 8 | Async & Data Flow | MEDIUM | | 5 |
| 优先级 | 类别 | 影响程度 | 前缀 | 规则数量 |
|---|---|---|---|---|
| 1 | 视图标识与差异对比 | 关键 | | 6 |
| 2 | 状态架构 | 关键 | | 7 |
| 3 | 视图组合 | 高 | | 6 |
| 4 | 导航与协调 | 高 | | 5 |
| 5 | 分层架构 | 高 | | 6 |
| 6 | 依赖注入 | 中高 | | 4 |
| 7 | 列表与集合性能 | 中 | | 4 |
| 8 | 异步与数据流 | 中 | | 5 |
Quick Reference
快速参考
1. View Identity & Diffing (CRITICAL)
1. 视图标识与差异对比(关键)
- - Apply @Equatable macro to every SwiftUI view
diff-equatable-views - - Use @SkipEquatable for closure/handler properties
diff-closure-skip - - Never store reference types without Equatable conformance
diff-reference-types - - Use stable O(1) identifiers in ForEach
diff-identity-stability - - Never use AnyView — use @ViewBuilder or generics
diff-avoid-anyview - - Use _printChanges() to diagnose unnecessary re-renders
diff-printchanges-debug
- - 为每个SwiftUI视图添加@Equatable宏
diff-equatable-views - - 为闭包/处理器属性使用@SkipEquatable
diff-closure-skip - - 严禁存储未遵循Equatable协议的引用类型
diff-reference-types - - 在ForEach中使用稳定的O(1)标识符
diff-identity-stability - - 严禁使用AnyView — 改用@ViewBuilder或泛型
diff-avoid-anyview - - 使用_printChanges()诊断不必要的重渲染
diff-printchanges-debug
2. State Architecture (CRITICAL)
2. 状态架构(关键)
- - Use @Observable classes for all ViewModels
state-observable-class - - @State for owned data, plain property for injected data
state-ownership - - One source of truth per piece of state
state-single-source - - Leverage @Observable property-level tracking
state-scoped-observation - - Pass @Binding only for two-way data flow
state-binding-minimal - - Use @Environment for app-wide shared dependencies
state-environment-global - - Never use @Published or ObservableObject
state-no-published
- - 为所有ViewModel使用@Observable类
state-observable-class - - 自有数据使用@State,注入数据使用普通属性
state-ownership - - 每个状态仅有单一数据源
state-single-source - - 利用@Observable的属性级跟踪功能
state-scoped-observation - - 仅在双向数据流场景下传递@Binding
state-binding-minimal - - 使用@Environment注入全局共享依赖
state-environment-global - - 严禁使用@Published或ObservableObject
state-no-published
3. View Composition (HIGH)
3. 视图组合(高)
- - Maximum 10 nodes in view body
view-body-complexity - - Extract computed properties/helpers into separate View structs
view-extract-subviews - - Zero business logic in body
view-no-logic-in-body - - Pass only needed properties, not entire models
view-minimal-dependencies - - Use @ViewBuilder for conditional composition
view-viewbuilder-composition - - Never perform work in View init
view-no-init-sideeffects
- - 视图body中最多包含10个节点
view-body-complexity - - 将计算属性/辅助逻辑提取为独立的View结构体
view-extract-subviews - - 视图body中严禁包含业务逻辑
view-no-logic-in-body - - 仅传递所需属性,而非整个模型
view-minimal-dependencies - - 使用@ViewBuilder实现条件组合
view-viewbuilder-composition - - 严禁在View的init方法中执行耗时操作
view-no-init-sideeffects
4. Navigation & Coordination (HIGH)
4. 导航与协调(高)
- - Every feature has a coordinator owning NavigationStack
nav-coordinator-pattern - - Define all routes as a Hashable enum
nav-routes-enum - - Coordinators must support URL-based deep linking
nav-deeplink-support - - Present modals via coordinator, not inline
nav-modal-sheets - - Never use NavigationLink(destination:) — use navigationDestination(for:)
nav-no-navigationlink
- - 每个功能模块都有一个管理NavigationStack的协调器
nav-coordinator-pattern - - 将所有路由定义为遵循Hashable协议的枚举
nav-routes-enum - - 协调器必须支持基于URL的深度链接
nav-deeplink-support - - 通过协调器展示模态视图,而非内联展示
nav-modal-sheets - - 严禁使用NavigationLink(destination:) — 改用navigationDestination(for:)
nav-no-navigationlink
5. Layer Architecture (HIGH)
5. 分层架构(高)
- - Domain layer has zero framework imports
layer-dependency-rule - - Every use case is a protocol with a single execute method
layer-usecase-protocol - - Repository protocols in Domain, implementations in Data
layer-repository-protocol - - Domain models are structs, never classes
layer-model-value-types - - Views never access repositories directly
layer-no-view-repository - - ViewModels expose display-ready state only
layer-viewmodel-boundary
- - 领域层不导入任何框架
layer-dependency-rule - - 每个用例都是一个包含单一execute方法的协议
layer-usecase-protocol - - 仓库协议定义在领域层,实现类放在数据层
layer-repository-protocol - - 领域模型为结构体,严禁使用类
layer-model-value-types - - 视图严禁直接访问仓库
layer-no-view-repository - - ViewModel仅对外暴露可直接用于展示的状态
layer-viewmodel-boundary
6. Dependency Injection (MEDIUM-HIGH)
6. 依赖注入(中高)
- - Inject via @Environment with custom EnvironmentKey
di-environment-injection - - All injected dependencies are protocol types
di-protocol-abstraction - - Compose dependency container at app root
di-container-composition - - Every protocol dependency has a mock for testing
di-mock-testing
- - 使用带自定义EnvironmentKey的@Environment进行注入
di-environment-injection - - 所有注入的依赖均为协议类型
di-protocol-abstraction - - 在应用根节点组合依赖容器
di-container-composition - - 每个协议依赖都有对应的测试用Mock实现
di-mock-testing
7. List & Collection Performance (MEDIUM)
7. 列表与集合性能(中)
- - ForEach must produce constant view count per element
list-constant-viewcount - - Filter/sort in ViewModel, never inside ForEach
list-filter-in-model - - Use LazyVStack/LazyHStack for unbounded content
list-lazy-stacks - - Provide explicit id keyPath — never rely on implicit identity
list-id-keypath
- - ForEach为每个元素生成的视图数量必须固定
list-constant-viewcount - - 在ViewModel中进行过滤/排序,严禁在ForEach内部处理
list-filter-in-model - - 对无界内容使用LazyVStack/LazyHStack
list-lazy-stacks - - 提供显式的id键路径 — 严禁依赖隐式标识
list-id-keypath
8. Async & Data Flow (MEDIUM)
8. 异步与数据流(中)
- - Use .task {} for async data loading
data-task-modifier - - Never perform async work in init
data-async-init - - Model loading states as enum, not booleans
data-error-loadable - - Prefer async/await over Combine for new code
data-combine-avoid - - Use .task automatic cancellation — never manage Tasks manually
data-cancellation
- - 使用.task {}进行异步数据加载
data-task-modifier - - 严禁在init方法中执行异步操作
data-async-init - - 使用枚举对加载状态(含错误)进行建模,严禁使用布尔值
data-error-loadable - - 新代码优先使用async/await,而非Combine
data-combine-avoid - - 使用.task的自动取消机制 — 严禁手动管理Task
data-cancellation
How to Use
使用方法
Read individual reference files for detailed explanations and code examples:
- Section definitions - Category structure and impact levels
- Rule template - Template for adding new rules
阅读单个参考文件获取详细说明和代码示例:
- 章节定义 - 类别结构和影响级别说明
- 规则模板 - 添加新规则的模板
Reference Files
参考文件
| File | Description |
|---|---|
| references/_sections.md | Category definitions and ordering |
| assets/templates/_template.md | Template for new rules |
| metadata.json | Version and reference information |
| 文件 | 描述 |
|---|---|
| references/_sections.md | 类别定义与排序说明 |
| assets/templates/_template.md | 新规则模板 |
| metadata.json | 版本与参考信息 |