iOS · 18 min
iOS interview questions in 2026, round by round
iOS interview questions for 2026, organised by round: Swift, concurrency, memory, SwiftUI, UIKit, architecture, system design, testing, coding and behavioural.
iOS · 14 min read
The top 10 iOS interview questions in 2026, with what each one scores, a weak answer, a senior answer and the follow-up that separates the two.
If you only prepare ten questions for an iOS loop, prepare these. They are the ones I have heard most often across more than 500 interviews on the hiring side, in phone screens, deep dives and senior rounds alike. None of them is exotic. That is the point: the common questions are where most of the scoring happens, because every candidate gets them and the interviewer can compare answers directly.
For each question you get four things: what the interviewer is scoring, a weak answer that sounds fine but earns little, a senior answer, and the follow-up that usually comes next. The follow-up is where the loop is decided. For a broader map by round, see the iOS interview questions hub.
Read the senior answer once, then close the page and say your own version out loud. Do not memorise mine. Interviewers hear rehearsed wording within a sentence, and it costs trust.
| Step | What to do | Time |
|---|---|---|
| 1 | Read the question and the scoring line | 1 minute |
| 2 | Answer aloud without looking | 2 minutes |
| 3 | Compare with the senior answer and note what you missed | 2 minutes |
| 4 | Answer the follow-up aloud | 2 minutes |
Ten questions at about seven minutes each is a little over an hour. That is a good evening’s work the week before a loop, and it will show you exactly which areas need the longer guides.
What is scored: value versus reference semantics, and a default you can defend.
Weak answer: “A struct lives on the stack, a class lives on the heap, so a struct is always faster.”
Senior answer: “The real difference is semantics. A struct is copied on assignment, so each owner has its own value and nobody can change it behind my back. A class is shared by reference, supports inheritance and has a lifetime managed by ARC. Where it lives in memory is an implementation detail; a struct inside a class or captured by an escaping closure ends up on the heap anyway. My default is a struct for models and a class when I need shared identity, like a cache or a coordinator, or when I need deinit. One thing I never claim is that a struct is thread safe by itself: value semantics only reach as deep as the stored properties, so a struct holding a class instance still shares that instance.”
The follow-up: “What happens when a struct contains an array of a million elements and you copy it?” A strong candidate explains copy on write: the buffer is shared until one copy mutates, so the copy itself is cheap.
What is scored: a mental model of reference counting, plus a debugging method.
Weak answer: “You add [weak self] to every closure.”
Senior answer: “A retain cycle is two or more objects keeping each other alive with strong references, so neither count drops to zero. The classic case is a view model that stores a closure which captures the view model strongly. I break it by making one side weak, on the reference that does not represent ownership. I do not add [weak self] everywhere by reflex, because a closure that runs once and is released, like a map, never forms a cycle. To find one, I use Xcode’s memory graph debugger, or put a log in deinit and check it fires when I leave the screen.”
The follow-up: “Does a Task that captures self create a cycle?” The senior answer: it keeps self alive until the task finishes, which is a lifetime problem rather than a permanent cycle, unless self also stores that task and the task never ends.
weak, unowned, or neither?”What is scored: reasoning about which object outlives which.
Weak answer: “Always weak, because unowned can crash.”
Senior answer: “Neither, when there is no cycle. weak when the other object may disappear first, like a delegate, since it becomes nil safely. unowned only when I can prove the other object lives at least as long as this one, such as a child that its parent owns and destroys. unowned saves the optional, but if my proof is wrong it crashes, so in code other people will change I lean toward weak.”
The follow-up: “Why are delegates usually weak?” Because the object that sets itself as delegate usually owns the delegating object, so a strong back reference would form a cycle.
The Swift and memory questions look basic, and that is why they are dangerous. Interviewers use them in the first ten minutes to decide how hard to push for the rest of the hour.
What is scored: the difference between a data race and a logical race.
Weak answer: “Actors make code thread safe.”
Senior answer: “An actor serialises access to its own state, so two tasks cannot touch that state at the same moment. That removes data races. It does not make multi-step logic atomic. Every await inside an actor method is a point where other calls can run on the actor, which is called reentrancy. If I check a cache, await a download, then write to the cache, another call may have written the same key in between. So after every await I re-check whatever I relied on, keep critical sections synchronous, or store the in-flight task and have later callers await it. And actors only guard their own isolated state: cancellation bugs and state outside the actor are still my problem.”
The follow-up: “Show me how you would deduplicate two concurrent requests for the same image.” The candidate should store a Task in a dictionary keyed by URL and return it to the second caller.
actor ThumbnailStore {
private var pending: [URL: Task<Data, Error>] = [:]
func load(_ url: URL) async throws -> Data {
if let existing = pending[url] { return try await existing.value }
let work = Task { let (bytes, _) = try await URLSession.shared.data(from: url); return bytes }
pending[url] = work
defer { pending[url] = nil }
return try await work.value
}
}@MainActor guarantee?”What is scored: isolation as a property of code.
Weak answer: “The whole function runs on the main thread, including the network call.”
Senior answer: “Synchronous code isolated to the main actor executes on the main thread, and the compiler prevents other domains from touching that state without an await. When a main actor method awaits a network call, it suspends and releases the main actor while the request runs elsewhere, then resumes on the main actor. What does block the main thread is heavy synchronous work in that method, like decoding a big payload. I move that off the main actor deliberately. Where the approachable concurrency settings are on, as in new Xcode 26 projects, a nonisolated async function stays on whichever actor called it, so I mark the work @concurrent to make the hop explicit.”
The follow-up: “How does the decoded value get back to the main actor safely?” It must be Sendable. A non-public struct whose fields are all sendable gets that conformance implicitly; a public one has to declare it.
When I write up a candidate, concurrency answers carry the most weight of anything on this page. A clear answer to question 4 tells me more about seniority than any other single question.
@State, @Binding, or an @Observable model?”What is scored: ownership, and why the wrong choice causes bugs.
Weak answer: “@State is for simple values and @Observable is for complex ones.”
Senior answer: “@State means this view owns the value and SwiftUI keeps it alive across redraws. @Binding means a parent owns it and I get read and write access. An @Observable class is a model whose reads are tracked per property, so a view usually redraws less than with ObservableObject. I do not say it only redraws for the changed property, though: a parent re-evaluating, an identity change or an environment change still invalidates the view. If a view creates the model, it holds it in @State; if the model is passed in, it is a plain property. The bug I look for is a view creating a model in a plain property, which recreates it every time the parent redraws and loses its state.”
The follow-up: “Why did a list of 500 rows get slow after one field changed?” Look for unstable identity in ForEach, or many rows reading one large model so a single change invalidates all of them.
What is scored: understanding how SwiftUI decides what is the same view.
Weak answer: “Each view has an id.”
Senior answer: “SwiftUI identifies views either structurally, by their position in the view tree, or explicitly, with id or the identifiers in ForEach. Identity decides the lifetime of state: if identity changes, @State is reset and .task restarts. An if/else that swaps two branches creates two different views, while a modifier with a changing value keeps one. Using an index or a random UUID as an id in a list breaks animations and state because identity changes when the data moves.”
The follow-up: “When would you change identity on purpose?” To reset a form or restart a task when the selected item changes, with .id(item.id).
What is scored: judgement and fit with the codebase, not pattern names.
Weak answer: “I would use MVVM because it is the best architecture.”
Senior answer: “First I follow what the app already does, because consistency beats my preference. Within that, I want a view that only renders state, a model or view model that owns state and handles user intent, and services behind protocols for network and storage so I can test with fakes. I would put the feature in its own module if the app is modular, with a small public interface. I would name one owner for each piece of state and decide early how navigation into the feature works, since deep links usually expose that.”
The follow-up: “The team wants to adopt TCA. What would you say?” A senior answer weighs testability and consistency against the learning curve, the dependency and hiring, and proposes a trial on one feature.
What is scored: source of truth, sync and conflict rules.
Weak answer: “Cache the API responses and show them when offline.”
Senior answer: “On the device, the database is the single source of truth, and every screen reads from it alone. Edits are written locally first, marked pending, and added to an outbox. A sync engine drains the outbox when the network is back, with retries, backoff and an idempotency key per change so a retry does not duplicate a note. For conflicts I would pick a rule and say it out loud. Whole-record last write wins is the trap here, because it silently throws away text someone typed on another device. I merge per field where fields are independent, and for the note body I keep both versions and ask the user which to keep. Losing user data is never an acceptable way to settle a conflict. The UI shows pending and failed states, so nobody thinks a note is saved on the server when it is not.”
The follow-up: “What happens when the user signs out with pending changes?” Warn, or flush first, and never silently drop data.
A good answer to either question in this section is mostly about constraints. Say what you are assuming, and the interviewer will tell you which assumption they want to change.
What is scored: ownership, method, and what changed afterwards.
Weak answer: “There was a crash, I found it and fixed it, and it was fine.”
Senior answer: “Give the impact first: who was affected and how badly. Then how you found out, how you narrowed it down, with crash logs, a reproduction, or a bisect. Then the fix, and the part most candidates skip: what you changed so the same class of bug cannot ship again, such as a test, a lint rule, or a staged rollout with a crash rate gate. Own your part in it without blaming anyone.”
The follow-up: “What would you do differently?” A real answer names a specific decision, not “communicate more”.
The behavioural question is last on this list, but it is not least. At senior level, I have seen a strong technical loop end in “no hire” because the candidate blamed a colleague in this answer.
@MainActor written on every type.@concurrent attribute marks a function that should run concurrently, which makes it visible in the code when work leaves the actor.Check any answer on this page against these docs before you repeat it in an interview. The APIs move each year, and the docs move with them.
Once these ten feel comfortable aloud, move to the top 50. The extra forty are where interviewers go when they want to test breadth after the core questions have landed.

iOS · 18 min
iOS interview questions for 2026, organised by round: Swift, concurrency, memory, SwiftUI, UIKit, architecture, system design, testing, coding and behavioural.

iOS · 16 min
Twelve senior iOS interview questions for 2026: what each scores, a weak and a senior answer, and the follow-up on Swift 6 concurrency, SwiftUI, architecture.

iOS · 13 min
Swift interview questions with spoken answers: value and reference types, protocols, generics, memory and errors, and the follow-up each one leads to.