Communitygithub.com

SwiftUI 設計模式

SwiftUI 架構模式、使用 @Observable 的狀態管理、視圖組合、導航、效能最佳化以及現代 iOS/macOS UI 最佳實踐。適用於建構或審查 SwiftUI 視圖、@Observable 狀態、導航或渲染效能時。

SwiftUI 設計模式 是什麼?

此技能提供一套完整的 SwiftUI 現代設計模式指南,專注於 Apple 平台上的宣告式高效能 UI 開發。它涵蓋了使用新的 Observation 框架進行狀態管理,並明確建議從傳統的 ObservableObject 遷移至 @Observable,以實現更精細的視圖重新渲染控制。技能詳細說明了如何正確選用 @State、@Binding、@Bindable 與 @Environment 等屬性包裝器,並透過實際程式碼範例展示 ViewModel 的設計、視圖組合與 ViewModifier 應用。此外,它還深入探討了使用 NavigationStack 的型別安全導航、惰性容器 (LazyVStack/LazyHStack) 與穩定識別碼的效能最佳化技巧,以及如何透過 Equatable 一致性避免不必要的重新渲染。此技能也強調應避免的常見反模式,例如使用 AnyView 或在工作佇列中忽略 Sendable 要求,適合任何希望在 SwiftUI 專案中實踐架構化、可測試且高效能開發的開發者參考。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/swiftui-patterns

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

SwiftUI 設計模式 是做什麼的?

現代 SwiftUI 設計模式,用於在 Apple 平台上建構宣告式、高效能的使用者介面。涵蓋 Observation 框架、視圖組合、型別安全導航以及效能最佳化。

啟動時機

  • 建構 SwiftUI 視圖與管理狀態(@State、@Observable、@Binding)
  • 使用 NavigationStack 設計導航流程
  • 結構化視圖模型與資料流
  • 針對列表與複雜版面最佳化渲染效能
  • 在 SwiftUI 中處理環境值與依賴注入

狀態管理

屬性包裝器選擇

選擇最簡單且適用的包裝器:

包裝器使用情境
@State視圖獨有的值型別(開關、表單欄位、Sheet 呈現)
@Binding對父層 @State 的雙向參考
@Observable class + @State擁有多個屬性的資料模型
@Observable class(無包裝器)從父層傳入的唯讀參考
@Bindable對 @Observable 屬性的雙向綁定
@Environment透過 .environment() 注入的共享依賴

@Observable ViewModel

使用 @Observable(而非 ObservableObject)— 它會追蹤屬性層級的變更,因此 SwiftUI 僅重新渲染讀取到變更屬性的視圖:

@Observable
final class ItemListViewModel {
    private(set) var items: [Item] = []
    private(set) var isLoading = false
    var searchText = ""

    private let repository: any ItemRepository

    init(repository: any ItemRepository = DefaultItemRepository()) {
        self.repository = repository
    }

    func load() async {
        isLoading = true
        defer { isLoading = false }
        items = (try? await repository.fetchAll()) ?? []
    }
}

使用 ViewModel 的視圖

struct ItemListView: View {
    @State private var viewModel: ItemListViewModel

    init(viewModel: ItemListViewModel = ItemListViewModel()) {
        _viewModel = State(initialValue: viewModel)
    }

    var body: some View {
        List(viewModel.items) { item in
            ItemRow(item: item)
        }
        .searchable(text: $viewModel.searchText)
        .overlay { if viewModel.isLoading { ProgressView() } }
        .task { await viewModel.load() }
    }
}

環境注入

以 @Environment 取代 @EnvironmentObject:

// 注入
ContentView()
    .environment(authManager)

// 消費
struct ProfileView: View {
    @Environment(AuthManager.self) private var auth

    var body: some View {
        Text(auth.currentUser?.name ?? "訪客")
    }
}

視圖組合

提取子視圖以限制失效範圍

