Swift Best Practices Skill
Overview
Apply modern Swift development best practices focusing on Swift 6+ features, concurrency safety, API design principles, and code quality guidelines for iOS and macOS projects targeting macOS 15.7+.
When to Use This Skill
Use this skill when:
- Writing new Swift code for iOS or macOS applications
- Reviewing Swift code for correctness, safety, and style
- Implementing Swift concurrency features (async/await, actors, MainActor)
- Designing Swift APIs and public interfaces
- Migrating code from Swift 5 to Swift 6
- Addressing concurrency warnings, data race issues, or compiler errors related to Sendable/isolation
- Working with modern Swift language features introduced in Swift 6 and 6.2
SwiftLens MCP Integration (Claude Code)
This skill complements SwiftLens MCP server for semantic-level Swift code analysis.
What SwiftLens Provides:
- 15 tools for semantic Swift analysis using Apple's SourceKit-LSP
- Symbol lookup, cross-file references, type information
- Safe code modification and refactoring
- Compiler-grade understanding of Swift code structure
What This Skill Provides:
- Swift 6+ design patterns and best practices
- Concurrency strategies (async/await, actors, MainActor)
- API design guidelines and naming conventions
- Migration guidance (Swift 5 → Swift 6)
Setup for Claude Code CLI:
Create .claude/mcps/swiftlens.json in your Swift project:
{
"mcpServers": {
"swiftlens": {
"description": "SwiftLens MCP provides semantic Swift analysis via SourceKit-LSP",
"command": "uvx",
"args": ["swiftlens"]
}
}
}
⚠️ Note: This is Claude Code configuration (not Claude Desktop). See references/swiftlens-mcp-claude-code.md for complete setup guide, all 15 tools, index building, and usage examples.
Workflow: SwiftLens provides runtime analysis (what the code is doing), this skill provides design expertise (what the code should be doing).
Core Guidelines
Fundamental Principles
- Clarity at point of use is paramount - evaluate designs by examining use cases, not just declarations
- Clarity over brevity - compact code comes from the type system, not minimal characters
- Write documentation for every public declaration - if you can't describe functionality simply, the API may be poorly designed
- Name by role, not type -
var greeting = "Hello" not var string = "Hello"
- Favour elegance through simplicity - avoid over-engineering unless complexity genuinely warrants it
Swift 6 Concurrency Model
Swift 6 enables complete concurrency checking by default with region-based isolation (SE-0414). The compiler now proves code safety, eliminating many false positives whilst catching real concurrency issues at compile time.
Critical understanding:
- Async ≠ background - async functions can suspend but don't automatically run on background threads
- Actors protect mutable shared state through automatic synchronisation
@MainActor ensures UI-related code executes on the main thread
- Global actor-isolated types are automatically
Sendable
Essential Patterns
Async/Await
// Parallel execution with async let
func fetchData() async -> (String, Int) {
async let stringData = fetchString()
async let intData = fetchInt()
return await (stringData, intData)
}
// Always check cancellation in long-running operations
func process(_ items: [Item]) async throws -> [Result] {
var results: [Result] = []
for item in items {
try Task.checkCancellation()
results.append(await process(item))
}
return results
}
MainActor for UI Code
// Apply at type level for consistent isolation
@MainActor
class ContentViewModel: ObservableObject {
@Published var images: [UIImage] = []
func fetchData() async throws {
self.images = try await fetchImages()
}
}
// Avoid MainActor.run when direct await works
await doMainActorStuff() // Good
await MainActor.run { doMainActorStuff() } // Unnecessary
Actor Isolation
actor DataCache {
private var cache: [String: Data] = [:]
func store(_ data: Data, forKey key: String) {
cache[key] = data // No await needed inside actor
}
nonisolated func cacheType() -> String {
return "DataCache" // No await needed - doesn't access isolated state
}
}
Common Pitfalls to Avoid
- Don't mark functions as
async unnecessarily - async calling convention has overhead
- Never use
DispatchSemaphore with async/await - risk of deadlock
- Don't create stateless actors - use non-isolated async functions instead
- Avoid split isolation - don't mix isolation domains within one type
- Check task cancellation - long operations must check
Task.checkCancellation()
- Don't assume async means background - explicitly move work to background if needed
- Avoid excessive context switching - group operations within same isolation domain
API Design Quick Reference
Naming Conventions
- Types/protocols:
UpperCamelCase
- Everything else:
lowerCamelCase
- Protocols describing capabilities:
-able, -ible, -ing suffixes (Equatable, ProgressReporting)
- Factory methods: Begin with
make (x.makeIterator())
- Mutating pairs: imperative vs past participle (
x.sort() / x.sorted())
Method Naming by Side Effects
- No side effects: Noun phrases (
x.distance(to: y))
- With side effects: Imperative verbs (
x.append(y), x.sort())
Argument Labels
- Omit when arguments can't be distinguished:
min(number1, number2)
- Value-preserving conversions omit first label:
Int64(someUInt32)
- Prepositional phrases label at preposition:
x.removeBoxes(havingLength: 12)
- Label all other arguments
Swift 6 Breaking Changes
Must Explicitly Mark Types with @MainActor (SE-0401)
Property wrappers no longer infer actor isolation automatically.
@MainActor
struct LogInView: View {
@StateObject private var model = ViewModel()
}
Global Variables Must Be Concurrency-Safe (SE-0412)
static let config = Config() // Constant - OK
@MainActor static var state = State() // Actor-isolated - OK
nonisolated(unsafe) var cache = [String: Data]() // Unsafe - use with caution
Other Changes
@UIApplicationMain/@NSApplicationMain deprecated (use @main)
any required for existential types
- Import visibility requires explicit access control
API Availability Patterns
// Basic availability
@available(macOS 15, iOS 18, *)
func modernAPI() { }
// Deprecation with message
@available(*, deprecated, message: "Use newMethod() instead")
func oldMethod() { }
// Renaming with auto-fix
@available(*, unavailable, renamed: "newMethod")
func oldMethod() { }
// Runtime checking
if #available(iOS 18, *) {
// iOS 18+ code
}
// Inverted checking (Swift 5.6+)
if #unavailable(iOS 18, *) {
// iOS 17 and lower
}
Key differences:
deprecated - Warning, allows usage
obsoleted - Error from specific version
unavailable - Error, completely prevents usage
How to Use This Skill
When Writing Code
- Apply naming conventions following role-based, clarity-first principles
- Use appropriate isolation (
@MainActor for UI, actors for mutable state)
- Implement async/await patterns correctly with proper cancellation handling
- Follow Swift 6 concurrency model - trust compiler's flow analysis
- Document public APIs with clear, concise summaries
When Reviewing Code
- Check for concurrency safety violations
- Verify proper actor isolation and Sendable conformance
- Ensure async functions handle cancellation appropriately
- Validate API naming follows Swift guidelines
- Confirm availability annotations are correct for target platforms
Code Quality Standards
- Minimise comments - code should be self-documenting where possible
- Avoid over-engineering and unnecessary abstractions
- Use meaningful variable names based on role, not type
- Follow established project architecture and patterns
- Prefer
count(where:) over filter().count
- Use
InlineArray for fixed-size, performance-critical data
- Trust compiler's concurrency flow analysis - avoid unnecessary
Sendable conformances
Resources
references/
Detailed reference material to load when in-depth information is needed:
- swiftlens-mcp-claude-code.md - SwiftLens MCP server setup for Claude Code CLI, 15 semantic analysis tools, index building, usage examples, and integration workflows
- api-design.md - Complete API design conventions, documentation standards, parameter guidelines, and naming patterns
- concurrency.md - Detailed async/await patterns, actor best practices, common pitfalls, performance considerations, and thread safety patterns
- swift6-features.md - New language features in Swift 6/6.2, breaking changes, migration strategies, and modern patterns
- availability-patterns.md - Comprehensive
@available attribute usage, deprecation strategies, and platform version management
Load these references when detailed information is needed beyond the core guidelines provided above.
Platform Requirements
- Swift 6.0+ compiler for Swift 6 features
- Swift 6.2+ for InlineArray and enhanced concurrency features
- macOS 15.7+ with appropriate SDK
- iOS 18+ for latest platform features
- Use
#available for runtime platform detection
- Use
@available for API availability marking
1---2name: swift-best-practices3description: This skill should be used when writing or reviewing Swift code for iOS or macOS projects. Apply modern Swift 6+ best practices, concurrency patterns, API design guidelines, and migration strategies. Covers async/await, actors, MainActor, Sendable, typed throws, and Swift 6 breaking changes. Keywords: concurrency, async-await, actors, Sendable, typed-throws, Swift-6, migration, data-races, MainActor, nonisolated, isolated, iOS, macOS, SwiftUI, Combine, Swift-concurrency, actor-isolation, strict-concurrency, Swift-migration, modern-Swift, Swift-evolution, code-review, Swift-patterns, Apple-platforms, Xcode, iOS-development, macOS-development4license: MIT5---67# Swift Best Practices Skill89## Overview1011Apply modern Swift development best practices focusing on Swift 6+ features, concurrency safety, API design principles, and code quality guidelines for iOS and macOS projects targeting macOS 15.7+.1213## When to Use This Skill1415Use this skill when:16- Writing new Swift code for iOS or macOS applications17- Reviewing Swift code for correctness, safety, and style18- Implementing Swift concurrency features (async/await, actors, MainActor)19- Designing Swift APIs and public interfaces20- Migrating code from Swift 5 to Swift 621- Addressing concurrency warnings, data race issues, or compiler errors related to Sendable/isolation22- Working with modern Swift language features introduced in Swift 6 and 6.22324## SwiftLens MCP Integration (Claude Code)2526This skill complements **SwiftLens MCP server** for semantic-level Swift code analysis.2728**What SwiftLens Provides:**29- 15 tools for semantic Swift analysis using Apple's SourceKit-LSP30- Symbol lookup, cross-file references, type information31- Safe code modification and refactoring32- Compiler-grade understanding of Swift code structure3334**What This Skill Provides:**35- Swift 6+ design patterns and best practices36- Concurrency strategies (async/await, actors, MainActor)37- API design guidelines and naming conventions38- Migration guidance (Swift 5 → Swift 6)3940**Setup for Claude Code CLI:**4142Create `.claude/mcps/swiftlens.json` in your Swift project:4344```json45{46 "mcpServers": {47 "swiftlens": {48 "description": "SwiftLens MCP provides semantic Swift analysis via SourceKit-LSP",49 "command": "uvx",50 "args": ["swiftlens"]51 }52 }53}54```5556**⚠️ Note**: This is **Claude Code** configuration (not Claude Desktop). See `references/swiftlens-mcp-claude-code.md` for complete setup guide, all 15 tools, index building, and usage examples.5758**Workflow**: SwiftLens provides **runtime analysis** (what the code is doing), this skill provides **design expertise** (what the code should be doing).5960## Core Guidelines6162### Fundamental Principles63641. **Clarity at point of use** is paramount - evaluate designs by examining use cases, not just declarations652. **Clarity over brevity** - compact code comes from the type system, not minimal characters663. **Write documentation for every public declaration** - if you can't describe functionality simply, the API may be poorly designed674. **Name by role, not type** - `var greeting = "Hello"` not `var string = "Hello"`685. **Favour elegance through simplicity** - avoid over-engineering unless complexity genuinely warrants it6970### Swift 6 Concurrency Model7172Swift 6 enables complete concurrency checking by default with region-based isolation (SE-0414). The compiler now proves code safety, eliminating many false positives whilst catching real concurrency issues at compile time.7374**Critical understanding:**75- **Async ≠ background** - async functions can suspend but don't automatically run on background threads76- Actors protect mutable shared state through automatic synchronisation77- `@MainActor` ensures UI-related code executes on the main thread78- Global actor-isolated types are automatically `Sendable`7980### Essential Patterns8182#### Async/Await83```swift84// Parallel execution with async let85func fetchData() async -> (String, Int) {86 async let stringData = fetchString()87 async let intData = fetchInt()88 return await (stringData, intData)89}9091// Always check cancellation in long-running operations92func process(_ items: [Item]) async throws -> [Result] {93 var results: [Result] = []94 for item in items {95 try Task.checkCancellation()96 results.append(await process(item))97 }98 return results99}100```101102#### MainActor for UI Code103```swift104// Apply at type level for consistent isolation105@MainActor106class ContentViewModel: ObservableObject {107 @Published var images: [UIImage] = []108109 func fetchData() async throws {110 self.images = try await fetchImages()111 }112}113114// Avoid MainActor.run when direct await works115await doMainActorStuff() // Good116await MainActor.run { doMainActorStuff() } // Unnecessary117```118119#### Actor Isolation120```swift121actor DataCache {122 private var cache: [String: Data] = [:]123124 func store(_ data: Data, forKey key: String) {125 cache[key] = data // No await needed inside actor126 }127128 nonisolated func cacheType() -> String {129 return "DataCache" // No await needed - doesn't access isolated state130 }131}132```133134### Common Pitfalls to Avoid1351361. **Don't mark functions as `async` unnecessarily** - async calling convention has overhead1372. **Never use `DispatchSemaphore` with async/await** - risk of deadlock1383. **Don't create stateless actors** - use non-isolated async functions instead1394. **Avoid split isolation** - don't mix isolation domains within one type1405. **Check task cancellation** - long operations must check `Task.checkCancellation()`1416. **Don't assume async means background** - explicitly move work to background if needed1427. **Avoid excessive context switching** - group operations within same isolation domain143144### API Design Quick Reference145146#### Naming Conventions147- Types/protocols: `UpperCamelCase`148- Everything else: `lowerCamelCase`149- Protocols describing capabilities: `-able`, `-ible`, `-ing` suffixes (`Equatable`, `ProgressReporting`)150- Factory methods: Begin with `make` (`x.makeIterator()`)151- Mutating pairs: imperative vs past participle (`x.sort()` / `x.sorted()`)152153#### Method Naming by Side Effects154- No side effects: Noun phrases (`x.distance(to: y)`)155- With side effects: Imperative verbs (`x.append(y)`, `x.sort()`)156157#### Argument Labels158- Omit when arguments can't be distinguished: `min(number1, number2)`159- Value-preserving conversions omit first label: `Int64(someUInt32)`160- Prepositional phrases label at preposition: `x.removeBoxes(havingLength: 12)`161- Label all other arguments162163### Swift 6 Breaking Changes164165#### Must Explicitly Mark Types with @MainActor (SE-0401)166Property wrappers no longer infer actor isolation automatically.167168```swift169@MainActor170struct LogInView: View {171 @StateObject private var model = ViewModel()172}173```174175#### Global Variables Must Be Concurrency-Safe (SE-0412)176```swift177static let config = Config() // Constant - OK178@MainActor static var state = State() // Actor-isolated - OK179nonisolated(unsafe) var cache = [String: Data]() // Unsafe - use with caution180```181182#### Other Changes183- `@UIApplicationMain`/`@NSApplicationMain` deprecated (use `@main`)184- `any` required for existential types185- Import visibility requires explicit access control186187### API Availability Patterns188189```swift190// Basic availability191@available(macOS 15, iOS 18, *)192func modernAPI() { }193194// Deprecation with message195@available(*, deprecated, message: "Use newMethod() instead")196func oldMethod() { }197198// Renaming with auto-fix199@available(*, unavailable, renamed: "newMethod")200func oldMethod() { }201202// Runtime checking203if #available(iOS 18, *) {204 // iOS 18+ code205}206207// Inverted checking (Swift 5.6+)208if #unavailable(iOS 18, *) {209 // iOS 17 and lower210}211```212213**Key differences:**214- `deprecated` - Warning, allows usage215- `obsoleted` - Error from specific version216- `unavailable` - Error, completely prevents usage217218## How to Use This Skill219220### When Writing Code2212221. Apply naming conventions following role-based, clarity-first principles2232. Use appropriate isolation (`@MainActor` for UI, actors for mutable state)2243. Implement async/await patterns correctly with proper cancellation handling2254. Follow Swift 6 concurrency model - trust compiler's flow analysis2265. Document public APIs with clear, concise summaries227228### When Reviewing Code2292301. Check for concurrency safety violations2312. Verify proper actor isolation and Sendable conformance2323. Ensure async functions handle cancellation appropriately2334. Validate API naming follows Swift guidelines2345. Confirm availability annotations are correct for target platforms235236### Code Quality Standards237238- Minimise comments - code should be self-documenting where possible239- Avoid over-engineering and unnecessary abstractions240- Use meaningful variable names based on role, not type241- Follow established project architecture and patterns242- Prefer `count(where:)` over `filter().count`243- Use `InlineArray` for fixed-size, performance-critical data244- Trust compiler's concurrency flow analysis - avoid unnecessary `Sendable` conformances245246## Resources247248### references/249250Detailed reference material to load when in-depth information is needed:251252- **swiftlens-mcp-claude-code.md** - SwiftLens MCP server setup for Claude Code CLI, 15 semantic analysis tools, index building, usage examples, and integration workflows253- **api-design.md** - Complete API design conventions, documentation standards, parameter guidelines, and naming patterns254- **concurrency.md** - Detailed async/await patterns, actor best practices, common pitfalls, performance considerations, and thread safety patterns255- **swift6-features.md** - New language features in Swift 6/6.2, breaking changes, migration strategies, and modern patterns256- **availability-patterns.md** - Comprehensive `@available` attribute usage, deprecation strategies, and platform version management257258Load these references when detailed information is needed beyond the core guidelines provided above.259260## Platform Requirements261262- Swift 6.0+ compiler for Swift 6 features263- Swift 6.2+ for InlineArray and enhanced concurrency features264- macOS 15.7+ with appropriate SDK265- iOS 18+ for latest platform features266- Use `#available` for runtime platform detection267- Use `@available` for API availability marking