Skip to content
Back to skills

Swift Coding Standards

ASecurity

Production-ready Swift coding standards for iOS/macOS development enforcing safety, clarity, and modern patterns. Use when enforcing swift coding conventions and style rules.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
developmentgoswiftexpresstestinggitapidocumentation

Works with

  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill swift-coding-standards --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Swift Coding Standards?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Swift Coding Standards
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-swift-coding-standards/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-swift-coding-standards)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: swift-coding-standards
description: Production-ready Swift coding standards for iOS/macOS development enforcing safety, clarity, and modern patterns. Use when enforcing swift coding conventions and style rules.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: coding-standards
  tags: [swift, ios, macos, coding-standards, swiftui, apple]
---

# Swift Coding Standards Mode

You are a Swift code quality expert. Your role is to enforce safe, clear, and idiomatic Swift patterns following Apple's API Design Guidelines and industry best practices.

## Core Principles

1. **Clarity at the Point of Use** - Code should read like prose
2. **Safety** - Leverage Swift's type system for compile-time guarantees
3. **Value Semantics** - Prefer structs over classes
4. **Protocol-Oriented** - Favor composition over inheritance

## Naming Conventions

### Types and Protocols

```swift
// ✅ UpperCamelCase for types
struct User { }
class NetworkManager { }
enum ConnectionState { }
protocol Configurable { }

// ✅ Protocol names describe capability
protocol Equatable { }       // -able for capabilities
protocol ProgressReporting { }  // -ing for active behaviors
protocol Collection { }     // Noun for roles

// ✅ Avoid type prefixes (no NS, UI prefix for custom types)
struct MyCustomView { }     // ✅
struct MYCustomView { }     // ❌ Looks like prefix
```

### Properties and Methods

```swift
// ✅ lowerCamelCase for properties and methods
var userName: String
let createdAt: Date

func calculateTotal() -> Int { }
func fetchUser(byId id: String) async throws -> User { }

// ✅ Boolean properties read as assertions
var isEmpty: Bool
var isEnabled: Bool
var hasContent: Bool
var canSubmit: Bool
var shouldRefresh: Bool

// ✅ Mutating methods use verb phrases
mutating func append(_ item: Item)
mutating func removeAll()

// ✅ Non-mutating methods use noun phrases or past participles
func appending(_ item: Item) -> [Item]
func sorted() -> [Item]
func distance(to point: Point) -> Double
```

### Parameters and Arguments

```swift
// ✅ Name parameters for clarity at call site
func move(from start: Point, to end: Point)
// Usage: move(from: origin, to: destination)

func insert(_ item: Item, at index: Int)
// Usage: insert(newItem, at: 3)

// ✅ Omit first argument label when it's clear from function name
func contains(_ element: Element) -> Bool
func add(_ observer: Observer)

// ✅ Use prepositions for clarity
func convert(_ temperature: Double, from: TemperatureUnit, to: TemperatureUnit)
func request(_ resource: Resource, with options: Options)

// ❌ Don't repeat type information
func addChild(childView: UIView)     // ❌
func addChild(_ view: UIView)        // ✅
```

### Constants and Type Properties

```swift
// ✅ lowerCamelCase for constants
let maximumRetryCount = 3
let defaultTimeout: TimeInterval = 30

// ✅ Static properties follow same rules
struct Configuration {
    static let shared = Configuration()
    static let defaultTheme = Theme.light
}

// ✅ Enum cases are lowerCamelCase
enum Direction {
    case north
    case south
    case east
    case west
}

enum HTTPMethod {
    case get
    case post
    case put
    case delete
}
```

## Type System

### Optionals

```swift
// ✅ Use optionals for values that can be absent
var middleName: String?
var deletedAt: Date?

// ✅ Use guard for early exit
func processUser(_ user: User?) throws {
    guard let user else {
        throw ValidationError.missingUser
    }
    // user is now non-optional
    process(user)
}

// ✅ Use optional chaining
let userName = user?.profile?.displayName ?? "Unknown"
let count = items?.count ?? 0

// ✅ Use map/flatMap for transformations
let uppercasedName = name.map { $0.uppercased() }
let user = userId.flatMap { userCache[$0] }

// ❌ Avoid force unwrapping
let name = user!.name  // ❌ Can crash

// ✅ Force unwrap only with clear justification
let url = URL(string: "https://apple.com")!  // Known-valid literal
```

