Top 30 Advanced iOS & Swift Interview Questions and Answers
30 advanced multiple-choice iOS & Swift interview questions, the deep end: internals, failure modes, and the design calls a senior engineer is expected to defend. They come from 30 bites in the iOS & Swift library, the hardest slice of the 129 iOS & Swift interview questions in the library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.
SwiftUI, UIKit, Xcode, Swift language, Apple platforms
30 questions. Pick an answer, or open “Show the answer” to read it.
Answers are graded in your browser. Nothing is saved, and no XP or streak is earned here. The app keeps score.
Question 1 of 30
Why does indexing a Swift String by an integer like myString[5] not compile, unlike in many other languages?
Show the answer
Answer: c · Characters are variable-width grapheme clusters, so an integer offset cannot give O(1) or unambiguous access
Swift Characters are extended grapheme clusters of varying byte length, so an integer offset is neither constant-time nor meaningful, which is why String.Index is opaque. Immutability is unrelated, and String.Index is not an Int alias.
Read the full bite: Why can't you subscript a Swift String with an Int?
Question 2 of 30
What is the primary effect of marking a Swift function parameter with @autoclosure?
Show the answer
Answer: a · It wraps the argument expression in a closure so it evaluates lazily only when the function invokes it
@autoclosure defers evaluation by wrapping the expression in a closure the function may or may not call, as with assert and ??. It does not force eager evaluation, manage retention, or relax type checking.
Read the full bite: What is @autoclosure and when is it useful?
Question 3 of 30
When implementing Copy-on-Write for a custom Swift struct, what must happen inside a mutating method before modifying the backing reference?
Show the answer
Answer: b · Verify isKnownUniquelyReferenced on the backing instance and clone it if the result is false
Before mutating, you must check isKnownUniquelyReferenced and clone when it returns false, indicating shared ownership. Answer A dangerously inverts the boolean logic, while C and D reflect common misconceptions that either defeat reference sharing or eliminate the optimization entirely.
Read the full bite: Explain Copy-on-Write in Swift and implement it for custom structs
Question 4 of 30
In Swift structured concurrency, what distinguishes a Task started with Task.init from a child task created via async let?
Show the answer
Answer: d · Task.init creates an unstructured task without parent-child cancellation propagation, while async let creates a structured child task
Task.init creates an unstructured task outside the parent-child tree, whereas async let creates a structured child task bound to its parent's scope and cancellation. Claiming they differ only in syntax repeats the common misconception that async/await is mere syntactic sugar, ignoring the runtime contract of structured concurrency.
Read the full bite: How does Swift async/await improve on completion handlers?
Question 5 of 30
How does Xcode's Debug Memory Graph differ from the Leaks instrument in detecting memory issues?
Show the answer
Answer: a · Memory Graph provides a point-in-time heap snapshot for manual cycle inspection, while Leaks continuously scans for unreachable memory blocks.
The Memory Graph is a manual, point-in-time heap snapshot used to visually inspect reference patterns and cycles, whereas the Leaks instrument continuously samples allocations to automatically detect unreachable memory blocks. Option C reverses their methodologies, and option D incorrectly claims the Memory Graph auto-flags leaks.
Read the full bite: How does Debug Memory Graph find leaks and retain cycles?
Question 6 of 30
Which configuration lets Time Profiler symbolicate a true Release build without changing its runtime performance characteristics?
Show the answer
Answer: a · Set DEBUG_INFORMATION_FORMAT to DWARF with dSYM File, preserve the dSYM for Instruments, and keep compiler optimizations unchanged.
DWARF with dSYM File creates a separate debug bundle that Instruments uses for symbolication while leaving Release optimizations intact, so the profile reflects real user execution. Disabling optimizations (B) makes stacks readable but invalidates the profile because the code path no longer matches what ships to users.
Read the full bite: What build settings improve Time Profiler symbols in Release builds?
Question 7 of 30
After reproducing a background battery drain issue with Energy Log on a physical device, you see Location Services pinned at high power. Which approach is most appropriate?
Show the answer
Answer: d · Align the spike with background tasks or signposts, implement a targeted fix, and validate with a second Energy Log
Correct answer C follows the full diagnostic loop: correlate the Instruments spike to a specific app action with timestamps or signposts, apply a targeted fix, and prove improvement with a new Energy Log. Option B is tempting because lowering location accuracy is a legitimate optimization, but doing so preemptively without correlating the spike to a specific background task skips root-cause analysis and may not address the actual drain.
Read the full bite: How would you use Energy Log to investigate battery drain?
Question 8 of 30
Two adjacent labels in a row do not fit together. You want the first to stay fully visible and the second to truncate. Which adjustment achieves this?
Show the answer
Answer: a · Raise the first label's compression resistance priority above the second's
Compression resistance resists shrinking below intrinsic size, so giving the first label higher compression resistance forces the second to truncate. Hugging governs growth, not truncation, and making both required would create an unsatisfiable conflict.
Read the full bite: How does Auto Layout resolve constraints?
Question 9 of 30
When implementing the SwiftUI Layout protocol, what is the correct division of responsibility between sizeThatFits and placeSubviews?
Show the answer
Answer: b · sizeThatFits reports the container's required size for a proposal; placeSubviews positions each subview within the granted bounds
The protocol uses a measure-then-place model: sizeThatFits returns the size the container needs, and placeSubviews positions children inside the bounds the parent grants. The roles are not reversed, and neither method draws or animates views.
Read the full bite: How do you build a custom SwiftUI Layout?
Question 10 of 30
An app with many dynamic frameworks launches slowly even though its didFinishLaunching is lightweight. What is the most likely cause?
Show the answer
Answer: c · Pre-main dyld work loading, rebasing, and binding the many dynamic libraries dominates launch time
Each dynamic framework adds dyld loading, rebasing, and binding cost that runs before main, so a heavy framework count slows pre-main launch even when your own startup code is light. The other options describe post-main or unrelated issues.
Read the full bite: Diagnose and fix slow iOS app launch time
Question 11 of 30
Why must every container view controller have a non-nil restorationIdentifier during UIKit state restoration?
Show the answer
Answer: d · UIKit uses them to reconstruct the graph structure, so a missing identifier loses the entire branch
UIKit uses restorationIdentifiers to encode the view controller hierarchy into a keyed archive, so a missing identifier on any container severs that branch entirely. Option C reflects the common misconception that restoration relies on manual visual snapshots rather than encoded graph archives.
Read the full bite: Outline the key steps and APIs for State Preservation and Restoration
Question 12 of 30
What is the main trade-off when choosing a unidirectional architecture like TCA over MVVM for a large iOS app?
Show the answer
Answer: d · TCA gains predictable, traceable, testable state transitions at the cost of more boilerplate and a steeper learning curve
Funneling all mutations through actions and pure reducers makes state explicit and testable but adds boilerplate and concepts, which is the core trade-off. MVVM works with SwiftUI and is testable, and TCA still manages state.
Read the full bite: Compare MVVM with unidirectional flow (TCA, Redux)
Question 13 of 30
Which architectural change in NavigationStack fundamentally fixes the fragility of deep linking and multi-layer pushes that plagued NavigationView?
Show the answer
Answer: b · Replacing per-link isActive boolean bindings with a single explicit array representing the entire route stack.
NavigationStack replaces scattered isActive bindings with a centralized path array, enabling reliable deep linking by mutating a single data structure. Option A reflects the common misconception that NavigationStack is merely a visual or naming update, when in fact it fundamentally changes how navigation state is modeled.
Question 14 of 30
Which architecture best decouples auth state from the view hierarchy while ensuring smooth transitions and no memory leaks?
Show the answer
Answer: b · Use an app coordinator to swap the window rootViewController on auth changes, crossfade with a snapshot, and nil the outgoing stack.
A dedicated coordinator that swaps the window root and releases the old hierarchy prevents retain cycles and keeps auth logic out of the app delegate. Presenting onboarding modally over the tab bar (D) tightly couples the two flows and forces the main interface to know about authentication state.
Read the full bite: How would you architect the app root for onboarding vs authenticated flows?
Question 15 of 30
Which approach correctly separates navigation from presentation in a unit-testable conditional wizard for both UIKit and SwiftUI?
Show the answer
Answer: c · Model the flow as a state machine enum with plain Swift transition logic and derive the UIKit and SwiftUI stacks from that state.
Modeling the wizard as a state machine enum with plain Swift transition logic lets you derive the stack for both UIKit and SwiftUI and unit test transitions without launching a UI. Option B is tempting because coordinators correctly separate UIKit concerns, but relying on local @State inside onAppear handlers still buries imperative navigation inside the view rather than driving it declaratively from the model.
Read the full bite: Design state-driven navigation for a conditional wizard in UIKit and SwiftUI
Question 16 of 30
A list backed by NSFetchedResultsController stutters while scrolling and fires thousands of SQL queries. Which fix most directly addresses the query count?
Show the answer
Answer: d · Set relationshipKeyPathsForPrefetching to batch-load the related objects each row displays
Thousands of queries indicate per-row relationship faulting, the N+1 problem, which prefetching collapses into batched fetches. Loading all rows at once hurts memory, disabling reuse worsens performance, and UIKit must stay on the main thread.
Read the full bite: Optimize a slow NSFetchedResultsController screen
Question 17 of 30
After running an NSBatchInsertRequest on a background context, the imported rows do not appear in the on-screen list. Why?
Show the answer
Answer: b · Batch inserts write directly to the store and do not automatically update in-memory contexts, so changes must be merged or refetched
Because batch inserts bypass the object graph and write straight to the store, running contexts are unaware until you merge the changes or refetch. They intentionally skip validation, work with SQLite, and do not fail silently here.
Read the full bite: Bulk-import large JSON into Core Data efficiently
Question 18 of 30
In an offline-first app, why is naive last-write-wins by timestamp often an inadequate conflict resolution strategy?
Show the answer
Answer: a · It requires the device clocks to be perfectly synced and can silently discard concurrent edits
Last-write-wins relies on comparable timestamps, so clock skew is risky, and it overwrites the losing edit entirely, silently losing concurrent changes. It needs no websocket, applies to writable data, and does not double storage.
Read the full bite: Design offline-first sync and conflict resolution
Question 19 of 30
A long-running async function does heavy CPU work in a tight loop with no awaits. What must you add so calling cancel() actually stops it early?
Show the answer
Answer: d · Periodic checks of Task.isCancelled or try Task.checkCancellation()
Cancellation is cooperative, so a loop with no suspension points never observes it unless you poll the flag yourself. cancel() does not forcibly terminate running code.
Read the full bite: How task cancellation works with async/await
Question 20 of 30
Two tasks call an actor method that checks a token and refreshes it if expired. Why might both end up issuing a network refresh despite actor isolation?
Show the answer
Answer: d · Actor reentrancy lets the second task enter while the first is suspended at an await
At an await suspension point the actor can admit another task, so both can pass the expiry check before either finishes refreshing. Actors do serialize, but reentrancy across awaits breaks the assumption of exclusive end-to-end execution.
Read the full bite: What is an actor and how does it prevent data races
Question 21 of 30
While profiling, the Color Offscreen-Rendered Yellow overlay highlights your shadowed, rounded cards. Which change most directly removes the off-screen pass?
Show the answer
Answer: a · Provide an explicit shadowPath so the shadow shape need not be computed off-screen
A shadow without a shadowPath forces an off-screen pass to derive its shape from the alpha channel; supplying shadowPath gives a known shape and eliminates that pass. Blanket shouldRasterize can backfire on changing content and does not address the root cause.
Read the full bite: Diagnose dropped frames during animation
Question 22 of 30
In a swipe-driven dismissal, which object is responsible for mapping the pan gesture's progress to the transition and deciding to finish or cancel on release?
Show the answer
Answer: d · The interaction controller conforming to UIViewControllerInteractiveTransitioning
The interaction controller, typically a UIPercentDrivenInteractiveTransition, scrubs progress with update and resolves with finish or cancel. The animator only defines what the transition looks like, not how the gesture drives it.
Read the full bite: Build an interruptible custom VC transition
Question 23 of 30
Why can't SwiftUI animate directly between a triangle Shape and a pentagon Shape, and what makes animatableData work instead?
Show the answer
Answer: b · There is no vertex correspondence between mismatched paths; animatableData interpolates one numeric parameter that regenerates the path
Two paths with different point counts have no defined point-to-point mapping to tween. animatableData exposes a single VectorArithmetic value SwiftUI interpolates, and path(in:) rebuilds geometry for each intermediate value, producing the morph.
Read the full bite: Animate a SwiftUI Shape morphing point count
Question 24 of 30
For real-time CIFilter processing of camera frames, which choice of dispatch queue for the sample buffer delegate avoids stalling capture and freezing the UI?
Show the answer
Answer: b · A dedicated serial background queue, keeping per-frame work off the main thread
A dedicated serial background queue processes frames in order without blocking the UI; the main queue would freeze the interface and stall capture. A concurrent queue risks out-of-order frames and races on shared filter state.
Question 25 of 30
A user grants location access in Settings while your app is suspended. How does your app reliably learn this and start updates without user action inside the app?
Show the answer
Answer: b · locationManagerDidChangeAuthorization fires when the app resumes with a delegate-set manager, where you start updates
A suspended app cannot run code; once it resumes with a CLLocationManager whose delegate is set, locationManagerDidChangeAuthorization is invoked with the new status, and you start updates there. Polling is unnecessary and nothing runs during true suspension.
Read the full bite: React to location authorization changed in Settings
Question 26 of 30
After your Core ML model detects an object in the ARKit camera image, what is the core difficulty in placing a 3D anchor at its location?
Show the answer
Answer: c · A 2D image point defines a ray, not a unique 3D point, so depth must be resolved via raycasting or scene depth
A detection gives a 2D location, which back-projects to an infinite ray; you need plane raycasting or LiDAR depth to pin the actual distance. ARKit and Core ML coexist fine, and anchors can be placed anywhere with a valid transform.
Read the full bite: Recognize objects in ARKit with a Core ML model
Question 27 of 30
Which approach best stabilizes a flaky XCUITest that taps an element before it has appeared?
Show the answer
Answer: a · Wait on the element's existence with an explicit timeout before tapping
Waiting on the element's existence makes the test act only when the UI is ready, fixing the race deterministically. Fixed sleeps are probabilistic and slow, and rerun-until-pass merely hides real failures.
Read the full bite: Diagnosing and stabilizing flaky XCUITests
Question 28 of 30
What is the main advantage of seeding login state through launchArguments instead of automating the login screen in each test?
Show the answer
Answer: a · Tests start in the needed state instantly and stay decoupled from the auth UI
Injecting state at launch puts each test in its precondition instantly without re-driving the login UI, so unrelated tests no longer break when sign-in changes. It does not affect coverage, animations, or eliminate all flakiness.
Read the full bite: Driving app state via launch arguments in XCUITest
Question 29 of 30
Why does adding a package as a local dependency let you see code changes immediately?
Show the answer
Answer: c · Xcode references the source files on disk instead of a resolved Git revision
A local package points Xcode at the on-disk source, so edits compile into the next build with no commit or version bump. It is not about a special daemon, skipping the linker, or patching a cached remote copy.
Read the full bite: Developing a local Swift Package alongside an app
Question 30 of 30
Which App Thinning mechanism directly reduces the size a specific device downloads by delivering only its matching assets and architecture?
Show the answer
Answer: c · Slicing
Slicing delivers a device-tailored variant containing only the needed architecture and asset resolutions. Bitcode enabled server-side recompilation rather than trimming downloads, on-demand resources defer specific tagged bundles, and signing is unrelated.
Read the full bite: App Thinning: slicing, bitcode, and on-demand resources
Could you explain these out loud?
That is what an interview actually tests. Tezvyn gives you questions like these with what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.