將視圖拆分為小型、專注的結構。當狀態變更時,僅有讀取該狀態的子視圖會重新渲染:

struct OrderView: View {
    @State private var viewModel = OrderViewModel()

    var body: some View {
        VStack {
            OrderHeader(title: viewModel.title)
            OrderItemList(items: viewModel.items)
            OrderTotal(total: viewModel.total)
        }
    }
}

ViewModifier 實現可重用樣式

struct CardModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(.regularMaterial)
            .clipShape(RoundedRectangle(cornerRadius: 12))
    }
}

extension View {
    func cardStyle() -> some View {
        modifier(CardModifier())
    }
}

導航

型別安全的 NavigationStack

使用 NavigationStack 搭配 NavigationPath 進行程式化、型別安全的路由:

@Observable
final class Router {
    var path = NavigationPath()

    func navigate(to destination: Destination) {
        path.append(destination)
    }

    func popToRoot() {
        path = NavigationPath()
    }
}

enum Destination: Hashable {
    case detail(Item.ID)
    case settings
    case profile(User.ID)
}

struct RootView: View {
    @State private var router = Router()

    var body: some View {
        NavigationStack(path: $router.path) {
            HomeView()
                .navigationDestination(for: Destination.self) { dest in
                    switch dest {
                    case .detail(let id): ItemDetailView(itemID: id)
                    case .settings: SettingsView()
                    case .profile(let id): ProfileView(userID: id)
                    }
                }
        }
        .environment(router)
    }
}

效能

針對大型集合使用惰性容器

LazyVStack 與 LazyHStack 僅在可見時才建立視圖:

ScrollView {
    LazyVStack(spacing: 8) {
        ForEach(items) { item in
            ItemRow(item: item)
        }
    }
}

穩定的識別碼

在 ForEach 中始終使用穩定、唯一的 ID — 避免使用陣列索引:

// 使用 Identifiable 協議或明確的 id
ForEach(items, id: \.stableID) { item in
    ItemRow(item: item)
}

避免在 body 中執行昂貴操作

  • 絕不在 body 內部執行 I/O、網路呼叫或大量計算
  • 使用 .task {} 進行非同步工作 — 當視圖消失時會自動取消
  • 在滾動視圖中審慎使用 .sensoryFeedback() 與 .geometryGroup()
  • 在列表中盡量減少 .shadow()、.blur() 與 .mask() — 它們會觸發離屏渲染

Equatable 協議一致性

對於 body 較為昂貴的視圖,遵循 Equatable 協議以跳過不必要的重新渲染:

struct ExpensiveChartView: View, Equatable {
    let dataPoints: [DataPoint] // DataPoint 必須遵循 Equatable

    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.dataPoints == rhs.dataPoints
    }

    var body: some View {
        // 複雜圖表渲染
    }
}

預覽

使用 #Preview 巨集搭配內聯模擬資料以實現快速迭代:

#Preview("空狀態") {
    ItemListView(viewModel: ItemListViewModel(repository: EmptyMockRepository()))
}

#Preview("已載入") {
    ItemListView(viewModel: ItemListViewModel(repository: PopulatedMockRepository()))
}

應避免的反模式

  • 在新程式碼中使用 ObservableObject / @Published / @StateObject / @EnvironmentObject — 應遷移至 @Observable
  • 將非同步工作直接放入 body 或 init 中 — 應使用 .task {} 或明確的載入方法
  • 在不擁有資料的子視圖中以 @State 建立 ViewModel — 應從父層傳入
  • 使用 AnyView 類型抹消 — 對於條件視圖應偏愛 @ViewBuilder 或 Group
  • 在與行為者傳遞資料時忽略 Sendable 要求

參考資料

參閱技能:swift-actor-persistence 以了解基於行為者的持久化模式。 參閱技能:swift-protocol-di-testing 以了解基於協議的依賴注入與 Swift Testing 測試。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相關技能