swiftui-view-refactor
重构并审查 SwiftUI 视图文件以实现一致的结构、依赖项注入和观察使用。当被要求清理 SwiftUI 视图的布局/排序、安全地处理视图模型(如果可能时为非可选)或标准化依赖项和 @Observable 状态的初始化和传递方式时使用。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install totalclaw:totalclaw~steipete-swiftui-view-refactorcURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~steipete-swiftui-view-refactor/file -o steipete-swiftui-view-refactor.md# SwiftUI View Refactor
_Attribution: copied from @Dimillian’s `Dimillian/Skills` (2025-12-31)._
## Overview
Apply a consistent structure and dependency pattern to SwiftUI views, with a focus on ordering, Model-View (MV) patterns, careful view model handling, and correct Observation usage.
## Core Guidelines
### 1) View ordering (top → bottom)
- Environment
- `private`/`public` `let`
- `@State` / other stored properties
- computed `var` (non-view)
- `init`
- `body`
- computed view builders / other view helpers
- helper / async functions
### 2) Prefer MV (Model-View) patterns
- Default to MV: Views are lightweight state expressions; models/services own business logic.
- Favor `@State`, `@Environment`, `@Query`, and `task`/`onChange` for orchestration.
- Inject services and shared models via `@Environment`; keep views small and composable.
- Split large views into subviews rather than introducing a view model.
### 3) Split large bodies and view properties
- If `body` grows beyond a screen or has multiple logical sections, split it into smaller subviews.
- Extract large computed view properties (`var header: some View { ... }`) into dedicated `View` types when they carry state or complex branching.
- It's fine to keep related subviews as computed view properties in the same file; extract to a standalone `View` struct only when it structurally makes sense or when reuse is intended.
- Prefer passing small inputs (data, bindings, callbacks) over reusing the entire parent view state.
Example (extracting a section):
```swift
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HeaderSection(title: title, isPinned: isPinned)
DetailsSection(details: details)
ActionsSection(onSave: onSave, onCancel: onCancel)
}
}
```
Example (long body → shorter body + computed views in the same file):
```swift
var body: some View {
List {
header
filters
results
footer
}
}
private var header: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.title2)
Text(subtitle).font(.subheadline)
}
}
private var filters: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(filterOptions, id: \.self) { option in
FilterChip(option: option, isSelected: option == selectedFilter)
.onTapGesture { selectedFilter = option }
}
}
}
}
```
Example (extracting a complex computed view):
```swift
private var header: some View {
HeaderSection(title: title, subtitle: subtitle, status: status)
}
private struct HeaderSection: View {
let title: String
let subtitle: String?
let status: Status
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(title).font(.headline)
if let subtitle { Text(subtitle).font(.subheadline) }
StatusBadge(status: status)
}
}
}
```
### 4) View model handling (only if already present)
- Do not introduce a view model unless the request or existing code clearly calls for one.
- If a view model exists, make it non-optional when possible.
- Pass dependencies to the view via `init`, then pass them into the view model in the view's `init`.
- Avoid `bootstrapIfNeeded` patterns.
Example (Observation-based):
```swift
@State private var viewModel: SomeViewModel
init(dependency: Dependency) {
_viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
```
### 5) Observation usage
- For `@Observable` reference types, store them as `@State` in the root view.
- Pass observables down explicitly as needed; avoid optional state unless required.
## Workflow
1) Reorder the view to match the ordering rules.
2) Favor MV: move lightweight orchestration into the view using `@State`, `@Environment`, `@Query`, `task`, and `onChange`.
3) If a view model exists, replace optional view models with a non-optional `@State` view model initialized in `init` by passing dependencies from the view.
4) Confirm Observation usage: `@State` for root `@Observable` view models, no redundant wrappers.
5) Keep behavior intact: do not change layout or business logic unless requested.
## Notes
- Prefer small, explicit helpers over large conditional blocks.
- Keep computed view builders below `body` and non-view computed vars above `init`.
- For MV-first guidance and rationale, see `references/mv-patterns.md`.
---
## 中文说明
# SwiftUI View Refactor
_出处:复制自 @Dimillian 的 `Dimillian/Skills`(2025-12-31)。_
## 概述
为 SwiftUI 视图应用一致的结构和依赖模式,重点关注排序、Model-View(MV)模式、谨慎的视图模型处理以及正确的 Observation 用法。
## 核心准则
### 1) 视图排序(从上到下)
- Environment
- `private`/`public` 的 `let`
- `@State` / 其他存储属性
- 计算 `var`(非视图)
- `init`
- `body`
- 计算视图构建器 / 其他视图辅助
- 辅助 / 异步函数
### 2) 优先采用 MV(Model-View)模式
- 默认采用 MV:视图是轻量级的状态表达;模型/服务持有业务逻辑。
- 优先使用 `@State`、`@Environment`、`@Query` 以及 `task`/`onChange` 进行编排。
- 通过 `@Environment` 注入服务和共享模型;保持视图小巧且可组合。
- 将大型视图拆分为子视图,而不是引入视图模型。
### 3) 拆分大型 body 和视图属性
- 如果 `body` 超过一屏,或包含多个逻辑区块,将其拆分为更小的子视图。
- 当大型计算视图属性(`var header: some View { ... }`)携带状态或包含复杂分支时,将其提取为专门的 `View` 类型。
- 将相关的子视图作为计算视图属性保留在同一文件中也是可以的;仅在结构上有意义或意图复用时,才提取为独立的 `View` 结构体。
- 优先传递小的输入(数据、绑定、回调),而非复用整个父视图状态。
示例(提取一个区块):
```swift
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HeaderSection(title: title, isPinned: isPinned)
DetailsSection(details: details)
ActionsSection(onSave: onSave, onCancel: onCancel)
}
}
```
示例(长 body → 更短的 body + 同一文件中的计算视图):
```swift
var body: some View {
List {
header
filters
results
footer
}
}
private var header: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.title2)
Text(subtitle).font(.subheadline)
}
}
private var filters: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(filterOptions, id: \.self) { option in
FilterChip(option: option, isSelected: option == selectedFilter)
.onTapGesture { selectedFilter = option }
}
}
}
}
```
示例(提取一个复杂的计算视图):
```swift
private var header: some View {
HeaderSection(title: title, subtitle: subtitle, status: status)
}
private struct HeaderSection: View {
let title: String
let subtitle: String?
let status: Status
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(title).font(.headline)
if let subtitle { Text(subtitle).font(.subheadline) }
StatusBadge(status: status)
}
}
}
```
### 4) 视图模型处理(仅在已存在时)
- 除非请求或现有代码明确需要,否则不要引入视图模型。
- 如果视图模型已存在,尽可能将其设为非可选。
- 通过 `init` 将依赖项传递给视图,然后在视图的 `init` 中将它们传入视图模型。
- 避免 `bootstrapIfNeeded` 模式。
示例(基于 Observation):
```swift
@State private var viewModel: SomeViewModel
init(dependency: Dependency) {
_viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
```
### 5) Observation 用法
- 对于 `@Observable` 引用类型,在根视图中将其存储为 `@State`。
- 按需显式地向下传递可观察对象;除非必要,否则避免可选状态。
## 工作流
1) 重新排序视图以匹配排序规则。
2) 优先采用 MV:使用 `@State`、`@Environment`、`@Query`、`task` 和 `onChange` 将轻量级编排移入视图中。
3) 如果视图模型已存在,将可选视图模型替换为在 `init` 中通过从视图传入依赖项初始化的非可选 `@State` 视图模型。
4) 确认 Observation 用法:根级 `@Observable` 视图模型使用 `@State`,不要冗余包装。
5) 保持行为不变:除非有要求,否则不要改动布局或业务逻辑。
## 注意事项
- 优先使用小巧、明确的辅助方法,而非大型条件块。
- 将计算视图构建器保持在 `body` 之下,将非视图计算变量保持在 `init` 之上。
- 关于 MV 优先的指导和理由,参见 `references/mv-patterns.md`。