Production-ready Swift coding standards for iOS/macOS development enforcing safety, clarity, and modern patterns. Use when enforcing swift coding conventions and style rules.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill swift-coding-standards --agent claude-codeInstalls 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 automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-swift-coding-standards)More formats (shields.io, HTML) on the badges page.
---
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)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!