Variables and Types
Variables and Constants
Section titled “Variables and Constants”Swift distinguishes between variables (mutable) and constants (immutable). Prefer let over var whenever the value does not need to change.
let maximumAttempts = 3 // Constant -- cannot be changedvar currentAttempt = 0 // Variable -- can be modifiedcurrentAttempt += 1 // OK
// maximumAttempts = 4 // Error: Cannot assign to "let' constantNaming Conventions
Section titled “Naming Conventions”- Use camelCase for variables, functions, and enum cases
- Use PascalCase for types (classes, structs, enums, protocols)
- Use descriptive names; avoid single-letter names except for loop indices
- Prefer
is,has,shouldprefixes for boolean properties
let studentName = "Alice" // camelCaselet isEnrolled = true // Boolean prefixlet maxRetryCount = 3 // Descriptivelet JSONData: Data // Acronyms are uppercased as a word
struct University { } // PascalCase for typesenum Grade { case honours } // PascalCase + camelCaseType Inference
Section titled “Type Inference”Swift infers types from the value assigned. You can always be explicit when the type is not obvious or when you need a specific type.
let inferredInt = 42 // Intlet inferredDouble = 3.14 // Doublelet inferredString = "hello" // Stringlet inferredBool = true // Bool
// Explicit types when neededlet version: Float = 3.14 // Float (32-bit, not Double)let bytes: UInt8 = 255 // Unsigned 8-bit integerlet hex: Int = 0xFF // Hexadecimal literallet binary: Int = 0b1010 // Binary literallet octal: Int = 0o77 // Octal literallet largeNumber = 1_000_000 // Underscores for readabilityNumeric Types
Section titled “Numeric Types”| Type | Size (bits) | Range |
|---|---|---|
Int8 | 8 | -128 to 127 |
Int16 | 16 | -32,768 to 32,767 |
Int32 | 32 | -2,147,483,648 to 2,147,483,647 |
Int64 | 64 | -9,223,372,036,854,775,808 to … |
Int | Platform | Same as Int64 on 64-bit, Int32 on 32-bit |
UInt | Platform | Unsigned equivalent of Int |
Float | 32 | 6 decimal digits precision |
Double | 64 | 15 decimal digits precision |
// Type conversion -- must be explicitlet integer = 42let double = Double(integer) // 42.0let backToInt = Int(double) // 42
let pi = 3.14159let truncated = Int(pi) // 3 (truncation, not rounding)
// Safe conversionlet tooBig: Int16 = 40000// let fits: Int8 = Int8(tooBig) // Error: crash at runtime if overflowlet safe: Int8? = Int8(exactly: tooBig) // nil (safe, returns optional)Booleans
Section titled “Booleans”Swift’s Bool type has only true and false. It does not implicitly convert from integers.
let isActive = truelet isDeleted = false
// Booleans in conditions -- must be Bool, not Intif isActive { print("User is active")}
// Bool methodslet result = isActive && !isDeleted // truelet either = isActive || isDeleted // truelet flipped = isActive.toggle() // false (mutates in place)Tuples
Section titled “Tuples”Tuples group multiple values into a single compound value. They are useful for returning multiple values from a function.
// Basic tuplelet httpStatus = (200, "OK")print(httpStatus.0) // 200print(httpStatus.1) // OK
// Named elementslet error = (code: 404, message: "Not Found")print(error.code) // 404print(error.message) // Not Found
// Decompositionlet (statusCode, statusMessage) = httpStatusprint(statusCode) // 200
// Partial decomposition with _let (code, _) = errorprint(code) // 404
// Tuples as return typesfunc minMax(_ array: [Int]) -> (min: Int, max: Int)? { guard let first = array.first else { return nil } var currentMin = first var currentMax = first for value in array { if value < currentMin { currentMin = value } if value > currentMax { currentMax = value } } return (currentMin, currentMax)}
if let result = minMax([3, 7, 1, 9, 4]) { print("Min: \(result.min), Max: \(result.max)") // Min: 1, Max: 9}Optionals
Section titled “Optionals”Optionals are Swift’s mechanism for representing the absence of a value. A variable of type T? can hold either a value of type T or nil.
Declaration and Usage
Section titled “Declaration and Usage”var name: String? = "Alice"name = nil // Valid for optionals
var age: Int? // Automatically initialised to nil
// Non-optional cannot be nil// var required: String = nil // Error: "nil'' is not compatible with "String'Unwrapping Optionals
Section titled “Unwrapping Optionals”var score: Int? = 85
// Forced unwrapping -- dangerous, can crashlet value = score! // 85, but crashes if score is nil
// Optional binding with if letif let unwrapped = score { print("Score is \(unwrapped)")}
// Multiple optional bindingslet nickname: String? = "Ally"if let s = score, let n = nickname { print("\(n) scored \(s)")}
// Shorthand (Swift 5.7+) -- same name as the optionalif let score { print("Score is \(score)")}
// guard let -- preferred for early exitsfunc process(score: Int?) { guard let score else { print("No score provided") return } print("Processing score: \(score)")}
// Optional chainingstruct Person { var address: Address?}struct Address { var street: String?}
let person = Person(address: Address(street: "Main St"))let street = person.address?.street // String? ("Main St")let missing = Person().address?.street // nilnil Coalescing Operator
Section titled “nil Coalescing Operator”let defaultColor = "black"let userColor: String? = nillet activeColor = userColor ?? defaultColor // "black"
// Chaininglet config: String?? = "custom"let final = config ?? "default" // "custom"
// With function callslet username = UserDefaults.standard.string(forKey: "username") ?? "Guest"Optional Map and FlatMap
Section titled “Optional Map and FlatMap”let rating: Int? = 4let doubled = rating.map { $0 * 2 } // 8
let ratings: [Int?] = [1, nil, 3, nil, 5]let valid = ratings.compactMap { $0 } // [1, 3, 5]Implicitly Unwrapped Optionals
Section titled “Implicitly Unwrapped Optionals”var outlet: UILabel! = UILabel() // Assumes non-nil after initialisationoutlet.text = "Hello" // No need to unwrap
// Still optional at runtime -- can be nil// outlet = nil// outlet.text = "Hello" // Crash if nilCollections
Section titled “Collections”Arrays
Section titled “Arrays”Arrays are ordered, zero-indexed collections of values of the same type.
// Creationvar numbers = [1, 2, 3, 4, 5]var empty: [String] = []var zeroes = Array(repeating: 0, count: 5)
// Type annotationvar names: [String] = ["Alice", "Bob", "Carol"]
// Access and modificationnumbers.append(6) // [1, 2, 3, 4, 5, 6]numbers.insert(0, at: 0) // [0, 1, 2, 3, 4, 5, 6]numbers.remove(at: 0) // [1, 2, 3, 4, 5, 6]numbers[0] = 10 // [10, 2, 3, 4, 5, 6]numbers[1...3] = [20, 30] // [10, 20, 30, 5, 6] (replace range)
// Propertiesnumbers.count // 5numbers.isEmpty // falsenumbers.capacity // Implementation detail (allocated space)
// Iterationfor number in numbers { print(number)}
for (index, number) in numbers.enumerated() { print("\(index): \(number)")}
// Sortingvar unsorted = [5, 2, 8, 1, 9]unsorted.sort() // In-place: [1, 2, 5, 8, 9]let sorted = unsorted.sorted(by: >) // New array: [9, 8, 5, 2, 1]
// Searchinglet found = numbers.contains(20) // truelet index = numbers.firstIndex(of: 30) // Int? -- index of first matchlet first = numbers.first // 10 (Int?, nil if empty)
// Higher-order functionslet squares = numbers.map { $0 * $0 } // [100, 400, 900, 25, 36]let evens = numbers.filter { $0 % 2 == 0 } // [10, 20, 30, 6]let total = numbers.reduce(0, +) // 71let firstOver10 = numbers.first { $0 > 10 } // 20
// flatMap for nested arrayslet nested = [[1, 2], [3, 4], [5]]let flat = nested.flatMap { $0 } // [1, 2, 3, 4, 5]Dictionaries
Section titled “Dictionaries”Dictionaries store key-value pairs with unique keys and unordered storage.
// Creationvar scores = ["Alice": 95, "Bob": 87, "Carol": 92]var empty: [String: Int] = [:]
// Access and modificationscores["Dave"] = 78 // Add new key-valuescores["Alice"] = 98 // Update existingscores.updateValue(100, forKey: "Bob") // Update (returns old value)let removed = scores.removeValue(forKey: "Carol") // Remove (returns old value)
// Access -- returns optionallet aliceScore = scores["Alice"] // Int? (95)let missing = scores["Eve"] // nil
// Iterationfor (name, score) in scores { print("\(name): \(score)")}
for name in scores.keys { print(name)}
for score in scores.values { print(score)}
// Transforminglet names = Array(scores.keys.sorted()) // ["Alice", "Bob", "Dave"]let doubledScores = scores.mapValues { $0 * 2 } // ["Alice": 196, "Bob": 174, ...]
// Mergingvar defaults = ["theme": "light", "fontSize": 14]var userPrefs = ["fontSize": 18]userPrefs.merge(defaults) { (_, new) in new } // Use new value for conflicts
// Grouping with Dictionary(grouping:)let words = ["apple", "banana", "avocado", "blueberry", "cherry"]let grouped = Dictionary(grouping: words, by: { $0.first! })// ["a": ["apple", "avocado"], "b": ["banana", "blueberry"], "c": ["cherry"]]Sets are unordered collections of unique values. They must conform to Hashable.
// Creationvar genres: Set<String> = ["Rock", "Jazz", "Pop"]var numbers: Set<Int> = [1, 2, 3, 2, 1] // {1, 2, 3} (duplicates removed)
// Operationsgenres.insert("Classical") // Insertgenres.remove("Pop") // Removegenres.contains("Jazz") // true
// Set operationslet a: Set = [1, 2, 3, 4]let b: Set = [3, 4, 5, 6]
a.union(b) // {1, 2, 3, 4, 5, 6}a.intersection(b) // {3, 4}a.symmetricDifference(b)// {1, 2, 5, 6}a.subtracting(b) // {1, 2}
// Set relationshipsa.isSubset(of: b) // falsea.isSuperset(of: b) // falsea.isDisjoint(with: b) // falseControl Flow
Section titled “Control Flow”Conditional Statements
Section titled “Conditional Statements”let temperature = 25
// if / else if / elseif temperature > 30 { print("Hot")} else if temperature > 20 { print("Warm")} else { print("Cold")}
// Ternary operatorlet status = temperature > 30 ? "hot" : "comfortable"
// Switch -- must be exhaustiveswitch temperature {case ..<0: print("Freezing")case 0..<15: print("Cold")case 15..<25: print("Comfortable")case 25...35: print("Warm")default: print("Hot")}
// Switch with pattern matchinglet point = (x: 2, y: -3)switch point {case (0, 0): print("Origin")case (_, 0): print("On x-axis")case (0, _): print("On y-axis")case (-2...2, -2...2): print("Close to origin")case let (x, y) where x == y: print("On y = x")default: print("Somewhere else")}
// Switch on rangeslet character: Character = "a"switch character {case "a"..."z": print("Lowercase letter")case "A"..."Z": print("Uppercase letter")default: print("Not a letter")}// For-in loopfor i in 1...5 { print(i) // 1, 2, 3, 4, 5}
for i in 1..<5 { print(i) // 1, 2, 3, 4}
for i in stride(from: 0, to: 10, by: 2) { print(i) // 0, 2, 4, 6, 8}
for i in stride(from: 10, through: 0, by: -1) { print(i) // 10, 9, ..., 0}
// While loopvar count = 5while count > 0 { print(count) count -= 1}
// Repeat-while (do-while equivalent)var input = ""repeat { print("Enter a number") // input = readLine() ?? ""} while input.isEmpty
// Labeled statements for nested loopsouterLoop: for i in 1...3 { for j in 1...3 { if i == 2 && j == 2 { break outerLoop } print("\(i), \(j)") }}
// where clause in for loopslet numbers: [Int?] = [1, nil, 3, nil, 5, 6, nil]for case let number? in numbers { print(number) // 1, 3, 5, 6}Guard Statement
Section titled “Guard Statement”guard ensures conditions are met early, keeping the “happy path” unindented.
func processOrder(quantity: Int, price: Double, customerName: String?) { guard quantity > 0 else { print("Invalid quantity") return }
guard price > 0 else { print("Invalid price") return }
guard let name = customerName, !name.isEmpty else { print("Customer name required") return }
let total = Double(quantity) * price print("Order for \(name): \(quantity) x $\(price) = $\(total)")}Strings
Section titled “Strings”Strings are value types (copied on assignment) and are Unicode-correct by default.
String Basics
Section titled “String Basics”let greeting = "Hello, World!"let emptyString = String()
// Multiline string literallet poem = """ Two roads diverged in a yellow wood, And sorry I could not travel both And be one traveler, long I stood """
// String interpolationlet name = "Alice"let age = 30let message = "My name is \(name) and I am \(age) years old."
// Extended string delimiters (raw strings)let rawPath = #"C:\Users\name\Documents"#let escapedQuote = #"He said "hello""#
// String concatenationvar full = "Hello" + " " + "World"full += "!"
// Character accessfor character in "Swift" { print(character) // S, w, i, f, t}
let chars = Array("Swift") // ["S", "w", "i", "f", "t"]String Properties and Methods
Section titled “String Properties and Methods”let text = "Hello, Swift Programming!"
text.isEmpty // falsetext.count // 25 (character count, not byte count)text.hasPrefix("Hello") // truetext.hasSuffix("ing!") // truetext.lowercased() // "hello, swift programming!"text.uppercased() // "HELLO, SWIFT PROGRAMMING!"text.trimmingCharacters(in: .whitespaces)
// Substringlet index = text.firstIndex(of: ",")!let before = text[..<index] // "Hello"let after = text[text.index(after: index)...] // " Swift Programming!"
// Split and joinlet parts = text.split(separator: " ") // ["Hello,", "Swift", "Programming!"]let joined = parts.joined(separator: "-") // "Hello,-Swift,-Programming!"
// Replacelet cleaned = text.replacingOccurrences(of: "Swift", with: "Rust")String Indices
Section titled “String Indices”Swift strings use String.Index for safe character access (not plain integers).
let str = "Hello, World!"let startIndex = str.startIndex // First character positionlet endIndex = str.endIndex // Position after last character
str[startIndex] // "H"str[str.index(before: endIndex)] // "!"
let commaIndex = str.firstIndex(of: ",")!str[str.index(after: commaIndex)] // " " (space after comma)
// Subscripting with rangelet range = str[str.startIndex..<str.index(str.startIndex, offsetBy: 5)]// "Hello"
// Find and replaceif let range = str.range(of: "World") { let replaced = str.replacingCharacters(in: range, with: "Swift") // "Hello, Swift!"}Type Casting
Section titled “Type Casting”Swift provides safe type casting with as?, as!, and is.
class Animal { let name: String; init(name: String) { self.name = name } }class Dog: Animal { func bark() { print("Woof!") } }class Cat: Animal { func meow() { print("Meow!") } }
let pets: [Animal] = [Dog(name: "Rex"), Cat(name: "Whiskers"), Dog(name: "Buddy")]
for pet in pets { if let dog = pet as? Dog { dog.bark() } else if let cat = pet as? Cat { cat.meow() }}
// Type checkingfor pet in pets { if pet is Dog { print("\(pet.name) is a dog") }}
// Any and AnyObjectvar things: [Any] = [42, "hello", true, Dog(name: "Rex")]
for thing in things { switch thing { case let number as Int: print("Integer: \(number)") case let text as String: print("String: \(text)") case let dog as Dog: print("Dog: \(dog.name)") default: print("Unknown type") }}Type Aliases
Section titled “Type Aliases”Type aliases create alternative names for existing types.
typealias Coordinate = (x: Double, y: Double)typealias CompletionHandler = (Result<Data, Error>) -> Voidtypealias JSONDictionary = [String: Any]
let location: Coordinate = (x: 51.5, y: -0.1)let handler: CompletionHandler = { result in switch result { case .success(let data): print("Received \(data.count) bytes") case .failure(let error): print("Error: \(error)") }}Summary
Section titled “Summary”Swift’s type system provides strong safety guarantees through explicit optionals, type inference, and comprehensive pattern matching. Collections (arrays, dictionaries, sets) are value types with rich functional operations. Control flow with guard, switch, and pattern matching enables clean, safe code that is difficult to write incorrectly.
Intuition
Section titled “Intuition”Swift’s type system enforces safety at compile time. Let creates immutable constants while var creates mutable variables, and the compiler prefers let whenever possible. Type inference eliminates verbose annotations while keeping full type safety. Optionals (Type?) represent values that might be absent, forcing explicit unwrapping with if let, guard let, or the nil-coalescing operator. The distinction between value types (structs, enums) and reference types (classes) is fundamental to Swift’s memory model.
Cross-References
Section titled “Cross-References”- [[swift/00-intro/1_swift-intro]] - Language overview and design philosophy
- [[swift/02-functions-closures/1_functions]] - Function parameter types and return values
- [[swift/03-oop/1_classes-and-structs]] - Structs and classes in depth
- [[swift/04-advanced/1_error-handling]] - Optional handling patterns
Common Mistakes
Section titled “Common Mistakes”Force unwrapping optionals with !: Using ! crashes if the value is nil. Prefer if let, guard let, or ?? to handle missing values safely.
Confusing sort and sorted: sort() mutates the array in place (requires var), while sorted() returns a new array. Using the wrong one causes unexpected mutations or compiler errors.
Assuming String.Index is an integer: You cannot use integer subscripts on strings. Use str.index(str.startIndex, offsetBy: n) for safe character access. Integer indexing causes compile errors.