mcpField
S
swiftui-view-refactor

swiftui-view-refactor

github.com/steipete开发7.5k157 次安装1 周前更新
代码仓库

关于 swiftui-view-refactor

面向重构 SwiftUI 视图的开发者技能,提供结构、Model-View 模式和 Observation 用法指导。重点是保持视图轻量且可组合,将业务逻辑放在模型或服务中,并保留现有行为。

主要功能

  1. 01统一视图属性的排列顺序
  2. 02采用轻量的 Model-View 结构
  3. 03将复杂 body 拆分为子视图
  4. 04一致地处理已有视图模型
  5. 05通过 @State 使用 Observation

适用场景

  1. 01重构时调整 SwiftUI 视图顺序
  2. 02将较大的 body 拆分为多个部分
  3. 03重构已有视图模型的视图

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/steipete/agent-scripts --skill swiftui-view-refactor

安装后在 Claude Code 中输入/swiftui-view-refactor确认已加载。

附带文件references/mv-patterns.md

详细介绍

SwiftUI 视图重构

致谢:摘自 @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 超出一屏或包含多个逻辑区块,请将其拆分成较小的子视图。
  • 如果较大的计算视图属性(var header: some View { ... })包含状态或复杂分支,请将其提取为专用的 View 类型。
  • 可以将相关子视图作为计算视图属性保留在同一文件中;只有在结构上合理或计划复用时,才提取为独立的 View 结构体。
  • 优先传递少量输入(数据、绑定、回调),而不是复用整个父视图状态。

示例(提取一个区块):

var body: some View {
    VStack(alignment: .leading, spacing: 16) {
        HeaderSection(title: title, isPinned: isPinned)
        DetailsSection(details: details)
        ActionsSection(onSave: onSave, onCancel: onCancel)
    }
}

示例(较长的主体 → 更短的主体 + 同一文件中的计算视图):

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 }
            }
        }
    }
}

示例(提取复杂的计算视图):

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):

@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。

评论

支持 Markdown