diff --git a/.github/workflows/skill-review.yml b/.github/workflows/skill-review.yml new file mode 100644 index 0000000..f0ab779 --- /dev/null +++ b/.github/workflows/skill-review.yml @@ -0,0 +1,13 @@ +name: Skill Review +on: + pull_request: + paths: ['**/SKILL.md'] +jobs: + review: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + steps: + - uses: actions/checkout@v4 + - uses: tesslio/skill-review@22e928dd837202b2b1d1397e0114c92e0fae5ead # main diff --git a/SKILL.md b/SKILL.md index 58dc19b..e9dc138 100644 --- a/SKILL.md +++ b/SKILL.md @@ -104,6 +104,64 @@ enum ServiceError: LocalizedError, Sendable { | Combine to async | `publisher.sinkSingleValue() async throws` | | Custom defaults | `@DefaultValue` property wrapper | +## Key Patterns + +### Actor Definition + +```swift +actor DataStore { + private var cache: [String: Data] = [:] + + func fetch(_ key: String) async throws -> Data { + if let cached = cache[key] { return cached } + let data = try await URLSession.shared.data(from: endpoint(for: key)).0 + cache[key] = data + return data + } +} +``` + +### @MainActor ViewModel + +```swift +@MainActor +final class ItemListViewModel: ObservableObject { + @Published private(set) var items: [Item] = [] + @Published private(set) var isLoading = false + private let store: DataStore + + init(store: DataStore) { self.store = store } + + func load() async { + isLoading = true + defer { isLoading = false } + items = (try? await store.fetchItems()) ?? [] + } +} +``` + +### AsyncStream Usage + +```swift +func progressUpdates() -> AsyncStream { + AsyncStream { continuation in + let observer = progress.observe(\.fractionCompleted) { p, _ in + continuation.yield(Float(p.fractionCompleted)) + if p.isFinished { continuation.finish() } + } + continuation.onTermination = { _ in observer.invalidate() } + } +} +``` + +### Swift 6 Strict Concurrency Workflow + +1. Enable in `Package.swift`: `swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]` +2. Build — fix `Sendable` warnings: mark value types `Sendable`, use `@MainActor` for UI state +3. Isolate shared mutable state in `actor` types or `Mutex` +4. Ensure closures crossing isolation boundaries are `@Sendable` +5. Verify: build with no warnings → run tests → check for runtime isolation violations in Console + ## Detailed References - **swift-concurrency.md** — Swift 6 concurrency patterns: actors, Sendable, AsyncSequence, task cancellation, synchronization, state machines, reactive patterns, generics, persistence, networking, testing, diagnostics