Claude Code Plugins

Community-maintained marketplace

Feedback

Build modern UIs with SwiftUI - views, state management, animations, navigation

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name swift-swiftui
description Build modern UIs with SwiftUI - views, state management, animations, navigation
version 2.0.0
sasmp_version 1.3.0
bonded_agent 03-swift-swiftui
bond_type PRIMARY_BOND

SwiftUI Skill

Declarative UI framework knowledge for building modern Apple platform interfaces.

Prerequisites

  • Xcode 15+ installed
  • iOS 16+ / macOS 13+ deployment target recommended
  • Understanding of reactive programming concepts

Parameters

parameters:
  min_ios_version:
    type: string
    default: "16.0"
    description: Minimum iOS version
  platforms:
    type: array
    items: [iOS, macOS, watchOS, tvOS, visionOS]
    default: [iOS]
  observation_framework:
    type: string
    enum: [observation, combine, observable_object]
    default: observation
    description: State management approach

Topics Covered

Property Wrappers

Wrapper Ownership Use Case
@State View owns Local, private state
@Binding Parent owns Two-way child connection
@StateObject View creates/owns Observable object lifecycle
@ObservedObject External owns Passed observable
@EnvironmentObject Environment owns Dependency injection
@Environment System provides System values (colorScheme, etc)

Observation (iOS 17+)

Feature Description
@Observable Macro for observable classes
@Bindable Create bindings from Observable
Automatic tracking No need for @Published

Layout System

Container Purpose
VStack Vertical arrangement
HStack Horizontal arrangement
ZStack Overlapping views
LazyVStack/HStack Lazy loading for lists
Grid 2D grid layout
GeometryReader Access to size/position

Code Examples

Observation Pattern (iOS 17+)

import SwiftUI

@Observable
final class ShoppingCart {
    var items: [CartItem] = []
    var couponCode: String = ""

    var subtotal: Decimal {
        items.reduce(0) { $0 + $1.price * Decimal($1.quantity) }
    }

    var total: Decimal {
        let discount = applyCoupon(to: subtotal)
        return subtotal - discount
    }

    func add(_ product: Product, quantity: Int = 1) {
        if let index = items.firstIndex(where: { $0.product.id == product.id }) {
            items[index].quantity += quantity
        } else {
            items.append(CartItem(product: product, quantity: quantity))
        }
    }

    func remove(_ item: CartItem) {
        items.removeAll { $0.id == item.id }
    }

    private func applyCoupon(to amount: Decimal) -> Decimal {
        guard !couponCode.isEmpty else { return 0 }
        // Apply coupon logic
        return amount * 0.1
    }
}

struct CartView: View {
    @Bindable var cart: ShoppingCart

    var body: some View {
        List {
            ForEach(cart.items) { item in
                CartItemRow(item: item)
            }
            .onDelete { indexSet in
                cart.items.remove(atOffsets: indexSet)
            }

            Section {
                HStack {
                    TextField("Coupon code", text: $cart.couponCode)
                    Button("Apply") { }
                }

                LabeledContent("Subtotal", value: cart.subtotal, format: .currency(code: "USD"))
                LabeledContent("Total", value: cart.total, format: .currency(code: "USD"))
                    .fontWeight(.bold)
            }
        }
        .navigationTitle("Cart (\(cart.items.count))")
    }
}

Custom View Modifier

struct CardStyle: ViewModifier {
    let cornerRadius: CGFloat
    let shadowRadius: CGFloat

    func body(content: Content) -> some View {
        content
            .background(.background)
            .clipShape(RoundedRectangle(cornerRadius: cornerRadius))
            .shadow(color: .black.opacity(0.1), radius: shadowRadius, y: 2)
    }
}

extension View {
    func cardStyle(cornerRadius: CGFloat = 12, shadowRadius: CGFloat = 4) -> some View {
        modifier(CardStyle(cornerRadius: cornerRadius, shadowRadius: shadowRadius))
    }
}

// Usage
struct ProductCard: View {
    let product: Product

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            AsyncImage(url: product.imageURL) { image in
                image.resizable().aspectRatio(contentMode: .fill)
            } placeholder: {
                ProgressView()
            }
            .frame(height: 150)
            .clipped()

            Text(product.name)
                .font(.headline)

            Text(product.price, format: .currency(code: "USD"))
                .foregroundStyle(.secondary)
        }
        .cardStyle()
    }
}

Custom Animations

struct PulsingButton: View {
    let title: String
    let action: () -> Void

    @State private var isPulsing = false

    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.headline)
                .foregroundStyle(.white)
                .padding(.horizontal, 24)
                .padding(.vertical, 12)
                .background(.blue)
                .clipShape(Capsule())
                .scaleEffect(isPulsing ? 1.05 : 1.0)
        }
        .onAppear {
            withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) {
                isPulsing = true
            }
        }
    }
}

struct MatchedGeometryExample: View {
    @Namespace private var animation
    @State private var isExpanded = false

    var body: some View {
        VStack {
            if isExpanded {
                RoundedRectangle(cornerRadius: 20)
                    .fill(.blue)
                    .matchedGeometryEffect(id: "shape", in: animation)
                    .frame(height: 300)
            } else {
                RoundedRectangle(cornerRadius: 10)
                    .fill(.blue)
                    .matchedGeometryEffect(id: "shape", in: animation)
                    .frame(width: 100, height: 100)
            }
        }
        .onTapGesture {
            withAnimation(.spring(response: 0.5, dampingFraction: 0.7)) {
                isExpanded.toggle()
            }
        }
    }
}

Navigation Stack (iOS 16+)

struct NavigationExample: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            List(products) { product in
                NavigationLink(value: product) {
                    ProductRow(product: product)
                }
            }
            .navigationTitle("Products")
            .navigationDestination(for: Product.self) { product in
                ProductDetailView(product: product)
            }
            .navigationDestination(for: Category.self) { category in
                CategoryView(category: category)
            }
        }
    }

    func navigateToProduct(_ product: Product) {
        path.append(product)
    }

    func popToRoot() {
        path.removeLast(path.count)
    }
}

Troubleshooting

Common Issues

Issue Cause Solution
View not updating Wrong property wrapper Check ownership: @State vs @StateObject
Preview crash Missing mock data Provide preview with sample data
Animation stutters Expensive body Extract subviews, avoid complex calculations
Navigation broken Missing NavigationStack Ensure view is inside NavigationStack
List slow Complex cells Use LazyVStack, simplify cell views

Debug Tips

// Trace view updates
var body: some View {
    let _ = Self._printChanges()
    // ... view content
}

// Check if preview
#if DEBUG
struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView(data: .preview)
    }
}
#endif

Validation Rules

validation:
  - rule: state_ownership
    severity: error
    check: @StateObject for views that create, @ObservedObject for passed
  - rule: body_purity
    severity: warning
    check: No side effects in body computed property
  - rule: lazy_for_lists
    severity: info
    check: Use LazyVStack/LazyHStack for long scrolling content

Usage

Skill("swift-swiftui")

Related Skills

  • swift-combine - Reactive programming
  • swift-uikit - UIKit interop
  • swift-architecture - MVVM patterns