swift-patterns

SKILL.md

Swift Language Patterns

Modern Swift idioms for clean, safe code.

Optionals

Safe Unwrapping

// ✅ Good: guard let for early exit
func processUser(_ user: User?) {
    guard let user = user else {
        print("User is nil")
        return
    }
    // user is non-optional here
    print(user.name)
}

// ✅ Good: if let for conditional
if let email = user.email {
    sendEmail(to: email)
}

// ✅ Good: nil coalescing
let displayName = user.name ?? "Anonymous"

// ✅ Good: Optional chaining
let street = user.address?.street

// ❌ Bad: Force unwrap (crashes if nil)
let email = user.email!  // Don't do this

Optional Mapping

// ✅ map for transformation
let uppercase = user.name?.map { $0.uppercased() }

// ✅ flatMap for chaining optionals
let domain = user.email?.flatMap { $0.split(separator: "@").last }

Closures

Trailing Closures

// ✅ Trailing closure syntax
viewModel.fetchUsers { users in
    self.users = users
}

// Equivalent to
viewModel.fetchUsers { users in
    self.users = users
}

// ✅ Shorthand argument names
viewModel.fetchUsers {
    self.users = $0
}

Capture Lists

// ✅ Capture lists to avoid retain cycles
class UserViewController: UIViewController {
    private let viewModel: UserViewModel

    func setupRefresh() {
        viewModel.onRefresh = { [weak self] in
            guard let self = self else { return }
            self.tableView.reloadData()
        }
    }

    // ✅ Or capture self explicitly when ownership is clear
    func setupTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] _ in
            self.updateUI()
        }
    }
}

Higher-Order Functions

let numbers = [1, 2, 3, 4, 5]

// ✅ map - transform
let doubled = numbers.map { $0 * 2 }  // [2, 4, 6, 8, 10]

// ✅ filter - select
let evens = numbers.filter { $0 % 2 == 0 }  // [2, 4]

// ✅ reduce - combine
let sum = numbers.reduce(0) { $0 + $1 }  // 15

// ✅ compactMap - filter out nils
let names = users.compactMap { $0.name }

// ✅ forEach - side effects
numbers.forEach { print($0) }

// ❌ Don't use forEach for transformation
// ❌ Bad: numbers.forEach { doubled.append($0 * 2) }
// ✅ Good: let doubled = numbers.map { $0 * 2 }

Properties

Computed Properties

struct User {
    let firstName: String
    let lastName: String

    // ✅ Computed property
    var fullName: String {
        "\(firstName) \(lastName)"
    }

    // ✅ With setter
    var age: Int {
        get { _age }
        set {
            guard newValue >= 0 else { return }
            _age = newValue
        }
    }
    private var _age: Int = 0
}

Property Observers

class Settings {
    var theme: Theme = .light {
        didSet {
            saveSettings()
            notifyThemeChanged()
        }
    }

    var score: Int = 0 {
        willSet {
            print("Score will change from \(score) to \(newValue)")
        }
        didSet {
            if oldValue > score {
                print("Score decreased!")
            }
        }
    }
}

Lazy Properties

struct DataProcessor {
    // ✅ Lazy - created only when accessed
    private lazy var formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    }()

    func process(date: Date) -> String {
        formatter.string(from: date)
    }
}

Protocols

Protocol-Oriented Programming

// ✅ Protocol with associated type
protocol Repository {
    associatedtype Item
    func fetch(id: String) async throws -> Item
    func save(_ item: Item) async throws
}

// ✅ Generic where clause
extension Repository {
    func fetchAll(ids: [String]) async throws -> [Item] {
        try await withThrowingTaskGroup(of: Item.self) { group in
            for id in ids {
                group.addTask { try await fetch(id: id) }
            }
            return try await group.reduce(into: [Item]()) { $0.append($1) }
        }
    }
}

Protocol Extensions

protocol Identifiable {
    var id: String { get }
}

extension Identifiable {
    func debugInfo() -> String {
        "ID: \(id)"
    }
}

struct User: Identifiable {
    let id: String
    let name: String
}

let user = User(id: "123", name: "John")
print(user.debugInfo())  // "ID: 123"

Generics

Generic Functions

// ✅ Generic function
func first<T>(_ array: [T]) -> T? {
    array.first
}

