Swift Language Basics: Variables, Types & Optionals

iOS Development Fundamentals

Chapter 2 · Swift Language Basics: Variables, Types & Optionals

Before writing more SwiftUI, this chapter covers Swift the language on its own terms — how values are declared and typed, and Swift's single most defining feature: optionals, the mechanism that makes "this might genuinely have no value" a fact the compiler itself checks, not something left to a runtime crash.

let vs. var

let appName = "Peak Tracker" // a constant — cannot be reassigned var score = 0 // a variable — can be reassigned score = 10 // fine // appName = "New Name" // real compile error — appName is a let
A Real, Deliberate Default
Swift's own official guidance is to reach for let by default, and only switch to var once a value genuinely needs to change. This isn't a style preference — the compiler itself will flag a var that's never actually reassigned, nudging code toward being immutable wherever it honestly can be.

Core Types & Real Type Inference

Int

A whole number — let count = 3.

Double

A floating-point number — let price = 4.99.

String

Text — let name = "Sam".

Bool

True or false — let isActive = true.

Swift is real, strictly statically typed — but it also has real type inference, so an explicit type annotation is only needed when it isn't obvious from the assigned value, or when declaring a variable with no initial value yet:

let count = 3 // inferred as Int let price: Double = 4 // explicit — without this, 4 would infer as Int var username: String // no value yet — type annotation is required here username = "sam_dev"

String Interpolation

let name = "Sam" let score = 42 let message = "\(name) scored \(score) points" // "Sam scored 42 points"
Coming From Web Development
Swift's \(...) string interpolation is directly equivalent to JavaScript's own template-literal ${...} syntax — same idea, different delimiter characters.

Optionals: Swift's Own Central Idea

A regular String in Swift is guaranteed to always hold a real string — never nil. An optional, written String?, is a genuinely different type: it can hold either a real String, or nothing at all (nil). This distinction is enforced by the real compiler, not left as a runtime risk the way an unchecked null reference is in many other languages.

var middleName: String? = nil middleName = "Jane" // let length = middleName.count // real compile error — middleName might be nil

Because an optional might genuinely hold nothing, Swift won't let real code use it directly as if it were guaranteed to have a value — it has to be unwrapped first.

Optional Binding: if let and guard let

var middleName: String? = "Jane" if let middleName { print("Middle name: \(middleName)") } else { print("No middle name") }
A Real, Genuinely Recent Shorthand
if let middleName { } is the real, current idiomatic form — introduced by Swift Evolution proposal SE-0345 and shipped in Swift 5.7. The older, still-valid form, if let middleName = middleName { }, repeats the name on both sides purely to shadow the optional with a real, non-optional constant of the same name inside the block — the shorthand simply lets the compiler synthesize that repetition automatically.

guard let works the same way, but is used specifically for an early exit — unwrapping and continuing in the normal flow, or exiting the current function/scope immediately if the value is nil:

func greet(_ name: String?) { guard let name else { print("No name provided") return } // name is a real, non-optional String from this point on print("Hello, \(name)!") }

The Nil-Coalescing Operator, ??

let middleName: String? = nil let display = middleName ?? "(none)" // "(none)" — falls back to the right-hand side when the left is nil
Force-Unwrapping — A Real, Genuine Risk
The ! operator (middleName!) forcibly unwraps an optional without checking it first — if the value actually is nil at that point, the app crashes immediately, at runtime, with no recovery. It has real, legitimate uses (mainly when a value is genuinely guaranteed non-nil by logic the compiler can't see), but reaching for if let, guard let, or ?? instead is the real, safer default this course follows throughout.

Hands-On Exercises

Exercise 1

Declare a var called favoriteColor of type String?, initially nil. Write an if let block (using the Swift 5.7 shorthand) that prints the color if set, or "No favorite color yet" if not. Test it both ways by changing the initial value.

📄 View solution
Exercise 2

Write a function describe(age: Int?) that uses guard let to print "Age not provided" and return early if age is nil, otherwise prints "Age is \(age)". Then rewrite the same logic using ?? and a single print call instead — explain, in your own words, when each style is the better real choice.

📄 View solution
Exercise 3

Explain, in your own words, why Swift's optionals catch a genuine class of real bugs at compile time that a language without them (or with an unchecked null reference) would only discover at runtime, potentially in production.

📄 View solution

Chapter 2 Quick Reference

  • let for constants (the real default), var for values that genuinely change
  • Core types: Int, Double, String, Bool — with real type inference from the assigned value
  • \(...) string interpolation — directly equivalent to JavaScript's ${...}
  • Optionals (T?) can hold a value or nil, checked by the real compiler — unwrap with if let/guard let (Swift 5.7's shorthand form, SE-0345) or ??
  • Force-unwrapping (!) crashes at runtime if the value is actually nil — a real, genuine risk to avoid as the default