### Value Types

```swift
// ✅ Prefer structs for data models
struct User: Identifiable, Equatable, Codable {
    let id: UUID
    var name: String
    var email: String
    var createdAt: Date

    init(name: String, email: String) {
        self.id = UUID()
        self.name = name
        self.email = email
        self.createdAt = Date()
    }
}

// ✅ Use classes for identity, inheritance, or reference semantics
final class NetworkSession {
    private let urlSession: URLSession
    private var observers: [Observer] = []

    // Reference semantics needed for shared state
}

// ✅ Mark classes as final by default
final class UserViewModel: ObservableObject { }
```

### Enums with Associated Values

```swift
// ✅ Use enums for state machines
enum LoadingState<T> {
    case idle
    case loading
    case loaded(T)
    case failed(Error)
}

// ✅ Pattern matching
func handle(_ state: LoadingState<User>) {
    switch state {
    case .idle:
        showPlaceholder()
    case .loading:
        showSpinner()
    case .loaded(let user):
        showUser(user)
    case .failed(let error):
        showError(error)
    }
}

// ✅ Use if case for single case matching
if case .loaded(let user) = state {
    print(user.name)
}

// ✅ Use guard case for early exit
guard case .loaded(let user) = state else {
    return
}
```

### Result Type

```swift
// ✅ Use Result for synchronous fallible operations
func parse(_ json: Data) -> Result<User, ParseError> {
    do {
        let user = try JSONDecoder().decode(User.self, from: json)
        return .success(user)
    } catch let error as DecodingError {
        return .failure(.decodingFailed(error))
    } catch {
        return .failure(.unknown(error))
    }
}

// ✅ Handle Result with switch or methods
let result = parse(data)

switch result {
case .success(let user):
    process(user)
case .failure(let error):
    handleError(error)
}

// Or use methods
let user = try result.get()
let name = result.map(\.name)
```

## Error Handling

### Error Types

```swift
// ✅ Define domain-specific errors
enum NetworkError: Error {
    case invalidURL
    case noConnection
    case timeout
    case serverError(statusCode: Int)
    case decodingFailed(DecodingError)
}

// ✅ Conform to LocalizedError for user-facing messages
extension NetworkError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "The URL is invalid."
        case .noConnection:
            return "No internet connection."
        case .timeout:
            return "The request timed out."
        case .serverError(let code):
            return "Server error: \(code)"
        case .decodingFailed:
            return "Failed to process server response."
        }
    }
}

// ✅ Use typed throws (Swift 6)
func fetchUser(id: String) throws(NetworkError) -> User {
    // Can only throw NetworkError
}
```

### Error Handling Patterns

```swift
// ✅ Use do-catch for error handling
do {
    let user = try await fetchUser(id: userId)
    updateUI(with: user)
} catch NetworkError.noConnection {
    showOfflineMessage()
} catch NetworkError.timeout {
    showRetryButton()
} catch {
    showGenericError(error)
}

// ✅ Use try? for optional results
let user = try? await fetchUser(id: userId)

// ✅ Use try! only for known-good operations
let data = try! JSONEncoder().encode(["key": "value"])

// ✅ Rethrow errors with context
func processOrder(_ order: Order) async throws {
    do {
        try await validateOrder(order)
        try await chargePayment(order)
        try await fulfillOrder(order)
    } catch {
        throw OrderError.processingFailed(order: order, underlying: error)
    }
}
```

## Concurrency

### Async/Await

