Skip to content
Development
Skill

/swift-patterns

Swift/iOS: SwiftUI, Combine, async/await, actors, SPM, Core Data, UIKit interop. Triggers: Swift, SwiftUI, Combine, iOS, Xcode, actor, Core Data, @MainActor, @State.

From plugin
ai-toolkit
161111 skills44 agents
Install
$ npx -y skills add softspark/ai-toolkit --skill swift-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/swift-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

Swift/iOS: SwiftUI, Combine, async/await, actors, SPM, Core Data, UIKit interop. Triggers: Swift, SwiftUI, Combine, iOS, Xcode, actor, Core Data, @MainActor, @State.

SKILL.md

swift-patterns.SKILL.md
name: swift-patterns
description: "Swift/iOS: SwiftUI, Combine, async/await, actors, SPM, Core Data, UIKit interop. Triggers: Swift, SwiftUI, Combine, iOS, Xcode, actor, Core Data, @MainActor, @State."
effort: medium
user-invocable: false
allowed-tools: Read

Swift / iOS Patterns

Project Structure

Swift Package (SPM)

MyPackage/
├── Package.swift
├── Sources/
│   ├── MyLibrary/
│   └── MyExecutable/
├── Tests/
│   └── MyLibraryTests/
└── Plugins/
// swift-tools-version: 5.10
import PackageDescription

let package = Package(
    name: "MyPackage",
    platforms: [.iOS(.v17), .macOS(.v14)],
    products: [
        .library(name: "MyLibrary", targets: ["MyLibrary"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
    ],
    targets: [
        .target(name: "MyLibrary",
                dependencies: [.product(name: "Algorithms", package: "swift-algorithms")]),
        .testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
    ]
)

Xcode Project Layout

MyApp/
├── MyApp/
│   ├── App/             # Entry point, ContentView
│   ├── Features/        # Feature modules (Views, ViewModels, Models)
│   ├── Core/            # Networking, Storage, Extensions
│   └── Resources/       # Assets.xcassets, Info.plist
├── MyAppTests/
└── MyAppUITests/

---

Idioms / Code Style

Optionals

// guard-let for early exit
func process(user: User?) {
    guard let user else { return }
    print(user.name)
}

// Optional chaining + nil coalescing
let name = user?.profile?.displayName ?? "Anonymous"

// map/flatMap on optionals
let length: Int? = optionalString.map { $0.count }
// Never force-unwrap in production: user!.name

Protocol-Oriented Programming

protocol Cacheable: Identifiable where ID: Hashable {
    var cacheKey: String { get }
}

extension Cacheable where ID == String {
    var cacheKey: String { id }
}

Value Types vs Reference Types

Use structs by default (value semantics, thread-safe). Use classes when identity matters, shared mutable state is intentional, inheritance is needed, or ObjC interop is required.

Property Wrappers

@propertyWrapper
struct Clamped<Value: Comparable> {
    var wrappedValue: Value {
        didSet { wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound) }
    }
    let range: ClosedRange<Value>

    init(wrappedValue: Value, _ range: ClosedRange<Value>) {
        self.range = range
        self.wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}

struct Volume {
    @Clamped(0...100) var level: Int = 50
}

Result Builders

@resultBuilder
struct ArrayBuilder<Element> {
    static func buildBlock(_ components: [Element]...) -> [Element] { components.flatMap { $0 } }
    static func buildExpression(_ expression: Element) -> [Element] { [expression] }
    static func buildOptional(_ component: [Element]?) -> [Element] { component ?? [] }
}

---

Error Handling

throws / try / catch

enum NetworkError: Error, LocalizedError {
    case invalidURL
    case timeout(seconds: Int)
    case serverError(statusCode: Int)

    var errorDescription: String? {
        switch self {
        case .invalidURL: "Invalid URL."
        case .timeout(let s): "Timed out after \(s)s."
        case .serverError(let code): "Server returned \(code)."
        }
    }
}

do {
    let user = try fetchUser(id: "123")
} catch let error as NetworkError {
    handleNetworkError(error)
} catch {
    handleUnexpected(error)
}

let user = try? fetchUser(id: "123") // nil on error

Result Type

switch fetchData(from: url) {
case .success(let data): process(data)
case .failure(let error): showError(error)
}

Typed Throws (Swift 6) and Async Throws

func load() throws(DatabaseError) -> [Item] { /* compiler-enforced error type */ }

func fetchUser(id: String) async throws -> User {
    let (data, response) = try await URLSession.shared.data(from: url)
    guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
        throw NetworkError.serverError(statusCode: 0)
    }
    return try JSONDecoder().decode(User.self, from: data)
}

---

Testing

XCTest

final class UserServiceTests: XCTestCase {
    var sut: UserService!
    var mockRepo: MockUserRepository!

    override func setUp() {
        mockRepo = MockUserRepository()
        sut = UserService(repository: mockRepo)
    }

    func testFetchUser_success() async throws {
        mockRepo.stubbedUser = User(id: "1", name: "Alice")
        let user = try await sut.fetchUser(id: "1")
        XCTAssertEqual(user.name, "Alice")
    }

    func testFetchUser_notFound_throws() async {
        mockRepo.shouldFail = true
        do {
            _ = try await sut.fetchUser(id: "999")
            XCTFail("Expected error")
        } catch {
            XCTAssertTrue(error is UserService.Error)
        }
    }
}

Swift Testing Framework (Swift 6+)

import Testing

@Suite("UserService")
struct UserServiceTests {
    @Test("fetches user by ID")
    func fetchUser() async throws {
        let mockRepo = MockUserRepository()
        mockRepo.stubbedUser = User(id: "1", name: "Alice")
        let sut = UserService(repository: mockRepo)
        let user = try await sut.fetchUser(id: "1")
        #expect(user.name == "Alice")
    }

    @Test("throws on missing user", arguments: ["999", ""])
    func fetchMissingUser(id: String) async {
        let sut = UserService(repository: MockUserRepository())
        await #expect(throws: UserService.Error.self) {
            try await sut.fetchUser(id: id)
        }
    }
}

Protocol-Based Mocking

protocol UserRepository {
    func fetch(id: String) async throws -> User
}

final class MockUserRepository: UserRepository {
    var stubbedUser: User?
    var shouldFail
Read more
Ships withai-toolkit

Professional-grade AI coding toolkit with multi-platform support. Machine-enforced safety, 109 skills, 44 agents, expanded lifecycle hooks, persona presets, experimental opt-in plugin packs, and benchmark tooling — works with Claude Code, Claude Chat/Cowork,

Get the whole plugin