// ✅ With constraints
func sorted<T: Comparable>(_ array: [T]) -> [T] {
    array.sorted()
}

// ✅ Multiple constraints
func process<T: Sequence & Sendable>(_ items: T) where T.Element: Hashable {
    // Process items
}

Associated Types

protocol DataSource {
    associatedtype Item
    func getItems() async throws -> [Item]
}

class RemoteDataSource: DataSource {
    typealias Item = User

    func getItems() async throws -> [User] {
        try await api.getUsers()
    }
}

Result Type

// ✅ Result for error handling
func fetchUser(id: String) async -> Result<User, Error> {
    do {
        let user = try await api.getUser(id)
        return .success(user)
    } catch {
        return .failure(error)
    }
}

// Usage
let result = await fetchUser(id: "123")
switch result {
case .success(let user):
    displayUser(user)
case .failure(let error):
    showError(error)
}

// ✅ map, flatMap on Result
let name = await fetchUser(id: "123").map(\.name)

Async/Await

Structured Concurrency

// ✅ async let for concurrent work
func loadDashboard() async throws -> Dashboard {
    async let user = getUser()
    async let notifications = getNotifications()
    async let stats = getStats()

    return try await Dashboard(
        user: user,
        notifications: notifications,
        stats: stats
    )
}

// ✅ TaskGroup for dynamic concurrency
func fetchAllImages(urls: [URL]) async throws -> [Image] {
    try await withThrowingTaskGroup(of: Image.self) { group in
        for url in urls {
            group.addTask {
                try await downloadImage(from: url)
            }
        }

        var images: [Image] = []
        for try await image in group {
            images.append(image)
        }
        return images
    }
}

MainActor

// ✅ MainActor for UI updates
@MainActor
class HomeViewModel: Observable {
    @Published var users: [User] = []

    func loadUsers() async {
        // Already on main actor
        let users = try? await api.getUsers()
        self.users = users ?? []
    }
}

// ✅ Explicit main actor dispatch
class NetworkService {
    func fetch() async -> Data {
        // Background work
        let data = ...

        // Dispatch to main if needed
        await MainActor.run {
            // UI work
        }

        return data
    }
}

Enums

Associated Values

// ✅ Enum with associated values
enum NetworkError: Error {
    case invalidURL
    case noConnection
    case serverError(code: Int, message: String)
    case decodingError(Error)
}

func handle(_ error: NetworkError) {
    switch error {
    case .invalidURL:
        print("Invalid URL")
    case .noConnection:
        print("No connection")
    case .serverError(let code, let message):
        print("Server error \(code): \(message)")
    case .decodingError(let error):
        print("Decoding failed: \(error)")
    }
}

Enum as State

// ✅ Enum for exhaustive state
enum LoadState<T> {
    case idle
    case loading
    case loaded(T)
    case error(Error)

    var isLoading: Bool {
        if case .loading = self { return true }
        return false
    }

    var value: T? {
        if case .loaded(let value) = self { return value }
        return nil
    }
}

// Usage
@Published var state: LoadState<[User]> = .idle

switch state {
case .idle:
    EmptyView()
case .loading:
    ProgressView()
case .loaded(let users):
    UserList(users: users)
case .error(let error):
    ErrorView(error: error)
}

Access Control

// ✅ Use explicit access control
public struct PublicAPI {
    public var value: String

    private init() {
        value = "default"
    }

    // ✅ private for fileprivate only when needed
    private var helper: String {
        "helper"
    }

    // ✅ internal (default, same module)
    var internalValue: String = "internal"

    // ✅ fileprivate (same file)
    fileprivate var fileOnly: String = "fileOnly"
}

Key Paths

// ✅ Key paths for type-safe references
struct User {
    let name: String
    let email: String
    let age: Int
}

let nameKeyPath = \User.name
let users = [User(...)]

let names = users.map(\.name)  // [String]

// ✅ With functions
func sort<T>(_ users: [T], by keyPath: KeyPath<T, String>) -> [T] {
    users.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}

let sorted = sort(users, by: \.name)

Remember: Swift is designed for safety. Use optionals, type inference, and value types to write robust code.

Weekly Installs
4
GitHub Stars
33
First Seen
Feb 20, 2026
Installed on
opencode4
github-copilot4
codex4
amp4
cline4
kimi-cli4