Swift Optional<T> (written T?) is a type that wraps either a value or nil — the compiler requires you to handle the nil case before using the value. Value types (struct, enum) are copied on assignment; reference types (class) are shared via pointer. Copy-on-Write (CoW) ensures that copy-heavy operations like passing a large array are O(1) until the copy is actually mutated.
In Objective-C, sending a message to nil silently does nothing — bugs hide for months. In Swift, nil is banned from types that don't explicitly declare they can hold it. String can never be nil. String? might be. The compiler forces you to decide what happens when it is.
Optionals: if let, guard let, and ??
var name: String? = "Priya"
name = nil // OK — String? allows nil
// if let: safely unwrap into a new non-optional binding
if let n = name {
print("Hello, \(n)!") // n is String here, not String?
} else {
print("Name is nil")
}
// Swift 5.7+: shorthand — if let name (same name, no need to rename)
if let name {
print("Hello, \(name)!")
}
// guard let: unwrap or exit early — ideal in functions
func greet(name: String?) {
guard let name = name else {
print("No name provided")
return // must exit scope here
}
// name is String (non-optional) from here to end of function
print("Hello, \(name)! Your name has \(name.count) characters.")
}
// ?? nil-coalescing: provide a default
let display = name ?? "Anonymous" // "Anonymous" if name is nil
// Optional chaining: ?. returns Optional
let count = name?.count // Int? — nil if name is nil
let upper = name?.uppercased().prefix(3) // chain — nil if any step is nilStructs vs classes: value vs reference semantics
// Struct: value type — assignment copies the value
struct Point {
var x: Double
var y: Double
}
var p1 = Point(x: 1.0, y: 2.0)
var p2 = p1 // p2 is a COPY of p1
p2.x = 99.0
print(p1.x) // 1.0 — p1 is unchanged
// Class: reference type — assignment shares the reference
class Node {
var value: Int
init(_ v: Int) { value = v }
}
let n1 = Node(10)
let n2 = n1 // n2 points to the SAME object as n1
n2.value = 99
print(n1.value) // 99 — n1 sees the change (same object)
// When to use each:
// struct — data models, coordinates, colors, sizes (most Swift types)
// class — when identity matters, when sharing mutation is intentional,
// when subclassing is needed, when UIKit/AppKit expects classesEnums with associated values
Swift enums can carry associated values — making them more like algebraic data types than C enums. This is Swift's equivalent of Kotlin's sealed classes or Rust's enums.
enum NetworkResult<T> {
case success(T)
case failure(Error)
case loading
}
func handleResult(_ result: NetworkResult<String>) {
switch result {
case .success(let data):
print("Got: \(data)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
case .loading:
print("Loading...")
} // switch on enum is exhaustive — compiler warns if case is missing
}
// Result is a built-in Swift enum: Result<Success, Failure>
func fetchUser(id: Int) -> Result<String, Error> {
if id > 0 { return .success("User\(id)") }
return .failure(NSError(domain: "App", code: 404))
}
if case .success(let user) = fetchUser(id: 42) {
print(user) // "User42"
}Copy-on-Write: why passing a 10,000-element array is O(1)
Swift's standard library value types (Array, Dictionary, String, Set) use Copy-on-Write (CoW) internally. When you copy a large array, Swift doesn't immediately copy all the elements — it shares the underlying buffer between both arrays until one of them is mutated. At the point of first mutation on the copy, the buffer is duplicated. This means passing a large array to a function is O(1) if the function only reads it. Only mutation triggers the actual copy. You can implement CoW in your own types using isKnownUniquelyReferenced.
// Demonstrating Copy-on-Write behaviour
var a = [1, 2, 3, 4, 5]
var b = a // O(1) — shares buffer with a
b.append(6) // NOW the buffer is copied — a is unchanged
print(a) // [1, 2, 3, 4, 5]
print(b) // [1, 2, 3, 4, 5, 6]
// Custom CoW wrapper using isKnownUniquelyReferenced
final class Buffer {
var data: [Int]
init(_ data: [Int]) { self.data = data }
}
struct MyCollection {
private var buffer = Buffer([])
mutating func append(_ value: Int) {
if !isKnownUniquelyReferenced(&buffer) {
buffer = Buffer(buffer.data) // copy only when needed
}
buffer.data.append(value)
}
}Property wrappers: @State, @Published, and custom wrappers
Property wrappers (@propertyWrapper) add custom storage and behaviour to properties. SwiftUI's @State, @Binding, @Published, and @EnvironmentObject are all property wrappers. They abstract boilerplate — @State var count = 0 is equivalent to creating a State<Int> struct, accessing .wrappedValue for reads, and .projectedValue (accessed via $count) for the binding.
let p = Point(x: 1, y: 2) — you cannot change p's properties because the entire struct value is frozen. let n = Node(10) — you cannot change which Node n points to, but you can still do n.value = 99 because Node is a class (reference type). The let freezes the reference, not the object's internals.Optional<String> is defined as enum Optional<T> { case some(T); case none }. nil is syntactic sugar for .none. name? is syntactic sugar for Optional<String>. This means optional chaining, if let, and switch are all just pattern-matching on an enum — the same mechanism as any other enum.self must be marked mutating — this signals that the caller's copy will be updated. Classes don't need (or use) mutating because class methods can always modify the shared instance. If you're seeing a compiler error about mutating a struct property in a function, the fix is to add mutating to the method.Optional as a generic enum: the formal model
Swift's Optional<T> is defined in the standard library as a @frozen enum with two cases: case none and case some(Wrapped). @frozen means no new cases will ever be added — the compiler can use exhaustive pattern matching without a default case, and can represent Optional<Int> as a single machine word (using a sentinel value for nil when the wrapped type permits). T? is syntactic sugar for Optional<T>. Optional chaining (foo?.bar?.baz) desugars to nested map/flatMap calls on the Optional monad. The ?? operator is defined as func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T — the @autoclosure ensures the right-hand side is only evaluated if needed (lazy evaluation).
Existential types and any Protocol
Swift 5.7 introduced the any keyword for existential types — any Drawable is a type-erased box that can hold any value conforming to Drawable. Before Swift 5.7, using a protocol name directly as a type created an implicit existential (deprecated). Existentials have runtime overhead: they use a 5-word representation (value buffer + type metadata + witness table). The some keyword (opaque types) is the compile-time alternative: some Drawable is a specific unknown-to-the-caller type that conforms to Drawable — the compiler resolves the concrete type at compile time with no boxing overhead. SwiftUI uses some View for this reason.
Apple Inc. The Swift Programming Language. docs.swift.org/swift-book/. Chapters: Optionals, Enumerations, Structures and Classes, Value and Reference Types. Apple Inc. Swift Standard Library source. github.com/apple/swift. Swift Evolution SE-0352 (any keyword), SE-0309 (unlock existentials for all protocols).