swift
Swift language fundamentals (5.10+ / 6.x). Covers optionals, value vs reference semantics, protocols & generics, Swift Concurrency (async/await, actors, Sendable, structured tasks), Result Builders, and Apple platform interop. USE WHEN: user mentions "Swift", "SwiftUI", "async/await Swift", "actor", "Sendable", "Codable", "Combine", "Result Builder", "Apple Keychain", "Secure Enclave", "iOS native" DO NOT USE FOR: SwiftUI screen layouts in depth - use SwiftUI-specific skill if exists DO NOT USE FOR: Compose iOS via Skia - use `frontend-frameworks/compose-multiplatform` DO NOT USE FOR: Kotlin/Native ↔ Swift bridging - use `languages/uniffi`
What this skill does
# Swift Core Knowledge
> **References**: [concurrency.md](quick-ref/concurrency.md) for async/await, actors, Sendable, structured tasks. [interop.md](quick-ref/interop.md) for ObjC, C, and Rust (cinterop) bridging, Keychain, Secure Enclave.
>
> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `swift`.
## Optionals
```swift
var name: String = "Alice" // Non-optional
var maybe: String? = nil // Optional (sugar for Optional<String>)
// Force-unwrap (avoid)
let n = maybe! // Crash if nil
// Optional binding
if let n = maybe {
print("got \(n)")
}
// Guard (early exit)
func greet(_ name: String?) {
guard let name else { return }
print("Hello, \(name)")
}
// Nil coalescing
let display = maybe ?? "Anonymous"
// Optional chaining
let len = maybe?.count // Int?
```
Use `guard let name else { ... }` (Swift 5.7+) — implicit shadow shorter than `guard let name = name else`.
## Value vs Reference Types
| Kind | Semantics | Allocation | Use for |
|---|---|---|---|
| `struct` | Value (copied on assign/pass) | Stack (usually) | Models, lightweight data, thread-safe by default |
| `enum` | Value | Stack | Discriminated unions, sealed states |
| `class` | Reference (shared identity) | Heap | Identity, inheritance, ObjC interop |
| `actor` | Reference + isolated | Heap | Mutable state across concurrency |
```swift
struct Point { var x: Double; var y: Double }
var a = Point(x: 1, y: 2)
var b = a // copy
b.x = 10
// a.x still 1, b.x is 10
class Node { var value: Int = 0 }
let n1 = Node()
let n2 = n1 // same instance
n2.value = 42 // n1.value also 42
```
Default to `struct`. Use `class` only for identity, mutable shared state, or ObjC inheritance.
## Enums with Associated Values
```swift
enum NetworkResult<Success> {
case success(Success)
case failure(NetworkError)
case loading
}
enum NetworkError: Error {
case unauthorized
case server(code: Int, body: String)
case timeout(after: TimeInterval)
}
func handle<T>(_ result: NetworkResult<T>) -> String {
switch result {
case .success(let value):
return "ok: \(value)"
case .failure(.unauthorized):
return "login"
case .failure(.server(let code, _)):
return "server \(code)"
case .failure(.timeout(let t)):
return "timeout \(t)"
case .loading:
return "loading"
}
}
```
`switch` is exhaustive — no `default` needed when all cases covered. Compiler enforces.
## Protocols & Generics
```swift
protocol Identifiable {
associatedtype ID: Hashable
var id: ID { get }
}
struct User: Identifiable {
let id: Int
let name: String
}
// Protocol-oriented design
protocol Drawable { func draw() }
extension Drawable where Self: Identifiable, Self.ID == Int {
func describe() -> String { "drawable id=\(id)" }
}
// Generic constraints
func first<C: Collection>(_ c: C) -> C.Element? where C.Element: Equatable {
c.first
}
// Existential ('any') vs opaque ('some')
func makeShape() -> some Drawable { Circle() } // concrete type, hidden
func loadShapes() -> [any Drawable] { [Circle(), Square()] } // heterogeneous
```
Prefer `some` over `any` — opaque types preserve type identity for the compiler (no boxing). Use `any` only when truly heterogeneous.
## Codable
```swift
struct User: Codable {
let id: Int
let name: String
let email: String?
let createdAt: Date
enum CodingKeys: String, CodingKey {
case id, name, email
case createdAt = "created_at"
}
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
decoder.keyDecodingStrategy = .convertFromSnakeCase // alternative to CodingKeys
let user = try decoder.decode(User.self, from: jsonData)
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let data = try encoder.encode(user)
```
For BHODL-style FFI, prefer Codable for IPC payloads and `JSONEncoder/Decoder` over manual parsing.
## Error Handling
```swift
enum WalletError: Error {
case insufficientFunds(Amount)
case invalidAddress(String)
}
func send(amount: Amount, to address: String) throws -> Transaction {
guard isValid(address) else { throw WalletError.invalidAddress(address) }
guard hasFunds(amount) else { throw WalletError.insufficientFunds(amount) }
return buildTx(amount, address)
}
// Call site
do {
let tx = try send(amount: 1000, to: "bc1q...")
print(tx)
} catch WalletError.insufficientFunds(let need) {
print("need \(need)")
} catch {
print("other error: \(error)")
}
// try? returns optional, try! force-unwraps (avoid)
let tx = try? send(amount: 1000, to: address)
```
## Result Builders (DSLs)
Power SwiftUI's `body` and many Apple DSLs.
```swift
@resultBuilder
struct StringBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined(separator: "\n")
}
static func buildEither(first: String) -> String { first }
static func buildEither(second: String) -> String { second }
}
@StringBuilder
func greeting(name: String, formal: Bool) -> String {
"Hello"
if formal { "Mr/Ms \(name)" } else { name }
"Welcome"
}
```
## Property Wrappers
```swift
@propertyWrapper
struct Clamped<Value: Comparable> {
var value: Value
let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: Value {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Volume {
@Clamped(0...100) var level: Int = 50
}
```
Common in SwiftUI (`@State`, `@Binding`, `@StateObject`, `@Environment`, `@AppStorage`).
## Memory Management
ARC (Automatic Reference Counting). Watch for retain cycles.
```swift
class Node {
var children: [Node] = []
weak var parent: Node? // weak breaks cycle
}
class ViewModel {
private var task: Task<Void, Never>?
func start() {
task = Task { [weak self] in
guard let self else { return }
await self.load()
}
}
}
```
`weak` → optional, becomes nil when target deallocs. `unowned` → non-optional, crash on access if dealloc'd. Prefer `weak` unless lifetime guaranteed.
## Concurrency — Essentials
```swift
func fetchUser(id: Int) async throws -> User {
let (data, _) = try await URLSession.shared.data(from: url(id))
return try JSONDecoder().decode(User.self, from: data)
}
// Parallel
async let user = fetchUser(id: 1)
async let posts = fetchPosts(userId: 1)
let result = try await (user, posts)
// Sequential
let user = try await fetchUser(id: 1)
let posts = try await fetchPosts(userId: user.id)
```
See [concurrency.md](quick-ref/concurrency.md) for actors, Sendable, TaskGroup, AsyncStream, MainActor.
## Anti-Patterns
| Anti-pattern | Why it's bad | Correct approach |
|---|---|---|
| Force-unwrap `!` everywhere | Crash on nil | `guard let`, `if let`, `??` |
| `class` for value-like types | Surprising aliasing | Use `struct` |
| `Combine` for new code (post-Swift Concurrency) | Apple steering toward async/await | Prefer `AsyncSequence`/`AsyncStream` |
| `DispatchQueue.main.async` from async code | Use `@MainActor` | `MainActor.run { ... }` or annotate function |
| `weak var` everywhere | Hides ownership intent | Only break actual cycles |
| `try!` outside tests | Crash on error | `try?` or proper `do/catch` |
| String-based KVO/selectors | No type safety | Use `KeyPath`, `Combine`, async sequences |
| `print` for production logging | No filter, no privacy | Use `os.Logger` (`import os`) |
## Build & Tooling
```swift
// Package.swift (Swift Package Manager)
// swift-tools-version:6.0
import PackageDescription
let package = Package(
name: "MyLib",
platforms: [.iOS(.v17), .macOS(.v14)],
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.