swiftui-patterns by johnrogers/claude-swift-engineering
npx skills add https://github.com/johnrogers/claude-swift-engineering --skill swiftui-patternsSwiftUI 17+ 通过 @Observable 移除了 ObservableObject 的样板代码,通过 @Environment 简化了环境注入,并引入了基于任务的异步模式。核心原则是:使用苹果的现代 API 而非响应式库。
| 需求 | 使用 (iOS 17+) | 不要使用 |
|---|---|---|
| 可观察模型 | @Observable | ObservableObject |
| 已发布属性 | 常规属性 | @Published |
| 自有状态 | @State | @StateObject |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 传递的模型 (绑定) | @Bindable | @ObservedObject |
| 环境注入 | environment(_:) | environmentObject(_:) |
| 环境访问 | @Environment(Type.self) | @EnvironmentObject |
| 出现时异步 | .task { } | .onAppear { Task {} } |
| 值变更 | onChange(of:initial:_:) | onChange(of:perform:) |
@Observable (无需 @Published)@State,对传递的模型使用 @Bindable.task { } (在视图消失时自动取消)NavigationPath 的 NavigationStack 进行编程式导航.accessibilityLabel() 和 .accessibilityHint()只要内容有被需要的可能,无论多小,都应始终加载参考文件。 拥有上下文总比错过一个模式或犯错误要好。
| 参考 | 何时加载 |
|---|---|
| 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 时 |
过度使用 @Bindable 处理传递的模型 — 为每个属性创建 @Bindable 会导致不必要的视图重载。仅对需要双向绑定的可变模型属性使用 @Bindable。只读的计算属性应使用常规属性。
状态放置错误 — 将模型状态放在视图中而非专用的 @Observable 模型中,会导致视图逻辑变得混乱。始终分离模型和视图的关注点。
NavigationPath 状态损坏 — 错误地修改 NavigationPath 可能导致其处于不一致状态。使用 navigationDestination(for:destination:) 配合适当的状态管理,以避免路径损坏。
缺少 .task 取消 — .task 会在视图消失时自动处理取消,但嵌套的 Task 不会。复杂的异步流程需要显式的取消跟踪,以避免僵尸任务。
忽略环境失效 — 在父级更改环境值不会自动使子视图失效。应一致地使用 @Environment,并理解基于观察的重渲染何时发生。
UIKit 互操作内存泄漏 — 如果委托循环未被打破,UIViewRepresentable 和 UIViewControllerRepresentable 可能会泄漏。需要使用弱引用和显式清理。
每周安装量
117
仓库
GitHub 星标数
176
首次出现
2026年1月23日
安全审计
安装于
opencode99
codex98
gemini-cli92
claude-code92
cursor91
github-copilot78
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.
| Need | Use (iOS 17+) | NOT |
|---|---|---|
| Observable model | @Observable | ObservableObject |
| Published property | Regular 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:) |
@Observable for model classes (no @Published needed)@State for view-owned models, @Bindable for passed models.task { } for async work (auto-cancels on disappear)NavigationStack with NavigationPath for programmatic navigation.accessibilityLabel() and .accessibilityHint() to interactive elementsALWAYS 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.
| Reference | Load When |
|---|---|
| Observable | Creating new @Observable model classes |
| State Management | Deciding between @State, @Bindable, @Environment |
| Environment | Injecting dependencies into view hierarchy |
| View Modifiers |
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.
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.
NavigationPath state corruption — Mutating NavigationPath incorrectly can leave it in inconsistent state. Use navigationDestination(for:destination:) with proper state management to avoid path corruption.
Missing.task cancellation — handles cancellation on disappear automatically, but nested Tasks don't. Complex async flows need explicit cancellation tracking to avoid zombie tasks.
Weekly Installs
117
Repository
GitHub Stars
176
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode99
codex98
gemini-cli92
claude-code92
cursor91
github-copilot78
MongoDB开发专家 | Payload CMS与Node.js全栈TypeScript开发,支持React Native/Next.js
226 周安装
移动安全编码专家指南:iOS/Android安全开发、WebView防护与漏洞修复
233 周安装
Android 模拟器技能:基于无障碍服务的自动化测试与构建工具集
232 周安装
Helm Chart 脚手架指南:从零创建、打包和管理Kubernetes应用部署
95 周安装
深度研究工具deep-research:AI驱动的高质量研究报告生成与自动化写作
238 周安装
自动化文档生成技能:AI驱动,从代码自动生成API文档、架构图和用户指南
237 周安装
Using onChange, task, or iOS 17+ modifiers |
| Migration Guide | Updating iOS 16 code to iOS 17+ |
| MVVM Observable | Setting up view model architecture |
| Navigation | Programmatic or deep-link navigation |
| Performance | Lists with 100+ items or excessive re-renders |
| UIKit Interop | Wrapping UIKit components (WKWebView, PHPicker) |
| Accessibility | VoiceOver, Dynamic Type, accessibility actions |
| Async Patterns | Loading states, refresh, background tasks |
| Composition | Reusable view modifiers or complex conditional UI |
.taskIgnoring 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.
UIKit interop memory leaks — UIViewRepresentable and UIViewControllerRepresentable can leak if delegate cycles aren't broken. Weak references and explicit cleanup are required.