RecallDeck
Interview track

iOS Developer interview prep

A spaced-repetition deck of 153+ iOS Developer interview questions — organised by topic and difficulty, and resurfaced right before you'd forget. Preview a few cards below, then sign in to study the whole track on an Anki-style SM-2 schedule.

153 cards10 topics

Free · sign in with GitHub · your progress stays yours.

What's covered

Every topic in this track, grouped the way you'd study it.

Swift & the Type System

12 cards
Swift & Types

Memory & ARC

9 cards
Memory & ARC

Concurrency: GCD & Swift Concurrency

11 cards
GCD & Swift Concurrency

UIKit & Layout

10 cards
UIKit & Layout

SwiftUI

8 cards
SwiftUI

iOS App Architecture

8 cards
Architecture

Networking & Persistence

9 cards
Networking & Persistence

Performance & Tooling

8 cards
Performance & Tooling

CS Fundamentals

43 cards
Data Structures & Algorithms

Behavioral

35 cards
Behavioral

Sample questions

A few cards from the deck — reveal each answer, then sign in to study the full set on a schedule.

What are the real differences between a struct and a class in Swift?

Short answer: A struct is a value type: it is copied on assignment and when passed to a function. A class is a reference type: variables share one instance. Inheritance, deinit and identity (===) exist only for classes; mutating a struct requires var and mutating methods.

In depth:

struct class
Semantics copy of the value shared reference
Inheritance ✗ (protocols only)
deinit / ===
Mutation mutating + var always, even via let
ARC no retain/release (unless it holds references) yes
Initializer memberwise for free you write it
  1. Default to struct — predictable copying, no races on shared state, cheaper for ARC.
  2. Class — when you need shared identity (a cache, a service), inheritance from the NSObject world, or deinit to release a resource.

⚠️ Common mistake: "structs always live on the stack." No: a struct stored in a class property, captured by an escaping closure, or boxed in an existential with a large payload lives on the heap. Value type defines copy semantics, not memory placement.

How does ARC work, and how is it different from a garbage collector?

Short answer: ARC is automatic reference counting: the compiler inserts retain/release calls around strong-reference operations at compile time. An object dies exactly when its reference count hits zero — deterministically and with no pauses. The price: ARC cannot collect reference cycles.

In depth:

ARC Tracing GC
Who decides compiler: inserts retain/release runtime: traverses the object graph
Moment of release deterministic: count = 0 non-deterministic, at collection time
Pauses none stop-the-world / incremental
Cycles not collected — needs weak/unowned collected automatically
Overhead atomic count increments heap headroom + CPU for tracing
  1. Compiler, not runtime — a retain is inserted when a new strong reference appears, a release when it leaves scope; the optimizer removes redundant matched pairs.
  2. Deterministic deinit — the resource is freed immediately, which is why deinit is suitable for closing files, invalidating timers, unsubscribing.
  3. The price — retain cycles remain the programmer's responsibility.

⚠️ Common mistake: calling ARC "a garbage collector, just faster." ARC doesn't scan memory at runtime — all the work is placed by the compiler ahead of time, which is why there are no pauses and why cycles don't get found on their own.

How does a serial queue differ from a concurrent one, what are main and global — and why is a queue not a thread?

Short answer: A queue is a "list of tasks" abstraction, not a thread: GCD multiplexes queues over a shared thread pool. A serial queue runs tasks strictly one at a time in FIFO order; a concurrent one runs several in parallel. Main is the only queue with the contract "I execute on the main thread"; global queues are system concurrent queues, one per QoS class.

In depth:

Queue Kind What's special
DispatchQueue.main serial bound to the main thread; all UI
DispatchQueue.global(qos:) concurrent system-owned, shared app-wide
DispatchQueue(label:) serial by default your own; attributes: .concurrent makes it parallel
QoS What it's for
.userInteractive animations, gesture response — "right now"
.userInitiated the user is waiting for a result (opening a screen)
.utility long work with progress (file download)
.background invisible work: sync, indexing
  1. Queue ≠ thread — a serial queue doesn't own a thread: its tasks may run on different pool threads, just never simultaneously.
  2. QoS drives scheduling — QoS tells the system how much CPU and energy to grant: .userInteractive jumps the line, .background can wait.

⚠️ Common mistake: "creating a queue creates a thread." No: thousands of queues are served by a dozen pool threads. But blocking many threads with sync waits is the road to thread explosion.

The UIViewController lifecycle: in what order are the methods called, and what belongs where?

