Refactor SwiftUI view files for consistent structure, dependency injection, and Observation usage. Use when asked to clean up a SwiftUI view's layout, handle view models safely, or standardize how dependencies are initialized and passed.
From ios-swift-skillsnpx claudepluginhub patrickserrano/skillsThis skill uses the workspace's default tool permissions.
Apply consistent structure and dependency patterns to SwiftUI views, focusing on ordering, MV patterns, careful view model handling, and correct Observation usage.
private/public let constants@State / other stored propertiesvar (non-view)initbody@State, @Environment, @Query, task, and onChange for orchestration@EnvironmentIf body grows beyond a screen or has multiple logical sections, split it:
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HeaderSection(title: title, isPinned: isPinned)
DetailsSection(details: details)
ActionsSection(onSave: onSave, onCancel: onCancel)
}
}
Or use computed view properties in the same file:
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 }
}
}
}
}
init, then into the view model@State private var viewModel: SomeViewModel
init(dependency: Dependency) {
_viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
@Observable reference types, store them as @State in the root view@State, @Environment, task, onChange@State initialized in init@State for root @Observable, no redundant wrappersWhen a SwiftUI view file exceeds ~300 lines:
private extensions// MARK: - comments (e.g., // MARK: - Actions, // MARK: - Subviews)struct focused on stored properties, init, and bodystruct LargeView: View {
@Environment(Store.self) private var store
@State private var items: [Item] = []
var body: some View {
List {
content
}
.task { await loadItems() }
}
}
// MARK: - Subviews
private extension LargeView {
var content: some View {
ForEach(items) { item in
ItemRow(item: item)
}
}
}
// MARK: - Actions
private extension LargeView {
func loadItems() async {
items = await store.fetchItems()
}
}
@Observable types stored as @State in root view@Environment