Syntax and Variables
let vs var, optionals with if let and guard let, basic types, and control flow in Swift.
let vs. var
Swift makes immutability an explicit, deliberate choice at every declaration: let creates a constant, var creates a variable.
let name = "Ali" // constant — cannot be reassigned
var age = 22 // variable — can be reassigned
age = 23 // fine
name = "Bilal" // compile error: cannot assign to value: 'name' is a 'let' constant
Swift's official style guidance is to always use let unless you know a value needs to change — this makes intent clear and lets the compiler help catch accidental mutation.
Type inference and annotations
Swift infers types from the assigned value, but you can annotate explicitly when needed:
let name = "Ali" // inferred as String
let age: Int = 22 // explicit annotation
let price: Double = 9.99 // explicit annotation
let city: String // declared without a value...
city = "Lahore" // ...must be assigned before first use
Basic types
let isActive: Bool = true
let count: Int = 42
let temperature: Double = 36.6
let initial: Character = "A"
let message: String = "Hello"
let numbers: [Int] = [1, 2, 3] // Array<Int>
let scores: [String: Int] = ["Ali": 90, "Sara": 85] // Dictionary<String, Int>
let uniqueTags: Set<String> = ["swift", "ios"]
Optionals — a first-class topic
Swift has no implicit null. Instead, any type can be made optional by appending ?, meaning it either holds a value or holds nil — and the compiler forces you to handle both possibilities before using it:
var middleName: String? = nil // might have a value, might not
middleName = "Bin"
// You cannot use an optional as if it were guaranteed to have a value:
// print(middleName.count) // compile error — String? has no .count directly
Optional binding: if let
var username: String? = "ali_dev"
if let username = username {
print("Welcome, \(username)!") // only runs if username actually has a value
} else {
print("No username set")
}
Swift 5.7+ also supports the shorthand if let username (reusing the same name), which is now the idiomatic style shown above.
guard let — for early exits
guard let is the idiomatic choice inside a function when you want to exit early if a value is missing, keeping the "happy path" unindented:
func greet(_ name: String?) {
guard let name = name else {
print("No name provided")
return
}
// `name` is a guaranteed non-optional String from this point forward
print("Hello, \(name)!")
}
greet("Ali") // Hello, Ali!
greet(nil) // No name provided
Force unwrapping — use sparingly
! forcibly extracts the value, crashing at runtime if it's actually nil:
let username: String? = "ali_dev"
print(username!) // "ali_dev" — fine here, but only because we know it's not nil
let missing: String? = nil
print(missing!) // fatal error: unexpectedly found nil while unwrapping an Optional value
Force unwrapping should be reserved for cases where a nil value would genuinely indicate a programmer error — not as a routine way to silence the compiler.
Nil-coalescing operator
let username: String? = nil
let displayName = username ?? "Guest" // "Guest" — falls back when the optional is nil
print(displayName)
Control flow
let score = 75
if score >= 90 {
print("A")
} else if score >= 70 {
print("B")
} else {
print("C")
}
// switch — exhaustive, and does NOT fall through to the next case by default
switch score {
case 90...100:
print("A")
case 70..<90:
print("B")
default:
print("C")
}
for i in 1...5 { // closed range: 1,2,3,4,5
print(i)
}
for i in 1..<5 { // half-open range: 1,2,3,4
print(i)
}
var counter = 0
while counter < 3 {
print(counter)
counter += 1
}
Common mistakes
- Force-unwrapping (
!) an optional out of convenience without confirming it can't benil— this is the single most common source of crashes in real Swift codebases. - Forgetting that Swift's
switchdoesn't fall through by default (unlike C or Java) — each case is self-contained unless you explicitly writefallthrough. - Using
varreflexively for everything — preferletby default and let the compiler tell you when something genuinely needs to be mutable.
Interview questions
Q: Why did Swift introduce Optionals instead of allowing a variable to simply hold nil directly, like Objective-C?
Because Optionals make the possibility of a missing value part of the type system (String vs. String? are different types), forcing the compiler to require the value be unwrapped and checked before use. This eliminates an entire class of "nil messaging" crashes that were common and hard to trace in Objective-C.
Q: What's the difference between if let and guard let?
if let binds an unwrapped optional's value for use only within that if block's scope. guard let binds the value for use in the rest of the enclosing scope, but requires an early exit (return, break, continue, or throw) in its else branch — making it the idiomatic choice for validating preconditions at the top of a function without nesting the rest of the function inside an if.