This skill should be used when the user asks to "write Swift code", "create a Swift type", "set up a Swift package", "review Swift code", "refactor Swift", "use async/await in Swift", "fix Swift style", "set up SwiftLint", "configure SwiftFormat", or when generating any Swift source code regardless of target platform. Provides modern Swift 6+ best practices covering type system, optionals, concurrency, error handling, protocols, generics, idiomatic patterns, and SwiftLint/SwiftFormat setup. Covers the language, standard library, and Foundation only — no platform UI frameworks; pair with `apple-app-development` for Apple app work.
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Modern Swift best practices for writing safe, expressive, and idiomatic code. Targets Swift 6+ — the language, standard library, and Foundation. Platform frameworks are out of scope, with one deliberate exception: references/concurrency.md ends with brief notes on migrating Combine pipelines to AsyncSequence, because that refactor lands in language-level code.
Applicable to all Swift targets: Apple platforms, server-side (Vapor, Hummingbird), CLI tools, cross-platform (Linux, Windows). For Apple platform and UI work (SwiftUI, app lifecycle, Xcode projects, widgets) use this skill together with apple-app-development, which owns the framework guidance.
references/type-system.md — Generics, protocols with associated types, opaque types (some), existentials (any), metatypes, @dynamicMemberLookup, @dynamicCallablereferences/concurrency.md — Actors, task groups, async sequences, Sendable, isolation, continuations, GCD migration, Combine-to-AsyncSequence migration notes (Apple targets)references/patterns.md — Property wrappers, result builders, key paths, Codable, extensions, copy-on-write, DSL designreferences/project-structure.md — Swift Package Manager setup, multi-target packages, build configurations, plugins, testing setupreferences/static-analysis.md — SwiftLint (configuration, rules, custom rules, auto-correct), SwiftFormat (configuration, formatting rules), combined setup, CI/CD integration, pre-commit hooksreferences/static-analysis.md) is adopted, it enforces the hard ceiling (function_body_length warn 50 / error 100).@_exported unless building a module facade.private or internal. Use public only at module boundaries.| Element | Convention | Example |
|---|---|---|
| Type / Protocol | PascalCase | UserProfile, Fetchable |
| Function / Property | camelCase | fetchUser(), isActive |
Constant (let at module scope) | camelCase | defaultTimeout |
| Enum case | camelCase | case loading, case success(Data) |
| Boolean | Prefix is, has, can, should | isValid, hasPermission |
| File name | PascalCase.swift | UserProfile.swift |
| Protocol | Adjective or -able/-ible suffix | Loadable, Configurable |
| Generic parameter | Descriptive or single uppercase | Element, T, Key, Value |
Follow the Swift API Design Guidelines:
distance(to:)), verb for mutating (sort()), -ed/-ing for non-mutating variant (sorted(), removing()).Use struct by default. Use class when you need identity, inheritance, or reference semantics. Use actor when you need thread-safe mutable state.
guard let user = fetchUser(id) else { return } // early exit — preferred
let displayName = user.nickname ?? user.name // nil coalescing
let uppercased = user.nickname.map { $0.uppercased() } // String?
let config = try optionalConfig ?? { throw AppError.notFound(resource: "Config") }()Rules:
if let value, guard let value, while let value, guard let foo, let bar — and write if let renamed = original only when intentionally binding to a different name.!) to silence the compiler; it trades a compile-time diagnostic for a runtime crash. The one acceptable use is a provable invariant, stated in a comment beside it.enum LoadingState<T> {
case idle
case loading
case loaded(T)
case failed(Error)
}Prefer enums over boolean flags for state modeling. Enables exhaustive switch.
protocol Repository {
associatedtype Entity
func fetch(id: UUID) async throws -> Entity
func save(_ entity: Entity) async throws
}
extension Array where Element: Identifiable {
func element(withID id: Element.ID) -> Element? {
first { $0.id == id }
}
}Rules:
Fetchable & Cacheable) over large monolithic protocols.For generics, opaque types, existentials, and advanced protocol patterns see references/type-system.md.
// Define domain errors
enum AppError: LocalizedError {
case networkUnavailable
case unauthorized
case notFound(resource: String)
var errorDescription: String? {
switch self {
case .networkUnavailable: "Network is unavailable"
case .unauthorized: "Authentication required"
case .notFound(let resource): "\(resource) not found"
}
}
}
// Use typed throws (Swift 6+)
func fetchUser(id: UUID) async throws(AppError) -> User { ... }
// Use Result for expected outcomes in callbacks
func validate(_ input: String) -> Result<ValidatedInput, ValidationError> { ... }Rules:
guard for preconditions with early exit.Error broadly unless at a top-level boundary.async throws over Result for async operations.LocalizedError for user-facing error messages.// async/await
func fetchUser(id: UUID) async throws -> User {
let data = try await networkClient.get("users/\(id)")
return try decode(data)
}
// Structured concurrency — parallel execution
func loadDashboard() async throws -> Dashboard {
async let profile = fetchProfile()
async let notifications = fetchNotifications()
return Dashboard(profile: try await profile, notifications: try await notifications)
}
// Actor — thread-safe mutable state
actor Cache<Key: Hashable, Value> {
private var storage: [Key: Value] = [:]
func get(_ key: Key) -> Value? { storage[key] }
func set(_ key: Key, value: Value) { storage[key] = value }
}Rules:
async let, TaskGroup. Avoid Task.detached unless truly needed.Task or actors.Sendable.Task.isCancelled or try Task.checkCancellation() to respond to cancellation.nonisolated explicitly when actor methods don't need isolation.For actors, task groups, async sequences, Sendable patterns, continuations, GCD migration, and Combine-to-AsyncSequence migration notes see references/concurrency.md.
filter, map(\.keyPath), reduce, Dictionary(grouping:by:)) over manual loops; use .lazy when chaining over large collections.$0, $1 only in short single-expression closures; name the parameters when the body is multi-line.async functions over completion-handler closures in new code; mark closures @Sendable when they cross concurrency boundaries.MyPackage/
├── Package.swift
├── Sources/
│ ├── MyLibrary/
│ │ ├── Models/
│ │ ├── Services/
│ │ └── Extensions/
│ └── MyExecutable/
│ └── main.swift
├── Tests/
│ └── MyLibraryTests/
└── Plugins/Rules:
For SPM setup, multi-target packages, build configurations, plugins, and testing see references/project-structure.md.
@Test("...") display name plus a short function name (fetchUser). XCTest: test_methodName_whenCondition_expectedResult, because XCTest discovers tests by the test prefix.protocol UserRepository {
func fetch(id: UUID) async throws -> User
}
// Production
struct RemoteUserRepository: UserRepository {
let client: HTTPClient
func fetch(id: UUID) async throws -> User { ... }
}
// Test
struct FakeUserRepository: UserRepository {
var stubbedUser: User?
func fetch(id: UUID) async throws -> User {
guard let user = stubbedUser else { throw AppError.notFound(resource: "User") }
return user
}
}import Testing
@Test("Fetches user by ID")
func fetchUser() async throws {
let repo = FakeUserRepository(stubbedUser: User(id: testID, name: "Alice"))
let user = try await repo.fetch(id: testID)
#expect(user.name == "Alice")
}
@Test("Throws when user not found")
func fetchUserNotFound() async {
let repo = FakeUserRepository(stubbedUser: nil)
await #expect(throws: AppError.self) {
try await repo.fetch(id: testID)
}
}Prefer Swift Testing (@Test, #expect) over XCTest for new code. Use XCTest when the project requires it or for UI/integration tests on Apple platforms.
| Mistake | Fix |
|---|---|
Force-unwrapping (!) without invariant | Use guard let, if let, ??, or optional chaining |
Catching Error broadly | Catch specific error types at appropriate boundaries |
Task { } without cancellation handling | Check Task.isCancelled or use Task.checkCancellation() |
Using GCD (DispatchQueue) in new code | Use Swift Concurrency (async/await, actors) |
Mutable var in struct properties without need | Default to let; use var only when mutation is required |
| Large protocols (>5 requirements) | Break into focused protocols; use composition |
Ignoring Sendable warnings | Conform value types or use actor isolation |
| Completion handlers in new code | Use async/await; wrap legacy APIs with withCheckedContinuation |
| Stringly-typed APIs | Use enums, phantom types, or value types for type safety |
Any / AnyObject without reason | Use generics or existentials with constraints |
Double for money | Use Decimal with explicit rounding |
| Hard-coded dependencies | Protocol-based DI via init injection |
#if directives for multi-target polymorphism | Use DI or separate file implementations per target; reserve #if for DEBUG, os(), canImport() |
provectus/awos-recruitment
8d9332c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.