SwiftUI Expert Skill

repository·main·Indexed 25 days ago

https://github.com/avdlee/swiftui-agent-skill

Specialized guidance and tools for AI coding agents to assist with SwiftUI development. Covers state management, view composition, performance optimization, and iOS 26+ Liquid Glass adoption. Includes a Python toolchain wrapping xctrace for recording and analyzing Instruments traces to diagnose performance issues, as well as detailed references for accessibility patterns, advanced animations (Phase, Keyframe, and Transactions), and Dynamic Type support.

Tokens
63.7K
Snippets
220
Records
258
Agent score
85%

What's inside swiftui-agent-skill

  1. Understand Soft-Deprecated SwiftUI APIs

    main

    A soft-deprecated API is marked as deprecated in the SDK headers using a placeholder version (100000.0) to suppress compiler warnings. These APIs still compile and function correctly, but they signal that they should not be used in new code.

    Common examples include:

    • NavigationView (use NavigationStack or NavigationSplitView instead)
    • ActionSheet / Alert (use .confirmationDialog or .alert modifiers instead)
    • MagnificationGesture (use MagnifyGesture instead)
    • PresentationMode (use \.dismiss instead)

    Treat these APIs as informational rather than urgent errors.

  2. Choose the appropriate SwiftUI Scroll API

    main

    Select a scrolling implementation based on your target iOS version and requirements:

    • iOS 18+: Use onScrollGeometryChange(for:of:action:) to observe scroll geometry and scrollPosition(_:) with ScrollPosition for flexible scrolling (identity, offset, or edge).
    • iOS 17+: Use scrollPosition(id:) with an optional ID binding for simple identity-based scrolling.
    • Legacy/Proxy-based: Use ScrollViewReader when you need proxy-based programmatic scrolling (e.g., scroll-to-top/bottom) or need to support versions earlier than iOS 17.
  3. Handle Sheet Dismissal and Actions Internally

    main

    Sheets should manage their own dismissal and actions internally using @Environment(\.dismiss). Avoid passing onSave or onCancel closures from the parent view to prevent callback prop-drilling and improve reusability.

    struct EditItemSheet: View {
        @Environment(\.dismiss) private var dismiss
        let item: Item
        @State private var name: String
    
        init(item: Item) {
            self.item = item
            _name = State(initialValue: item.name)
        }
    
        var body: some View {
            NavigationStack {
                Form { TextField("Name", text: $name) }
                    .navigationTitle("Edit Item")
                    .toolbar {
                        ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } }
                        ToolbarItem(placement: .confirmationAction) { Button("Save") { /* save and dismiss */ } }
                    }
            }
        }
    }
  4. Install the SwiftUI Expert Skill via skills.sh

    main

    You can install this skill using a single command via the skills.sh platform. This is the quickest way to add the skill to an AI agent that supports the Agent Skills open format.

    npx skills add https://github.com/avdlee/swiftui-agent-skill --skill swiftui-expert-skill
  5. Manage view containers correctly

    main

    Custom views should own their static containers (like an HStack for a header) so the caller doesn't have to. However, custom views should NOT own lazy or repeatable containers (like LazyVStack or List); those should be owned by the caller to allow for proper list management and performance.

    // Good - owns static container
    struct HeaderView: View {
        var body: some View {
            HStack {
                Image(systemName: "star")
                Text("Title")
                Spacer()
            }
        }
    }
    
    // Good - caller owns lazy container
    struct FeedView: View {
        let items: [Item]
        
        var body: some View {
            LazyVStack {
                ForEach(items) { item in
                    ItemRow(item: item)
                }
            }
        }
    }
  6. Create an Adaptive Table for Compact Size Classes

    main

    On iPhone or iPad in Slide Over, Table only shows the first column. To provide a good user experience, use @Environment(\.horizontalSizeClass) to detect .compact and customize the first column to display combined information (e.g., name and email) instead of just one field.

    struct AdaptiveTable: View {
        @Environment(\.horizontalSizeClass) private var horizontalSizeClass
        private var isCompact: Bool { horizontalSizeClass == .compact }
    
        @State private var people: [Person] = [ /* ... */ ]
        @State private var sortOrder = [KeyPathComparator(\Person.givenName)]
    
        var body: some View {
            Table(people, sortOrder: $sortOrder) {
                TableColumn("Given Name", value: \.givenName) { person in
                    VStack(alignment: .leading) {
                        Text(isCompact ? person.fullName : person.givenName)
                        if isCompact {
                            Text(person.emailAddress)
                                .foregroundStyle(.secondary)
                        }
                    }
                }
                TableColumn("Family Name", value: \.familyName)
                TableColumn("E-Mail Address", value: \.emailAddress)
            }
            .onChange(of: sortOrder) { _, newOrder in
                people.sort(using: newOrder)
            }
        }
    }
  7. Organize SwiftUI View File Structure

    main

    While property ordering does not affect correctness or performance, adopting a consistent order can improve readability. A common pattern is to order properties by:

    1. Environment properties (@Environment)
    2. State properties (@Binding, @State, @StateObject, @ObservedObject)
    3. Private properties
    4. Initializers (init)
    5. The body property
    6. Helper subviews (computed properties or methods)
    struct ContentView: View {
        // MARK: - Environment Properties
        @Environment(\.colorScheme) var colorScheme
    
        // MARK: - State Properties
        @Binding var isToggled: Bool
        @State private var viewModel: SomeViewModel
    
        // MARK: - Private Properties
        private let title: String = "SwiftUI Guide"
    
        // MARK: - Initializer (if needed)
        init(isToggled: Binding<Bool>) {
            self._isToggled = isToggled
        }
    
        // MARK: - Body
        var body: some View {
            VStack {
                header
                content
            }
        }
    
        // MARK: - Computed Subviews
        private var header: some View {
            Text(title).font(.largeTitle).padding()
        }
    
        private var content: some View {
            VStack {
                Text("Counter: \(counter)")
            }
        }
    }
  8. Use Lazy Containers for Large Data Sets

    main

    To improve performance and reduce memory usage when rendering large collections, use lazy containers like LazyVStack, LazyHStack, LazyVGrid, or LazyHGrid. These containers only load views as they appear on the screen.

    struct ContentView: View {
        let items = Array(0..<1000)
    
        var body: some View {
            ScrollView {
                LazyVStack {
                    ForEach(items, id: \.self) { item in
                        Text("Item \(item)")
                    }
                }
            }
        }
    }
  9. Handle Swift Charts Version Compatibility and Fallbacks

    main

    Because chart modifiers like .chartXSelection change the return type of the Chart view, you cannot conditionally apply them using a standard if #available block inside a single chain. Instead, you must duplicate the entire Chart implementation to provide fallbacks for older iOS versions.

    API Availability Breakdown

    • iOS 16+: Core Chart types (BarMark, LineMark, etc.), ChartProxy, chartOverlay, chartBackground.
    • iOS 17+: SectorMark, selection APIs (chartXSelection, chartYSelection, chartAngleSelection), scrolling APIs (chartScrollableAxes, chartXVisibleDomain), and chartGesture.
    • iOS 18+: Plot types (AreaPlot, BarPlot, LinePlot, PointPlot, RectanglePlot, RulePlot, SectorPlot) and function plotting.
    • iOS 26+: Chart3D, SurfacePlot, Z-axis marks, and 3D camera/pose APIs.