axiom-swift-modern

Installation
SKILL.md

Modern Swift Idioms

Purpose

Claude frequently generates outdated Swift patterns from its training data. This skill corrects the most common ones — patterns that compile fine but use legacy APIs when modern equivalents are clearer, more efficient, or more correct.

Philosophy: "Don't repeat what LLMs already know — focus on edge cases, surprises, soft deprecations." (Paul Hudson)

Modern API Replacements

Old Pattern Modern Swift Since Why
Date() Date.now 5.6 Clearer intent
filter { }.count count(where:) 5.0 Single pass, no intermediate allocation
replacingOccurrences(of:with:) replacing(_:with:) 5.7 Swift native, no Foundation bridge
CGFloat Double 5.5 Implicit bridging; exceptions: optionals, inout, ObjC-bridged APIs
Task.sleep(nanoseconds:) Task.sleep(for: .seconds(1)) 5.7 Type-safe Duration API
DateFormatter() .formatted() / FormatStyle 5.5 No instance management, localizable by default
String(format: "%.2f", val) val.formatted(.number.precision(.fractionLength(2))) 5.5 Type-safe, localized
localizedCaseInsensitiveContains() localizedStandardContains() 5.0 Handles diacritics, ligatures, width variants
"\(firstName) \(lastName)" PersonNameComponents with .formatted() 5.5 Respects locale name ordering
"yyyy-MM-dd" with DateFormatter try Date(string, strategy: .iso8601) 5.6 Modern parsing (throws); use "y" not "yyyy" for display
contains() on user input localizedStandardContains() 5.0 Required for correct text search/filtering

Modern Syntax

Old Pattern Modern Swift Since
if let value = value { if let value { 5.7
Explicit return in single-expression Omit return; if/switch are expressions 5.9
Circle() in modifiers .circle (static member lookup) 5.5
import UIKit alongside import SwiftUI Often not needed — SwiftUI re-exports most UIKit/AppKit types. Retain for UIKit-only APIs (UIApplication, etc.) 5.5

Foundation Modernization

Old Pattern Modern Foundation Since
FileManager.default.urls(for: .documentDirectory, ...) URL.documentsDirectory 5.7
url.appendingPathComponent("file") url.appending(path: "file") 5.7
books.sorted { $0.author < $1.author } (repeated) Conform to Comparable, call .sorted()
"yyyy" in date format for display "y" — correct in all calendar systems

SwiftUI Convenience APIs Claude Misses

  • ContentUnavailableView.search(text: searchText) (iOS 17+) automatically includes the search term — no need to compose a custom string
  • LabeledContent in Forms (iOS 16+) provides consistent label alignment without manual HStack layout
  • confirmationDialog() must attach to triggering UI — Liquid Glass morphing animations depend on the source element

Swift 6.3 Concurrency Posture

Write Swift 6.3-first code, not Swift 5-era code. These defaults apply to ALL new Swift code, not just when concurrency errors appear.

Default Rationale
Assume strict concurrency and MainActor default isolation for app/UI modules Swift 6.3 language mode; Xcode 26+ default for new projects
Prefer async/await over GCD, DispatchGroup, and callback pyramids GCD is a bridge pattern for legacy APIs, not default architecture
Async does not mean background — use @concurrent (Swift 6.2+) to force off-main Async functions resume on the same actor they were called from
Prefer structured concurrency (async let, TaskGroup) over unstructured Task {} Structured tasks propagate cancellation and errors automatically
Do not use Task.detached unless there is a specific, stated reason Loses actor context, priority, and task-local values
Prefer Sendable structs/enums for data that crosses actor boundaries Value types are inherently safe to share
Use actors only for truly shared mutable state across concurrency domains Don't make every class an actor — UI code stays @MainActor
Treat @unchecked Sendable, @preconcurrency, nonisolated(unsafe) as temporary bridge tools Each should have a removal ticket, not be permanent
Do not add escape hatches just to silence compiler errors They hide data races that crash in production

For detailed patterns, decision trees, and error-specific guidance, see axiom-swift-concurrency.

Common Claude Hallucinations

These patterns appear frequently in Claude-generated code:

  1. Creates DateFormatter instances inline — Use .formatted() or FormatStyle instead. If a formatter must exist, make it static let.
  2. Uses DispatchQueue.main.async — Use @MainActor or MainActor.run. GCD is a bridge pattern, not a default.
  3. Uses DispatchQueue.global().async for background work — Use @concurrent (Swift 6.2+) or extract to an actor.
  4. Uses Task.detached to "make it background" — Use @concurrent. Task.detached loses actor context.
  5. Uses CGFloat for SwiftUI parametersDouble works everywhere since Swift 5.5 implicit bridging.
  6. Generates guard let x = x else — Use guard let x else shorthand.
  7. Returns explicitly in single-expression computed properties — Omit return.
  8. Spawns unstructured Task {} in loops — Use TaskGroup for dynamic parallel work.
  9. Adds @unchecked Sendable to silence warnings — Convert to actor or proper Sendable type.

Resources

Skills: axiom-swift-performance, axiom-swift-concurrency, axiom-swiftui-architecture

Related skills
Installs
70
GitHub Stars
888
First Seen
Mar 9, 2026