본문으로 건너뛰기
cccskills
무료GitHub에 공개

swift-patterns

SwiftUI view composition, @Observable patterns, async/await concurrency, TCA architecture, and Combine reactive streams.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md7.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Swift Patterns

Modern Swift patterns for iOS/macOS application development.

SwiftUI View Composition

// Small, focused views composed together
struct ProductCard: View {
    let product: Product

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            ProductImage(url: product.imageURL)
            ProductInfo(name: product.name, price: product.price)
            RatingStars(rating: product.rating, count: product.reviewCount)
        }
        .padding()
        .background(.regularMaterial)
        .clipShape(RoundedRectangle(cornerRadius: 12))
    }
}

// Extract subviews for readability and reusability
struct ProductImage: View {
    let url: URL

    var body: some View {
        AsyncImage(url: url) { phase in
            switch phase {
            case .success(let image):
                image.resizable().aspectRatio(contentMode: .fill)
            case .failure:
                Image(systemName: "photo").foregroundStyle(.secondary)
            case .empty:
                ProgressView()
            @unknown default:
                EmptyView()
            }
        }
        .frame(height: 200)
        .clipped()
    }
}

// ViewModifier for reusable styling
struct CardModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(.regularMaterial)
            .clipShape(RoundedRectangle(cornerRadius: 12))
            .shadow(radius: 2)
    }
}

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

@Observable Pattern (iOS 17+)

import Observation

@Observable
final class ProductStore {
    var products: [Product] = []
    var isLoading = false
    var errorMessage: String?

    private let apiClient: APIClient

    init(apiClient: APIClient = .shared) {
        self.apiClient = apiClient
    }

    func loadProducts() async {
        isLoading = true
        errorMessage = nil

        do {
            products = try await apiClient.fetchProducts()
        } catch {
            errorMessage = error.localizedDescription
        }

        isLoading = false
    }

    func deleteProduct(_ product: Product) async throws {
        try await apiClient.deleteProduct(id: product.id)
        products.removeAll { $0.id == product.id }
    }
}

// Usage in SwiftUI (automatic tracking, no @Published needed)
struct ProductListView: View {
    @State private var store = ProductStore()

    var body: some View {
        List(store.products) { product in
            ProductCard(product: product)
        }
        .overlay {
            if store.isLoading { ProgressView() }
            if let error = store.errorMessage {
                ContentUnavailableView("Error", systemImage: "exclamationmark.triangle",
                                       description: Text(error))
            }
        }
        .task { await store.loadProducts() }
    }
}

Structured Concurrency

// TaskGroup for parallel async work
func loadDashboard() async throws -> Dashboard {
    async let profile = apiClient.fetchProfile()
    async let orders = apiClient.fetchRecentOrders()
    async let recommendations = apiClient.fetchRecommendations()

    // All three run concurrently, await all results
    return try await Dashboard(
        profile: profile,
        orders: orders,
        recommendations: recommendations
    )
}

// TaskGroup with dynamic number of tasks
func loadImages(urls: [URL]) async -> [URL: UIImage] {
    await withTaskGroup(of: (URL, UIImage?).self) { group in
        for url in urls {
            group.addTask {
                let image = try? await ImageLoader.load(url)
                return (url, image)
            }
        }

        var results: [URL: UIImage] = [:]
        for await (url, image) in group {
            if let image { results[url] = image }
        }
        return results
    }
}

// Actor for thread-safe shared state
actor ImageCache {
    private var cache: [URL: UIImage] = [:]
    private var inFlight: [URL: Task<UIImage, Error>] = [:]

    func image(for url: URL) async throws -> UIImage {
        if let cached = cache[url] { return cached }

        // Coalesce duplicate requests
        if let existing = inFlight[url] {
            return try await existing.value
        }

        let task = Task {
            let (data, _) = try await URLSession.shared.data(from: url)
            guard let image = UIImage(data: data) else {
                throw ImageError.invalidData
            }
            return image
        }

        inFlight[url] = task
        let image = try await task.value
        cache[url] = image
        inFlight[url] = nil
        return image
    }
}

TCA (The Composable Architecture) Pattern

import ComposableArchitecture

@Reducer
struct ProductFeature {
    @ObservableState
    struct State: Equatable {
        var products: [Product] = []
        var isLoading = false
        var alert: AlertState<Action>?
    }

    enum Action {
        case onAppear
        case productsLoaded(Result<[Product], Error>)
        case deleteProduct(Product)
        case alertDismissed
    }

    @Dependency(\.apiClient) var apiClient

    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .onAppear:
                state.isLoading = true
                return .run { send in
                    let result = await Result { try await apiClient.fetchProducts() }
                    await send(.productsLoaded(result))
                }

            case .productsLoaded(.success(let products)):
                state.isLoading = false
                state.products = products
                return .none

            case .productsLoaded(.failure(let error)):
                state.isLoading = false
                state.alert = AlertState { TextState(error.localizedDescription) }
                return .none

            case .deleteProduct(let product):
                state.products.removeAll { $0.id == product.id }
                return .run { _ in try await apiClient.deleteProduct(id: product.id) }

            case .alertDismissed:
                state.alert = nil
                return .none
            }
        }
    }
}

Checklist

  • Views under 50 lines; extract subviews for composition
  • Use @Observable (iOS 17+) over ObservableObject/@Published
  • Structured concurrency with async let for parallel work
  • Actors for shared mutable state (not locks/queues)
  • ViewModifiers for reusable styling patterns
  • Environment for dependency injection in SwiftUI
  • Task cancellation handled (check Task.isCancelled)
  • Preview providers for every view with mock data

Anti-Patterns

  • Massive views: 200+ line body property (extract subviews)
  • @StateObject in child views: use @State or pass as parameter
  • Blocking main thread with synchronous work in views
  • Force unwrapping optionals: use guard let or nil coalescing
  • Ignoring task cancellation: leaked work after view disappears
  • Using singletons instead of dependency injection (untestable)

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

WCAG 2.2 AA compliance, ARIA patterns, keyboard navigation, screen reader optimization

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vibeeval/vibecosystem5382026년 8월 9일 업데이트

axe-core integration, WCAG 2.2 AA checklist, keyboard navigation testing, screen reader testing, and ARIA pattern validation.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Steam-style achievement system with XP, levels, streaks, and skill trees. Gamifies the development workflow. 25 achievements across 5 categories.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Framework for measuring and tracking agent response quality over time. Detects regressions before they reach production. Use when evaluating agent changes, auditing quality, or establishing performance baselines.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Agent Context Isolation

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vibeeval/vibecosystem5382026년 8월 9일 업데이트

Agent ve skill dosyalarinin yapisal dogrulamasi. Frontmatter kontrol, naming convention, zorunlu bolum kontrolu, tutarlilik denetimi. Yeni agent/skill eklendiginde veya mevcut dosyalar duzenlediginde otomatik calistirilir.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vibeeval/vibecosystem5382026년 8월 9일 업데이트

vibeeval의 스킬 모두 보기

이 스킬의 문제 신고