Swift Language Patterns
Quick reference for Swift language best practices and idioms.
Optionals Handling
Anti-patterns
// ❌ Force unwrapping
let name = user.userName!
// ❌ Nested if lets (pyramid of doom)
if let user = user {
if let name = user.userName {
if let email = user.email {
// ...
}
}
}
// ❌ Unnecessary optional chaining
if expense?.amount != nil {
let amount = expense!.amount
}Good patterns
// ✅ Nil coalescing
let name = user.userName ?? "Unknown"
// ✅ Guard for early return
guard let user = expense.payer else { return }
// ✅ Combined guard
guard let user = expense.payer,
let name = user.userName,
!name.isEmpty else { return }
// ✅ Optional chaining
let upperName = user?.userName?.uppercased()Checklist
- Avoid force unwrapping (
!) unless crash is intentional - Use optional chaining (
?.) and nil coalescing (??) - Prefer
guard letfor early returns - Use
if letfor conditional unwrapping - Avoid pyramid of doom with multiple
if let
Type Safety & Enums
Anti-patterns
// ❌ String constants
if category == "Food" { }
// ❌ Magic numbers
if status == 0 { }
// ❌ If-else chains for enums
if category == .food {
} else if category == .transport {
} else if category == .entertainment {
}Good patterns
// ✅ Enum with raw values
enum ExpenseCategory: String, CaseIterable {
case food = "Food"
case transport = "Transport"
case entertainment = "Entertainment"
}
// ✅ Switch with exhaustive checking
switch category {
case .food:
// ...
case .transport:
// ...
case .entertainment:
// ...
}
// ✅ Associated values
enum NetworkResult {
case success(data: Data)
case failure(error: Error)
}Checklist
- Use enums instead of string/int constants
- Avoid stringly-typed code
- Use associated values for related data
- Prefer
switchover multipleiffor enums - Use
CaseIterablewhen appropriate
Collections & Sequences
Anti-patterns
// ❌ Manual loop for transformation
var names: [String] = []
for user in users {
names.append(user.name)
}
// ❌ Inefficient filtering
let found = users.filter { $0.id == userId }.first
// ❌ Force array access
let first = array[0]
// ❌ Count comparison
if array.count == 0 { }Good patterns
// ✅ Map for transformation
let names = users.map { $0.name }
// ✅ CompactMap for optional unwrapping
let names = users.compactMap { $0.userName }
// ✅ First(where:) for finding
let found = users.first { $0.id == userId }
// ✅ Safe array access
guard let first = array.first else { return }
// ✅ isEmpty
if array.isEmpty { }Checklist
- Use
map,filter,reduceinstead of loops where appropriate - Prefer
compactMapovermap+ filter nil - Use
first(where:)instead offilter().first - Avoid force-accessing arrays with
[0] - Use
isEmptyinstead ofcount == 0
Error Handling
Anti-patterns
// ❌ Silent failure
try? context.save()
// ❌ Generic error handling
do {
try something()
} catch {
print("Error")
}
// ❌ Boolean success flags
func saveData() -> Bool {
// ...
}Good patterns
// ✅ Custom errors
enum DataError: LocalizedError {
case saveFailed
case invalidData(reason: String)
var errorDescription: String? {
switch self {
case .saveFailed:
return "Failed to save data"
case .invalidData(let reason):
return "Invalid data: \(reason)"
}
}
}
// ✅ Proper error propagation
func saveExpense() throws {
guard !name.isEmpty else {
throw DataError.invalidData(reason: "Name is empty")
}
try context.save()
}
// ✅ Result type
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
// ...
}Checklist
- Use Swift error handling (
throws,try,catch) - Define custom error types
- Avoid swallowing errors silently
- Use
Resulttype for async operations - Provide meaningful error messages
Naming Conventions
Anti-patterns
// ❌ Unclear names
var e: Expense
func calc() { }
var flg: Bool
// ❌ Redundant names
func calculateCalculation() { }
var nameString: String
// ❌ Poor boolean names
var active: Bool
var valid: BoolGood patterns
// ✅ Clear names
var currentExpense: Expense
func calculateTotal() { }
var isActive: Bool
// ✅ Descriptive methods
func saveExpense(named: String, amount: Double)
func deleteExpense(_ expense: Expense)
// ✅ Boolean clarity
var isActive: Bool
var hasValidData: Bool
var shouldShowAlert: BoolChecklist
- Clear, descriptive names
- Boolean names start with
is,has,should - Method names describe action
- Avoid abbreviations
- Follow Swift API Design Guidelines