```swift
// ✅ Use async/await for asynchronous code
func fetchUser(id: String) async throws -> User {
    let url = URL(string: "\(baseURL)/users/\(id)")!
    let (data, response) = try await urlSession.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw NetworkError.invalidResponse
    }

    return try JSONDecoder().decode(User.self, from: data)
}

// ✅ Concurrent execution with async let
func fetchUserProfile(id: String) async throws -> UserProfile {
    async let user = fetchUser(id: id)
    async let posts = fetchPosts(userId: id)
    async let followers = fetchFollowers(userId: id)

    return try await UserProfile(
        user: user,
        posts: posts,
        followers: followers
    )
}

// ✅ TaskGroup for dynamic concurrency
func fetchAllUsers(ids: [String]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        for id in ids {
            group.addTask {
                try await fetchUser(id: id)
            }
        }

        return try await group.reduce(into: []) { $0.append($1) }
    }
}
```

### Actors

```swift
// ✅ Use actors for shared mutable state
actor UserCache {
    private var cache: [String: User] = [:]

    func get(_ id: String) -> User? {
        cache[id]
    }

    func set(_ user: User) {
        cache[user.id] = user
    }

    func clear() {
        cache.removeAll()
    }
}

// ✅ Use @MainActor for UI updates
@MainActor
final class UserViewModel: ObservableObject {
    @Published var user: User?
    @Published var isLoading = false
    @Published var error: Error?

    func loadUser(id: String) async {
        isLoading = true
        defer { isLoading = false }

        do {
            user = try await userService.fetchUser(id: id)
        } catch {
            self.error = error
        }
    }
}

// ✅ Sendable for thread-safe types
struct UserData: Sendable {
    let id: String
    let name: String
}
```

## SwiftUI

### View Design

```swift
// ✅ Small, focused views
struct UserRow: View {
    let user: User

    var body: some View {
        HStack {
            Avatar(url: user.avatarURL)
            VStack(alignment: .leading) {
                Text(user.name)
                    .font(.headline)
                Text(user.email)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }
        }
    }
}

// ✅ Extract complex logic to computed properties
struct OrderSummary: View {
    let order: Order

    private var formattedTotal: String {
        order.total.formatted(.currency(code: "USD"))
    }

    private var statusColor: Color {
        switch order.status {
        case .pending: .orange
        case .confirmed: .green
        case .cancelled: .red
        }
    }

    var body: some View {
        VStack {
            Text(formattedTotal)
            Text(order.status.rawValue)
                .foregroundStyle(statusColor)
        }
    }
}

// ✅ Use ViewBuilder for conditional content
@ViewBuilder
private var contentView: some View {
    switch viewModel.state {
    case .loading:
        ProgressView()
    case .loaded(let items):
        ItemList(items: items)
    case .error(let error):
        ErrorView(error: error)
    }
}
```

### State Management

```swift
// ✅ Use appropriate property wrappers
struct ContentView: View {
    // Local state
    @State private var isShowingSheet = false
    @State private var searchText = ""

    // Observed object (external)
    @ObservedObject var viewModel: ContentViewModel

    // State object (owned)
    @StateObject private var localViewModel = LocalViewModel()

    // Environment
    @Environment(\.dismiss) private var dismiss
    @Environment(\.colorScheme) private var colorScheme

    // Binding (passed from parent)
    @Binding var selectedItem: Item?

    var body: some View {
        // ...
    }
}

// ✅ Prefer Observation framework (iOS 17+)
@Observable
final class UserViewModel {
    var user: User?
    var isLoading = false
    var errorMessage: String?

    func loadUser(id: String) async {
        isLoading = true
        defer { isLoading = false }

        do {
            user = try await userService.fetchUser(id: id)
        } catch {
            errorMessage = error.localizedDescription
        }
    }
}
```

## Testing

### Unit Tests

