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
- INLINECODE1 /
public INLINECODE3 - INLINECODE4 / other stored properties
- computed
var (non-view) - INLINECODE6
- INLINECODE7
- 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):
CODEBLOCK0
Example (long body → shorter body + computed views in the same file):
CODEBLOCK1
Example (extracting a complex computed view):
CODEBLOCK2
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):
CODEBLOCK3
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 视图重构
来源:复制自 @Dimillian 的 Dimillian/Skills(2025-12-31)。
概述
为 SwiftUI 视图应用一致的结构和依赖模式,重点关注顺序、模型-视图(MV)模式、谨慎的视图模型处理以及正确的 Observation 使用方式。
核心指南
1) 视图顺序(从上到下)
- - 环境变量
- private/public let 常量
- @State / 其他存储属性
- 计算属性 var(非视图相关)
- init 初始化方法
- body 属性
- 计算视图构建器 / 其他视图辅助方法
- 辅助方法 / 异步函数
2) 优先使用 MV(模型-视图)模式
- - 默认采用 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) 如果存在视图模型,用非可选的 @State 视图模型替换可选视图模型,并在 init 中通过从视图传递依赖项进行初始化。
4) 确认 Observation 使用方式:根 @Observable 视图模型使用 @State,无冗余包装器。
5) 保持行为不变:除非要求,否则不更改布局或业务逻辑。
备注
- - 优先使用小型、明确的辅助方法,而不是大型条件块。
- 将计算视图构建器放在 body 下方,非视图计算变量放在 init 上方。
- 有关 MV 优先的指导和原理,请参阅 references/mv-patterns.md。