swiftui-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SwiftUI Patterns (iOS 17+)

SwiftUI Patterns (iOS 17+)

SwiftUI 17+ removes ObservableObject boilerplate with @Observable, simplifies environment injection with @Environment, and introduces task-based async patterns. The core principle: use Apple's modern APIs instead of reactive libraries.
SwiftUI 17+ 借助@Observable移除了ObservableObject的冗余代码,通过@Environment简化了环境注入,并引入了基于任务的异步模式。核心原则:使用Apple的现代API而非响应式库。

Overview

概述

Quick Reference

快速参考

NeedUse (iOS 17+)NOT
Observable model
@Observable
ObservableObject
Published propertyRegular property
@Published
Own state
@State
@StateObject
Passed model (binding)
@Bindable
@ObservedObject
Environment injection
environment(_:)
environmentObject(_:)
Environment access
@Environment(Type.self)
@EnvironmentObject
Async on appear
.task { }
.onAppear { Task {} }
Value change
onChange(of:initial:_:)
onChange(of:perform:)
需求iOS 17+ 推荐用法不推荐用法
可观察模型
@Observable
ObservableObject
可发布属性常规属性
@Published
自有状态
@State
@StateObject
传递的模型(绑定)
@Bindable
@ObservedObject
环境注入
environment(_:)
environmentObject(_:)
环境访问
@Environment(Type.self)
@EnvironmentObject
出现时执行异步操作
.task { }
.onAppear { Task {} }
值变更
onChange(of:initial:_:)
onChange(of:perform:)

Core Workflow

核心工作流程

  1. Use
    @Observable
    for model classes (no @Published needed)
  2. Use
    @State
    for view-owned models,
    @Bindable
    for passed models
  3. Use
    .task { }
    for async work (auto-cancels on disappear)
  4. Use
    NavigationStack
    with
    NavigationPath
    for programmatic navigation
  5. Apply
    .accessibilityLabel()
    and
    .accessibilityHint()
    to interactive elements
  1. 为模型类使用
    @Observable
    (无需@Published)
  2. 视图自有模型使用
    @State
    ,传递的模型使用
    @Bindable
  3. 异步工作使用
    .task { }
    (在视图消失时自动取消)
  4. 结合
    NavigationStack
    NavigationPath
    实现程序化导航
  5. 为交互元素添加
    .accessibilityLabel()
    .accessibilityHint()

Reference Loading Guide

参考资料加载指南

ALWAYS load reference files if there is even a small chance the content may be required. It's better to have the context than to miss a pattern or make a mistake.
ReferenceLoad When
ObservableCreating new
@Observable
model classes
State ManagementDeciding between
@State
,
@Bindable
,
@Environment
EnvironmentInjecting dependencies into view hierarchy
View ModifiersUsing
onChange
,
task
, or iOS 17+ modifiers
Migration GuideUpdating iOS 16 code to iOS 17+
MVVM ObservableSetting up view model architecture
NavigationProgrammatic or deep-link navigation
PerformanceLists with 100+ items or excessive re-renders
UIKit InteropWrapping UIKit components (WKWebView, PHPicker)
AccessibilityVoiceOver, Dynamic Type, accessibility actions
Async PatternsLoading states, refresh, background tasks
CompositionReusable view modifiers or complex conditional UI
只要有哪怕一点点可能需要用到相关内容,就务必加载参考文件。拥有上下文总比遗漏模式或犯错要好。
参考资料加载场景
Observable创建新的
@Observable
模型类时
State Management决定使用
@State
@Bindable
还是
@Environment
Environment向视图层级注入依赖时
View Modifiers使用
onChange
task
或iOS 17+修饰符时
Migration Guide将iOS 16代码升级到iOS 17+时
MVVM Observable搭建视图模型架构时
Navigation实现程序化或深度链接导航时
Performance处理包含100+条目的列表或过度重渲染问题时
UIKit Interop封装UIKit组件(WKWebView、PHPicker)时
Accessibility处理VoiceOver、动态类型、无障碍操作时
Async Patterns实现加载状态、刷新、后台任务时
Composition实现可复用视图修饰符或复杂条件UI时

Common Mistakes

常见错误

  1. Over-using
    @Bindable
    for passed models
    — Creating
    @Bindable
    for every property causes unnecessary view reloads. Use
    @Bindable
    only for mutable model properties that need two-way binding. Read-only computed properties should use regular properties.
  2. State placement errors — Putting model state in the view instead of a dedicated
    @Observable
    model causes view logic to become tangled. Always separate model and view concerns.
  3. NavigationPath state corruption — Mutating
    NavigationPath
    incorrectly can leave it in inconsistent state. Use
    navigationDestination(for:destination:)
    with proper state management to avoid path corruption.
  4. Missing
    .task
    cancellation
    .task
    handles cancellation on disappear automatically, but nested Tasks don't. Complex async flows need explicit cancellation tracking to avoid zombie tasks.
  5. Ignoring environment invalidation — Changing environment values at parent doesn't invalidate child views automatically. Use
    @Environment
    consistently and understand when re-renders happen based on observation.
  6. UIKit interop memory leaks
    UIViewRepresentable
    and
    UIViewControllerRepresentable
    can leak if delegate cycles aren't broken. Weak references and explicit cleanup are required.
  1. 过度为传递的模型使用
    @Bindable
    —— 为每个属性创建
    @Bindable
    会导致不必要的视图重载。仅对需要双向绑定的可变模型属性使用
    @Bindable
    。只读计算属性应使用常规属性。
  2. 状态放置错误 —— 将模型状态放在视图中而非专用的
    @Observable
    模型中会导致视图逻辑混乱。务必分离模型和视图关注点。
  3. NavigationPath状态损坏 —— 不正确地修改
    NavigationPath
    会使其处于不一致状态。使用
    navigationDestination(for:destination:)
    并配合正确的状态管理,以避免路径损坏。
  4. 遗漏
    .task
    取消处理
    ——
    .task
    会在视图消失时自动处理取消,但嵌套的Task不会。复杂的异步流程需要显式的取消跟踪,以避免僵尸任务。
  5. 忽略环境失效 —— 在父视图中更改环境值不会自动使子视图失效。持续使用
    @Environment
    ,并理解基于观察的重渲染触发时机。
  6. UIKit互操作内存泄漏 —— 如果不打破委托循环,
    UIViewRepresentable
    UIViewControllerRepresentable
    可能会发生内存泄漏。需要使用弱引用和显式清理。