Short answer: loadView creates the view → viewDidLoad — one-time setup (no geometry yet!) → viewWillAppear — refresh before every presentation → viewWillLayoutSubviews/viewDidLayoutSubviews — the only places where frames are valid → viewDidAppear — animations and expensive starts → viewWillDisappear/viewDidDisappear.

In depth:

loadView ─► viewDidLoad ─► viewWillAppear ─► viewWillLayoutSubviews
 (create     (once)         (every show)            │ layout pass
  view)                                             ▼
viewDidDisappear ◄─ viewWillDisappear ◄─ viewDidAppear ◄─ viewDidLayoutSubviews
                                          (on screen)      (frames are valid!)
  1. viewDidLoad — once per view controller's life: add subviews, constraints, subscriptions. The view has no real size yet.
  2. viewWillAppear — every presentation: refresh data, bar state.
  3. viewDidLayoutSubviews — bounds are current: corner rounding, CAShapeLayer paths, size-dependent layout. Called many times — keep it cheap.
  4. viewDidAppear — the view is on screen: start animations, expensive work, analytics.
  5. viewWillDisappear/viewDidDisappear — stop timers, save state.

⚠️ Common mistake: reading view.bounds in viewDidLoad — you get the storyboard size or zeros, not the device's real geometry. Anything size-dependent belongs in viewDidLayoutSubviews.

Why are SwiftUI Views structs, and why isn't constantly recreating them expensive?

Short answer: A View is not a view in the UIKit sense but a lightweight value-description: a blueprint of what to show. Structs are cheap to create and throw away, so SwiftUI freely recreates them on every body recomputation. The long-lived part — the render tree (attribute graph) — lives behind the scenes; that's where state and identity are kept.

In depth:

  1. Blueprint, not instance — a UIView is a heavyweight object with a lifecycle and a layer; a View struct is a couple of fields plus body, holding no resources.
  2. State lives outside — @State is stored by the framework and attached to the view's identity in the tree, not to the struct: the struct dies, the value survives.
  3. Diffing — SwiftUI compares the new description with the old one and minimally mutates the persistent tree; only the difference reaches the pixels.
body → View values (cheap, recreated every time)
            │ diff

attribute graph / render tree (long-lived:
@State, identity, animations)

⚠️ Common mistake: equating "recreating structs" with "redrawing the UI." Producing descriptions costs pennies; rendering is what can be expensive, and it happens only where the diff found a change.

Why does Apple-style MVC turn into Massive View Controller, and how do you slim a VC without changing the architecture?

Short answer: Because in UIKit the view controller is the default assembly point for everything: view lifecycle, layout, the table's data source, navigation and network calls. You slim it by extracting responsibilities into separate objects — no architecture change required.

In depth:

  1. Diagnosis — Apple's MVC isn't bad per se; the problem is that the "C" happily accepts any code, and without discipline the view controller grows to thousands of lines.
  2. What to extract:
Responsibility Where it goes
UITableViewDataSource / delegate a dedicated data source object
A screen section with its own logic a child view controller
Networking, cache, analytics plain service objects
Subview configuration the views themselves (configure(with:))
  1. Result — the view controller stays a conductor: it wires services, views and navigation together but doesn't do all the work itself.

⚠️ Common mistake: answering "MVC is a bad architecture, that's why everyone moved to MVVM." What's bad is an undisciplined implementation: without extracting responsibilities the same overload happens to a ViewModel too.

Ready to make it stick?

Start your first session in under a minute. Your future self, mid-interview, will thank you.

Questions about this track

How should I prepare for a iOS Developer interview?

Study the concepts you'll be asked to explain, not just the ones you can code. RecallDeck's iOS Developer track gives you 153+ curated interview questions and resurfaces each one with an Anki-style SM-2 schedule right before you'd forget it — so the answers are still there under pressure on interview day.

What topics does the iOS Developer track cover?

The iOS Developer track is organised into the core areas iOS Developer interviews actually test, grouped by topic and by difficulty (Concept, Junior, Middle, Senior). You can preview the full outline and sample questions above before signing in.

Is spaced repetition effective for iOS Developer interview prep?

Yes. Actively recalling an answer and grading yourself honestly builds far more durable memory than re-reading notes. RecallDeck schedules each iOS Developer card to reappear at the moment you're about to forget it, so your daily reviews shrink while your recall holds.

Is the iOS Developer track free?

Yes — the iOS Developer track and the full SM-2 scheduler are free, with 20 new cards a day. Sign in with GitHub and your progress syncs to your account. RecallDeck Pro ($5/month or $29/year) raises the daily budget and adds cram mode.

Other interview tracks

RecallDeckSpaced-repetition interview prep