Source profileQuality 79/100

affaan-m/ECC/skills/swiftui-patterns/SKILL.md

swiftui-patterns

SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS/macOS UI best practices.

Source repository stars
234,327
Declared platforms
0
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Modern SwiftUI patterns for building declarative, performant user interfaces on Apple platforms. Covers the Observation framework, view composition, type-safe navigation, and performance optimization.

Best for

    Not for

    • Using ObservableObject / @Published / @StateObject / @EnvironmentObject in new code — migrate to @Observable
    • Putting async work directly in body or init — use .task {} or explicit load methods

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/swiftui-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "swiftui-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/swiftui-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      When to Activate

      Building SwiftUI views and managing state (@State, @Observable, @Binding)

      Building SwiftUI views and managing state (@State, @Observable, @Binding)Designing navigation flows with NavigationStackStructuring view models and data flow
    2. 02

      State Management

      Choose the simplest wrapper that fits:

      Choose the simplest wrapper that fits:Use @Observable (not ObservableObject) — it tracks property-level changes so SwiftUI only re-renders views that read the changed property:Replace @EnvironmentObject with @Environment:
    3. 03

      Property Wrapper Selection

      Choose the simplest wrapper that fits:

      Choose the simplest wrapper that fits:
    4. 04

      @Observable ViewModel

      Use @Observable (not ObservableObject) — it tracks property-level changes so SwiftUI only re-renders views that read the changed property:

      Use @Observable (not ObservableObject) — it tracks property-level changes so SwiftUI only re-renders views that read the changed property:
    5. 05

      View Consuming the ViewModel

      Review the “View Consuming the ViewModel” section in the pinned source before continuing.

      Review and apply the “View Consuming the ViewModel” source section.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score79/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/swiftui-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    SwiftUI Patterns

    Modern SwiftUI patterns for building declarative, performant user interfaces on Apple platforms. Covers the Observation framework, view composition, type-safe navigation, and performance optimization.

    When to Activate

    • Building SwiftUI views and managing state (@State, @Observable, @Binding)
    • Designing navigation flows with NavigationStack
    • Structuring view models and data flow
    • Optimizing rendering performance for lists and complex layouts
    • Working with environment values and dependency injection in SwiftUI

    State Management

    Property Wrapper Selection

    Choose the simplest wrapper that fits:

    WrapperUse Case
    @StateView-local value types (toggles, form fields, sheet presentation)
    @BindingTwo-way reference to parent's @State
    @Observable class + @StateOwned model with multiple properties
    @Observable class (no wrapper)Read-only reference passed from parent
    @BindableTwo-way binding to an @Observable property
    @EnvironmentShared dependencies injected via .environment()

    @Observable ViewModel

    Use @Observable (not ObservableObject) — it tracks property-level changes so SwiftUI only re-renders views that read the changed property:

    @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()) ?? []
        }
    }
    

    View Consuming the 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 Injection

    Replace @EnvironmentObject with @Environment:

    // Inject
    ContentView()
        .environment(authManager)
    
    // Consume
    struct ProfileView: View {
        @Environment(AuthManager.self) private var auth
    
        var body: some View {
            Text(auth.currentUser?.name ?? "Guest")
        }
    }
    

    View Composition

    Extract Subviews to Limit Invalidation

    Break views into small, focused structs. When state changes, only the subview reading that state re-renders:

    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 for Reusable Styling

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

    Navigation

    Type-Safe NavigationStack

    Use NavigationStack with NavigationPath for programmatic, type-safe routing:

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

    Performance

    Use Lazy Containers for Large Collections

    LazyVStack and LazyHStack create views only when visible:

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

    Stable Identifiers

    Always use stable, unique IDs in ForEach — avoid using array indices:

    // Use Identifiable conformance or explicit id
    ForEach(items, id: \.stableID) { item in
        ItemRow(item: item)
    }
    

    Avoid Expensive Work in body

    • Never perform I/O, network calls, or heavy computation inside body
    • Use .task {} for async work — it cancels automatically when the view disappears
    • Use .sensoryFeedback() and .geometryGroup() sparingly in scroll views
    • Minimize .shadow(), .blur(), and .mask() in lists — they trigger offscreen rendering

    Equatable Conformance

    For views with expensive bodies, conform to Equatable to skip unnecessary re-renders:

    struct ExpensiveChartView: View, Equatable {
        let dataPoints: [DataPoint] // DataPoint must conform to Equatable
    
        static func == (lhs: Self, rhs: Self) -> Bool {
            lhs.dataPoints == rhs.dataPoints
        }
    
        var body: some View {
            // Complex chart rendering
        }
    }
    

    Previews

    Use #Preview macro with inline mock data for fast iteration:

    #Preview("Empty state") {
        ItemListView(viewModel: ItemListViewModel(repository: EmptyMockRepository()))
    }
    
    #Preview("Loaded") {
        ItemListView(viewModel: ItemListViewModel(repository: PopulatedMockRepository()))
    }
    

    Anti-Patterns to Avoid

    • Using ObservableObject / @Published / @StateObject / @EnvironmentObject in new code — migrate to @Observable
    • Putting async work directly in body or init — use .task {} or explicit load methods
    • Creating view models as @State inside child views that don't own the data — pass from parent instead
    • Using AnyView type erasure — prefer @ViewBuilder or Group for conditional views
    • Ignoring Sendable requirements when passing data to/from actors

    References

    See skill: swift-actor-persistence for actor-based persistence patterns. See skill: swift-protocol-di-testing for protocol-based DI and testing with Swift Testing.

    Alternatives

    Compare before choosing