Swift Interview Questions and Answers
Optionals, protocols, ARC, structured concurrency and iOS fundamentals.
Whether you are preparing for entry-level Swift interview questions for freshers or senior software engineer interview questions addressing concurrency, scalability, and system architecture, this track provides peer-reviewed model answers with syntax walkthroughs, edge cases, and practical interview tips.
1 What are closures and how do capture lists work? Medium
A closure is a self-contained block of functionality that can be passed around and captures values from its surrounding context. Functions are a special case of closures.
let add = { (a: Int, b: Int) -> Int in a + b }
let names = ["Ada", "Grace"].sorted { $0 < $1 }
Syntax tools include trailing closure syntax and shorthand argument names such as $0 and $1. Capture lists control how references are captured: [weak self] or [unowned self] prevent retain cycles when a closure is stored by the object it references.
Closures are reference types, so capturing a variable captures the box that holds it. Escaping closures are annotated @escaping and outlive the function call; non-escaping is the default and allows compiler optimisations. Use closures for completion handlers, callbacks and functional pipelines like map and filter.
2 Explain ARC and how retain cycles happen. Medium
Swift uses Automatic Reference Counting: each class instance has a reference count and is deallocated when the count reaches zero. ARC is deterministic, unlike a tracing garbage collector, but it only manages references, not cycles.
A retain cycle occurs when two objects strongly reference each other, so neither count reaches zero and memory leaks.
class Node {
var next: Node?
weak var parent: Node? // breaks the cycle
}
Break cycles with weak (optional, becomes nil when the target deallocates) for a child or back reference, or unowned (non-optional, assumes the target outlives the reference and traps otherwise). In closures use a capture list: [weak self] in self?.doWork(). Delegates should be declared weak var delegate.
Value types do not participate in cycles. Find leaks with Instruments' Leaks template and the memory graph debugger.
3 How do protocols and protocol extensions enable protocol-oriented programming? Medium
A protocol declares requirements such as properties, methods, initialisers or associated types that conforming types implement. Swift encourages protocol-oriented programming: define behaviour once in extensions and compose protocols instead of building deep inheritance trees.
protocol Shape {
var area: Double { get }
}
extension Shape {
var description: String { "area: \(area)" } // default implementation
}
struct Circle: Shape {
let r: Double
var area: Double { .pi * r * r }
}
Extensions can add default methods, computed properties and conformance to other protocols. Protocols with associated types cannot be used as existential types before Swift 5.7's any; previously you used generics or type erasure.
Protocols support inheritance and composition such as some Shape & Codable. Prefer them for testability, dependency injection and sharing behaviour across unrelated types.
4 When should you use guard instead of if let? Medium
Both unwrap optionals, but guard is designed for early exit and keeps the happy path unindented. Its else branch must exit the current scope with return, break, continue or throw, and the unwrapped value stays available afterwards.
func greet(_ name: String?) {
guard let name, !name.isEmpty else { return }
print("Hello, \(name)") // name is available here
}
With if let, the binding is visible only inside the if block, and nested checks cause pyramid indentation. Use guard to validate preconditions at the top of a function and if let for optional logic in the middle.
guard also works with guard case, with guard let self inside closures, and can unwrap several optionals in one statement. Since Swift 5.7 you can write if let name or guard let name without repeating the variable name, which keeps validation concise.
5 How does error handling work in Swift? Medium
Swift uses throws functions and try for recoverable errors. Errors conform to the Error protocol, usually as enums.
enum NetworkError: Error {
case badStatus(Int)
case noData
}
func load() throws -> Data {
guard status == 200 else { throw NetworkError.badStatus(status) }
guard let data else { throw NetworkError.noData }
return data
}
do {
let data = try load()
} catch NetworkError.badStatus(let code) {
print(code)
} catch {
print(error)
}
try? converts a thrown error into an optional and discards the error; try! crashes on error and should be avoided. Functions that call throwing functions must be throws or handle errors with do/catch.
Use defer for cleanup that must run on both success and failure. Since Swift 5.5 you also get async throws. Prefer typed errors and exhaustive catch for recovery, and use Result when you need to store or pass the outcome.
6 What are lazy properties and property observers? Medium
A stored property can run code when it is set or read through observers and modifiers.
willSetanddidSetobserve changes to a stored property.willSetreceives the new value,didSetthe old, and neither fires during initialisation.lazydefers creation until first access. It is useful for expensive setup that depends on other properties, and the property must bevar.
class ViewModel {
lazy var formatter = DateFormatter()
var score = 0 {
didSet {
print("changed from \(oldValue) to \(score)")
}
}
}
Computed properties with get and set do not store a value and are recalculated on each access; a get-only computed property is read-only. Observers are not called when setting from within the initialiser or from a designated initialiser delegation.
Lazy properties are not thread-safe by themselves. Use observers for lightweight side effects, not heavy work.
Frequently Asked Questions About Swift Interviews
What do hiring managers evaluate in Swift technical rounds?
Technical interviewers look for foundational fluency, idiomatic syntax, clarity when communicating complex logic, and awareness of performance trade-offs (e.g. memory footprint, render performance, and network latency) in production environments.
What are the best interview tips for practicing Swift questions?
Use active recall: summarize each answer in your own words before revealing the model solution. Focus on explaining why a certain approach is chosen rather than just memorizing code syntax.