let / var
Swift variables: let vs var, type inference, type annotations, computed properties, and lazy initialisation.
Swift — variables
EXAMPLE
// ===== let vs var =====
let name = "Alex" // immutable
var count = 0 // mutable
count += 1
// name = "Sam" // error: cannot assign
// Default to let; reach for var only when the value changes.
// ===== Type inference + annotations =====
let pi = 3.14 // inferred Double
let id: Int = 42 // explicit Int
let isReady: Bool = true
let name2: String = "Alex"
let items: [String] = ["a", "b"]
let map: [String: Int] = ["a": 1]
// ===== Type conversion =====
let s = "42"
let n = Int(s) ?? 0 // Int? failable initialiser
let d = Double(n)
let str = String(n)
// ===== Optionals =====
var maybe: String? = nil
maybe = "hello"
// Unwrap:
if let value = maybe { print(value) }
guard let value = maybe else { return }
let length = maybe?.count ?? 0 // safe call + nil coalescing
let forced = maybe! // force (crashes if nil)
// ===== Computed properties =====
struct Circle {
var radius: Double
var area: Double { Double.pi * radius * radius }
var circumference: Double {
get { 2 * Double.pi * radius }
set { radius = newValue / (2 * Double.pi) }
}
}
// ===== Property observers =====
class User {
var name: String = "" {
willSet { print("about to set to \(newValue)") }
didSet { print("changed from \(oldValue) to \(name)") }
}
}
// ===== Lazy =====
class ImageLoader {
lazy var thumbnail: UIImage = {
// computed only on first access
return generateThumbnail()
}()
func generateThumbnail() -> UIImage { ... }
}
// Lazy requires var (not let) and is initialised on first use.
// ===== Static =====
struct Config {
static let baseURL = URL(string: "https://api.example.com")!
static var environment = "prod"
}
Config.baseURL
// ===== Scope =====
func demo() {
let outer = 1
if outer > 0 {
let inner = 2
print(outer, inner)
}
// inner not visible here
}
// ===== Destructuring (tuples) =====
let (x, y) = (3, 4)
let person = (name: "Alex", age: 30)
print(person.name, person.age)
// ===== Patterns to internalise =====
// - let by default; var only when truly mutable
// - Optional types in signatures; resolve at boundaries
// - Computed properties for derived state
// - lazy for expensive one-shot initialisation
// ===== Pitfalls =====
// - Force-unwrap (!) everywhere -> crashes
// - lazy on a value type (struct) — does not work the way you expect
// - Property observers run on EVERY assignment, even no-ops
// - Implicitly unwrapped optionals (Type!) when Type? would have surfaced bugs
Why it matters
let by default, var when you must, Optional for missing values. Computed properties for derivations, observers for reactivity, lazy for expensive one-shots, static for type-level state. The discipline of immutability + explicit nullability is most of what makes Swift safe.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Exercise
Declare an immutable binding.
pi = 3.14
Three letters.
Discussion
Loading…