```swift
final class UserServiceTests: XCTestCase {
    var sut: UserService!
    var mockRepository: MockUserRepository!

    override func setUp() {
        super.setUp()
        mockRepository = MockUserRepository()
        sut = UserService(repository: mockRepository)
    }

    override func tearDown() {
        sut = nil
        mockRepository = nil
        super.tearDown()
    }

    func test_fetchUser_withValidId_returnsUser() async throws {
        // Given
        let expectedUser = User(id: "1", name: "Test", email: "test@example.com")
        mockRepository.stubbedUser = expectedUser

        // When
        let user = try await sut.fetchUser(id: "1")

        // Then
        XCTAssertEqual(user.id, expectedUser.id)
        XCTAssertEqual(user.name, expectedUser.name)
    }

    func test_fetchUser_withInvalidId_throwsNotFound() async {
        // Given
        mockRepository.stubbedError = UserError.notFound

        // When/Then
        do {
            _ = try await sut.fetchUser(id: "invalid")
            XCTFail("Expected error to be thrown")
        } catch UserError.notFound {
            // Expected
        } catch {
            XCTFail("Unexpected error: \(error)")
        }
    }
}
```

### Testing Concurrency

```swift
func test_concurrentAccess_maintainsConsistency() async {
    // Given
    let cache = UserCache()
    let users = (1...100).map { User(id: "\($0)", name: "User \($0)") }

    // When - concurrent writes
    await withTaskGroup(of: Void.self) { group in
        for user in users {
            group.addTask {
                await cache.set(user)
            }
        }
    }

    // Then - all users should be cached
    for user in users {
        let cached = await cache.get(user.id)
        XCTAssertEqual(cached?.id, user.id)
    }
}
```

## Documentation

```swift
/// A user account in the system.
///
/// Users are identified by a unique `id` and must have a valid email address.
///
/// ## Topics
///
/// ### Creating Users
/// - ``init(name:email:)``
///
/// ### Properties
/// - ``id``
/// - ``name``
/// - ``email``
struct User: Identifiable {
    /// The unique identifier for this user.
    let id: UUID

    /// The user's display name.
    var name: String

    /// The user's email address.
    ///
    /// This must be a valid email format and is used for authentication.
    var email: String

    /// Creates a new user with the specified name and email.
    ///
    /// - Parameters:
    ///   - name: The user's display name.
    ///   - email: The user's email address. Must be a valid format.
    /// - Throws: `ValidationError.invalidEmail` if the email format is invalid.
    init(name: String, email: String) throws {
        guard email.contains("@") else {
            throw ValidationError.invalidEmail
        }
        self.id = UUID()
        self.name = name
        self.email = email
    }
}
```

## SwiftLint Configuration

```yaml
# .swiftlint.yml
disabled_rules:
  - trailing_whitespace

opt_in_rules:
  - array_init
  - closure_end_indentation
  - closure_spacing
  - collection_alignment
  - contains_over_first_not_nil
  - discouraged_object_literal
  - empty_count
  - empty_string
  - explicit_init
  - fatal_error_message
  - first_where
  - force_unwrapping
  - implicitly_unwrapped_optional
  - last_where
  - literal_expression_end_indentation
  - multiline_arguments
  - multiline_parameters
  - operator_usage_whitespace
  - overridden_super_call
  - prefer_zero_over_explicit_init
  - private_action
  - private_outlet
  - prohibited_super_call
  - redundant_nil_coalescing
  - single_test_class
  - sorted_first_last
  - unavailable_function
  - unneeded_parentheses_in_closure_argument
  - vertical_parameter_alignment_on_call
  - yoda_condition

line_length:
  warning: 120
  error: 200

type_body_length:
  warning: 300
  error: 500

file_length:
  warning: 500
  error: 1000

identifier_name:
  min_length: 2
  excluded:
    - id
    - x
    - y

nesting:
  type_level: 2
```

## Validation Checklist

```text
□ No force unwrapping without justification
□ Optionals handled with guard/if let
□ Value types (structs) preferred over classes
□ Classes marked final unless inheritance needed
□ Actors used for shared mutable state
□ async/await used for asynchronous code
□ Error types conform to LocalizedError
□ SwiftUI views are small and focused
□ @MainActor used for UI updates
□ Tests cover async code properly
□ Documentation on public APIs
□ SwiftLint passes without warnings
```

## Resources

- [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/)
- [Swift Documentation](https://docs.swift.org/)
- [SwiftUI Documentation](https://developer.apple.com/documentation/swiftui)
- [Swift Concurrency](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/)
- [Swift Evolution](https://github.com/apple/swift-evolution)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.