Best for
- Use when working with Swift code.
Pyroxin/opinionated-claude-skills/opinionated-apple-development/skills/swift-programmer/SKILL.md
Swift-specific idioms, tooling, and philosophy. Use when working with Swift code. Emphasizes protocol-oriented programming, value semantics, strict concurrency (Swift 6+), and compile-time safety guarantees.
Decision brief
Related skills: - software-engineer — Core design principles and system architecture - macos-programmer — macOS-specific patterns when building Mac apps - test-driven-development — Testing philosophy and practices - functional-programmer — Functional paradigm principles (Swift s…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/Pyroxin/opinionated-claude-skills --skill "opinionated-apple-development/skills/swift-programmer"Inspect the Agent Skill "swift-programmer" from https://github.com/Pyroxin/opinionated-claude-skills/blob/10c332aaec223fb10f63174029c4803f77eac269/opinionated-apple-development/skills/swift-programmer/SKILL.md at commit 10c332aaec223fb10f63174029c4803f77eac269. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Swift 6 fundamental shift: From memory safety to compile-time data-race safety. Think in isolation domains, not threads.
Mental model shift: Think in isolation domains, not threads. Each domain (task, actor, global actor) executes serially with exclusive access to its state. Every piece of mutable state belongs to exactly one isolation domain at any time.
The Problem: Actor state can change at ANY await because other tasks can run on the actor while suspended.
Guarantee 1: @MainActor ONLY guarantees main thread execution for: - Async functions (always) - Sync functions called from @MainActor context
Anti-pattern: Using custom actors for SwiftUI observable objects causes race conditions between UI thread and actor's executor. Always use @MainActor for UI-related state.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 93/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 8 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
<skill_scope skill="swift-programmer"> Related skills:
software-engineer — Core design principles and system architecturemacos-programmer — macOS-specific patterns when building Mac appstest-driven-development — Testing philosophy and practicesfunctional-programmer — Functional paradigm principles (Swift supports FP)This skill covers Swift-specific idioms, tooling, and philosophy. It emphasizes protocol-oriented programming, value semantics, strict concurrency (Swift 6+), and compile-time safety guarantees. </skill_scope>
Version targeting: Use the latest Swift version available. For internal apps, don't support older versions. For open-source libraries, support N-1 or N-2 versions maximum.
Prefer: Protocol composition over inheritance, value semantics over reference semantics, static dispatch over dynamic dispatch.
<concurrency_fundamentals> Mental model shift: Think in isolation domains, not threads. Each domain (task, actor, global actor) executes serially with exclusive access to its state. Every piece of mutable state belongs to exactly one isolation domain at any time.
Suspension points at await: Task yields thread, executor may schedule different task, upon resumption may execute on different thread but always maintains same isolation domain. This separation of logical execution (isolation) from physical execution (threads) is critical.
Structured concurrency: Task hierarchies with automatic cancellation propagation. Use TaskGroup for parallel work with bounded lifetime. Avoid detached tasks except in exceptional circumstances.
Swift 6.2 Approachable Concurrency1: Code is single-threaded by default until you explicitly introduce parallelism with @concurrent attribute. Async functions stay on the calling actor unless explicitly offloaded. This eliminates most data-race errors for naturally sequential code.
</concurrency_fundamentals>
<actor_reentrancy>
The Problem: Actor state can change at ANY await because other tasks can run on the actor while suspended.
actor BankAccount {
var balance: Double = 0
func withdraw(_ amount: Double) async throws {
guard balance >= amount else { throw InsufficientFunds() }
// ⚠️ DANGER: Another task can run here
await processWithdrawal(amount)
balance -= amount // May violate guard above!
}
// ✅ Better: synchronous transaction
func withdrawSync(_ amount: Double) throws {
guard balance >= amount else { throw InsufficientFunds() }
balance -= amount // Atomic, no re-entrancy
}
}
Real-world example:
actor OrderProcessor {
var pendingOrders: [Order] = []
func processNextOrder() async {
guard !pendingOrders.isEmpty else { return }
let order = pendingOrders[0]
// ⚠️ DANGER: pendingOrders could change here
await performExpensiveProcessing(order)
// Crash if another task removed the order!
pendingOrders.removeFirst()
}
// ✅ Fix: Check state after suspension
func processNextOrderSafe() async {
guard !pendingOrders.isEmpty else { return }
let order = pendingOrders[0]
await performExpensiveProcessing(order)
// Re-check after suspension
if let index = pendingOrders.firstIndex(where: { $0.id == order.id }) {
pendingOrders.remove(at: index)
}
}
}
Pattern: Keep actor methods synchronous when possible. Compose with async wrappers. For async methods, re-validate state after every await.
</actor_reentrancy>
<mainactor_details>
Guarantee 1: @MainActor ONLY guarantees main thread execution for:
@MainActor contextGuarantee 2: Sync @MainActor functions called from non-isolated contexts CAN run on background threads in Swift 5 mode. Swift 6 mode catches this at compile time.
Example of the danger:
@MainActor
class ViewModel {
var state: Int = 0
func updateState() { // Sync method
state += 1
updateUI() // Assumes main thread
}
}
// Calling from background thread (Swift 5 mode):
Task.detached {
let vm = await ViewModel()
// ⚠️ This CAN run on background thread!
await vm.updateState() // Race condition possible
}
Swift 6.2 Isolated Conformances:
protocol Exportable {
func export()
}
// This conformance is tied to @MainActor
extension ViewModel: @MainActor Exportable {
func export() {
// Can safely use main-actor state
print(state)
}
}
// Compiler prevents non-main-actor usage
nonisolated func process(_ item: any Exportable) {
item.export() // ❌ Error: Cannot use @MainActor conformance
}
@MainActor func process(_ item: any Exportable) {
item.export() // ✅ OK: We're on main actor
}
Opt-out with nonisolated:
@MainActor class ViewModel {
var title: String = ""
nonisolated func heavyComputation() async -> Data {
// Runs on global concurrent pool
return await performExpensiveWork()
}
// ❌ Cannot access main-actor state from nonisolated
nonisolated func broken() {
print(title) // Error: main-actor property access
}
}
Swift 6.2 Default Main Actor Mode: Enable -default-isolation MainActor build flag to infer @MainActor on all types by default. Opt out specific functions with @concurrent or nonisolated.
</mainactor_details>
<isolation_decision>
| Use Case | Type | Why |
|---|---|---|
| UI updates, SwiftUI state | @MainActor class | Must run on main thread |
| SwiftUI observable objects | @MainActor class with @Observable | Required for SwiftUI reactivity |
| Shared mutable state (non-UI) | actor | Custom serialization domain |
| Parallel workers, background tasks | actor | Serialize access to worker state |
| No mutable shared state | class or struct | No isolation needed |
| Explicitly parallel work | @concurrent func | Offload to background pool |
| Global singletons (UI) | @MainActor class with static let | Ensure singleton thread-safe |
| Global singletons (non-UI) | actor with static let | Serialize singleton access |
| ❌ SwiftUI data models | Never actor | Forces UI updates off main thread → crashes |
| ❌ SwiftUI view state | Never plain class | Use @MainActor class + @Observable |
Anti-pattern: Using custom actors for SwiftUI observable objects causes race conditions between UI thread and actor's executor. Always use @MainActor for UI-related state.
</isolation_decision>
<sendable_deep> Sendable types (safe to share across isolation domains):
let) Sendable properties@SendableCompiler inference: Non-public types get Sendable inferred if they meet requirements. Public types require explicit conformance.
@unchecked Sendable for manually synchronized types:
import Synchronization
final class ThreadSafeCache: @unchecked Sendable {
private let cache = Mutex<[String: Data]>([:])
func get(_ key: String) -> Data? {
cache.withLock { $0[key] }
}
func set(_ key: String, _ value: Data) {
cache.withLock { $0[key] = value }
}
}
Common Sendable violations:
// ❌ Non-final class can't be Sendable (inheritance issues)
class Config: Sendable { // Error
let value: String = ""
}
// ✅ Make it final
final class Config: Sendable {
let value: String = ""
}
// ❌ Mutable property on Sendable
final class Counter: Sendable { // Error
var count: Int = 0
}
// ✅ Use actor instead
actor Counter {
var count: Int = 0
}
// ❌ Non-Sendable property
final class ViewModel: Sendable { // Error
let cache: NSCache<NSString, NSData> // NSCache not Sendable
}
// ✅ Use @unchecked if you know it's safe
final class ViewModel: @unchecked Sendable {
let cache: NSCache<NSString, NSData>
// Document why this is safe
}
@Sendable closures:
func processAsync(_ handler: @Sendable @escaping () -> Void) {
Task {
handler()
}
}
// ❌ Capturing mutable variable
var count = 0
processAsync {
count += 1 // Error: capture of 'count' in @Sendable closure
}
// ✅ Capture immutable copy
var count = 0
count += 1
processAsync { [count] in
print(count) // OK: captured by value
}
// ❌ Capturing non-Sendable type
class NotSendable { }
let obj = NotSendable()
processAsync {
obj.doSomething() // Error: capture non-Sendable in @Sendable
}
// ✅ Make type Sendable or use nonisolated(unsafe)
nonisolated(unsafe) let obj = NotSendable()
processAsync {
obj.doSomething() // OK but unsafe - you ensure safety
}
</sendable_deep>
<region_isolation> SE-0414 Region-based Isolation2: Allows safe transfer of non-Sendable values when ownership is provably transferred.
SE-0430 sending Keyword3: Explicitly marks parameters/returns as transferring ownership across isolation boundaries.
class NonSendable {
var data: String = ""
}
actor DataProcessor {
// `consuming` means ownership is transferred IN
func process(_ item: consuming NonSendable) {
// Safe: item transferred into actor's region
item.data = "processed"
}
// `sending` means ownership is transferred OUT
func create() -> sending NonSendable {
let item = NonSendable()
item.data = "created"
return item // Transferred to caller
}
}
func use() async {
let item = NonSendable()
await processor.process(item)
// item no longer accessible - ownership transferred
// print(item.data) // Error: use after transfer
let newItem = await processor.create()
// newItem is now owned by this isolation domain
print(newItem.data) // OK
}
Use when: Passing large non-Sendable objects between actors without copying, implementing ownership transfer semantics, avoiding Sendable conformance for complex types.
See local docs: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/doc/swift/diagnostics/sending-risks-data-race.md
</region_isolation>
<concurrent_attribute> Purpose4: Explicitly offload async work to background thread pool, freeing up the calling actor.
Before @concurrent (Swift 6.0-6.1):
@MainActor
class ViewModel {
func loadData() async {
// This runs on main actor, blocking UI
let data = await performExpensiveWork()
self.data = data
}
}
With @concurrent (Swift 6.2):
class ImageProcessor {
// Always runs on concurrent thread pool
@concurrent
static func processImage(_ data: Data) async -> Image {
// Expensive work runs in parallel
return performExpensiveProcessing(data)
}
}
@MainActor
class ViewModel {
func loadData() async {
// Work offloaded to background
let processed = await ImageProcessor.processImage(rawData)
// Back on main actor for this line
self.data = processed
}
}
When to use:
When NOT to use:
See local docs: /Applications/Xcode.app/Contents/PlugIns/IDEIntelligenceChat.framework/Versions/A/Resources/AdditionalDocumentation/Swift-Concurrency-Updates.md
</concurrent_attribute>
<migration_errors> Error 1: "Stored property 'X' of 'Sendable'-conforming class is mutable"
// ❌ Problem
final class Config: Sendable {
var timeout: TimeInterval = 30
}
// ✅ Fix 1: Make immutable
final class Config: Sendable {
let timeout: TimeInterval
}
// ✅ Fix 2: Use actor
actor Config {
var timeout: TimeInterval = 30
}
Error 2: "Capture of 'X' with non-Sendable type in @Sendable closure"
class ViewModel {
func process() {
Task {
self.update() // ❌ Error: non-Sendable capture
}
}
}
// ✅ Fix 1: Make type Sendable
@MainActor
final class ViewModel {
func process() {
Task { @MainActor in
self.update() // OK: isolated to main actor
}
}
}
// ✅ Fix 2: Use weak capture
class ViewModel {
func process() {
Task { [weak self] in
await self?.update()
}
}
}
Error 3: "Call to main actor-isolated instance method 'X' in synchronous nonisolated context"
@MainActor
class ViewModel {
func update() { }
}
func caller(vm: ViewModel) {
vm.update() // ❌ Error: sync call to main-actor method
}
// ✅ Fix 1: Make caller async
func caller(vm: ViewModel) async {
await vm.update()
}
// ✅ Fix 2: Isolate caller to main actor
@MainActor
func caller(vm: ViewModel) {
vm.update() // OK: both on main actor
}
Error 4: "Static property 'shared' is not concurrency-safe"
class Singleton {
static let shared = Singleton() // ❌ Error
}
// ✅ Fix 1: Isolate to main actor
@MainActor
class Singleton {
static let shared = Singleton()
}
// ✅ Fix 2: Use actor
actor Singleton {
static let shared = Singleton()
}
// ✅ Fix 3: Make Sendable
final class Singleton: Sendable {
static let shared = Singleton()
// Must have no mutable state
}
Use @preconcurrency import for unmigrated dependencies:
@preconcurrency import ThirdPartySDK
// Suppresses Sendable warnings from ThirdPartySDK
See local docs: All files in /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/doc/swift/diagnostics/
</migration_errors>
<protocol_oriented> Core principle: "Don't start with a class, start with a protocol."[^abrahams-pop]
When to use protocols:
When to use classes:
super implementationsProtocol extensions = mixins: Default implementations enable code reuse without inheritance.
Anti-pattern from OOP: Treating protocols as "interfaces" with no default implementations. This recreates OOP hierarchy problems.
Dispatch gotcha: Extension methods without protocol requirement = static dispatch (compile-time type). With requirement = dynamic dispatch. </protocol_oriented>
<value_vs_reference> Apple's guidance: "Prefer structs over classes unless you need reference semantics."5
Decision tree:
Need identity semantics (object lifetime matters)? → class
Need shared mutable state? → class (careful!)
Need stored property inheritance? → class
Everything else → struct
Performance myth: "Classes are faster because pointers." Reality: Value types often faster—stack allocation, no ref-counting, passed in registers when small.
Best practice: Avoid value types with inner references—violates value semantics and adds ref-counting overhead. </value_vs_reference>
<error_handling>
| Mechanism | Use When |
|---|---|
throws | Recoverable errors with context |
Result<T, E> | Async callbacks, deferred handling |
Optional | Simple absence, no error details needed |
Swift 6 typed throws:
enum FileError: Error { case notFound, permissionDenied }
func loadFile(_ path: String) throws(FileError) -> Data {
guard fileExists(path) else { throw .notFound }
return try Data(contentsOf: URL(fileURLWithPath: path))
}
// Caller: error is FileError, not 'any Error'
</error_handling>
Swift Package Manager: Three-layer architecture (Core ← Domain ← Features). Unidirectional dependencies.
Testing: Prefer Swift Testing for new unit tests (native async, #expect macro, parameterized tests). XCTest required for UI/performance tests.
Documentation: DocC mandatory for all public APIs. Use ``Symbol`` links. Document complexity when not O(1).
Configuration files: See local .swiftlint.yml and .swiftformat in projects for standard configs.
<common_mistakes> From Java/C#: Using classes for everything → Use structs by default
From Python/JavaScript: Using Any extensively → Leverage generics and protocols
From C/C++: Using UnsafePointer unnecessarily → Let ARC work, use weak/unowned for cycles
From Rust: Over-applying ownership patterns → Trust Swift's ARC + copy-on-write
From Objective-C: Using NSString, NSArray, NSDictionary → Use Swift native types
From any OOP: Creating deep inheritance hierarchies → Use protocol composition </common_mistakes>
<memory_gotchas> Retain cycles: Parent ↔ child, closures capturing self, delegate patterns
Closure capture lists:
{ [weak self] in
guard let self else { return }
// Use self
}
Weak vs Unowned:
weak: Optional, auto-nil when deallocated (safe)unowned: Non-optional, NOT auto-nil (crashes if accessed after dealloc)Rule: Prefer weak unless lifetime relationship is absolutely certain.
</memory_gotchas>
<api_design> Apple's three pillars6:
Naming:
x.distance(to: y)x.sort()sort()/sorted(), append(_:)/appending(_:)makeIterator()Access control: Default to private, use internal for module-wide, public/open only for framework APIs.
Source: https://www.swift.org/documentation/api-design-guidelines/ </api_design>
<swift_6_2> InlineArray7: Fixed-size arrays with stack allocation, no heap, no ref-counting. Use for performance-critical fixed-size collections.
Span8: Safe contiguous memory access without unsafe pointers. Compile-time lifetime checking prevents use-after-free.
Isolated conformances: Protocol conformances can be tied to specific actors (e.g., @MainActor Exportable).
@concurrent attribute: Explicitly offload async work to background thread pool.
Default main actor mode: Opt-in -default-isolation MainActor for mostly-single-threaded apps.
</swift_6_2>
<third_party> When contributing to open-source Swift:
You're a guest—respect house rules. </third_party>
<local_docs> Xcode ships with LLM-optimized Swift documentation:
Swift Diagnostic Docs (compiler errors):
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/doc/swift/diagnostics/sendable-closure-captures.md, actor-isolated-call.md, 30+ othersLLM-Optimized Framework Guides:
/Applications/Xcode.app/Contents/PlugIns/IDEIntelligenceChat.framework/Versions/A/Resources/AdditionalDocumentation/Swift-Concurrency-Updates.md, Swift-InlineArray-Span.md, framework integration guidesThe Swift Programming Language Book:
Tooling:
Style Guides:
Swift Project. 2025. Approachable Concurrency Vision Document. Swift Evolution. https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md ↩
Michael Gottesman, et al. 2024. SE-0414: Region-based Isolation. Swift Evolution. https://github.com/swiftlang/swift-evolution/blob/main/proposals/0414-region-based-isolation.md ↩
Michael Gottesman, et al. 2024. SE-0430: sending parameter and result values. Swift Evolution. https://github.com/swiftlang/swift-evolution/blob/main/proposals/0430-transferring-parameters-and-results.md ↩
Holly Borla, et al. 2025. SE-0461: Run nonisolated async functions on the caller's actor by default. Swift Evolution. https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md ↩
Apple Inc. Choosing Between Structures and Classes. Swift Documentation. https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes ↩
Apple Inc. Swift API Design Guidelines. https://www.swift.org/documentation/api-design-guidelines/ ↩
Alejandro Alonso, et al. 2025. SE-0453: Vector (InlineArray). Swift Evolution. https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md ↩
Guillaume Lessard, et al. 2024. SE-0447: Span: Safe Access to Contiguous Storage. Swift Evolution. https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md ↩
Frequently asked questions
Related skills: - software-engineer — Core design principles and system architecture - macos-programmer — macOS-specific patterns when building Mac apps - test-driven-development — Testing philosophy and practices - functional-programmer — Functional paradigm principles (Swift s…
The source record exposes this install command: npx skills add https://github.com/Pyroxin/opinionated-claude-skills --skill "opinionated-apple-development/skills/swift-programmer". Inspect the command and pinned source before running it.
Alternatives
coreyhaines31/marketingskills
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program
oaustegard/claude-skills
Generate hierarchical _FEATURES.md files that describe what a codebase DOES from a user/consumer perspective, anchored to source symbols via tree-sitting. Supports large complex codebases through feature-driven decomposition into sub-feature files. Uses a multi-pass synthesis: orientation → detail → overview rewrite. Use when someone says "what does this do", "document features", "feature inventory", "_FEATURES.md", or needs to understand a codebase's purpose before modifying it. Complements tre
narrative-io/narrative-skills-marketplace
Translate a fuzzy analytical question into a rigorous investigation plan. Interrogates the ask, grounds the plan in the available data dictionary, applies analytical best practices, and produces a structured brief of query specifications for a downstream query-writing skill. Plans, does not write SQL. Use when: "why did X drop", "is there a relationship between A and B", "who are our highest-value customers", "what's driving the change in Y", "investigate this trend", "design an analysis for", "
event4u-app/agent-config
Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.