Skip to content
tezvyn:

Top 30 iOS & Swift Interview Questions and Answers

30 multiple-choice questions on iOS & Swift, of the kind that come up in a technical interview, drawn from 30 bites in the iOS & Swift 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.

  1. Question 1 of 30

    What is the actual effect of declaring a class instance with let in Swift?

    Show the answer

    Answer: c · The reference cannot be reassigned, but the instance's properties can still be mutated

    For reference types, let freezes only the pointer, so the variable cannot be reassigned but the object's properties remain mutable. Option B is a common misconception that confuses the reference with the instance itself.

    Read the full bite: What is the difference between let and var in Swift?

  2. Question 2 of 30

    Which statement accurately describes a Swift Optional's implementation and a safe way to unwrap it?

    Show the answer

    Answer: b · It is a generic enum with Some and None cases, and it can be safely unwrapped using if let or the nil-coalescing operator.

    Swift models Optional as a generic enum with Some and None cases, enforcing nil safety at compile time. The most tempting distractor suggests force-unwrapping is standard, but the card warns that using ! is unsafe and only acceptable when non-nil status is guaranteed.

    Read the full bite: What is an optional in Swift? Demonstrate two safe unwrapping methods.

  3. Question 3 of 30

    In Swift, a developer omits the default case in a switch over an Int and covers only a few values. What happens?

    Show the answer

    Answer: c · It fails to compile because the switch is not exhaustive

    Swift requires switches to be exhaustive, so covering only some Int values without a default is a compile error. There is no implicit fallthrough and no break is needed, so the other options describe C behavior, not Swift.

    Read the full bite: How does Swift's switch differ from C's switch?

  4. Question 4 of 30

    When designing a Swift data model that requires independent undo snapshots without retroactive mutation, why is a struct preferred over a class?

    Show the answer

    Answer: c · Because assignment creates a unique copy, preventing shared references from mutating prior snapshots.

    Structs are value types, so each assignment copies the instance and prior snapshots remain independent. Option D is a common misconception because structs are not guaranteed to be stack-allocated, and option A is wrong since structs do not support inheritance.

    Read the full bite: Explain the primary differences between a struct and a class in Swift

  5. Question 5 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?

  6. Question 6 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?

  7. Question 7 of 30

    In Swift, how does declaring an instance with let affect property mutability for a struct compared to a class?

    Show the answer

    Answer: c · With let, a struct is fully immutable, but a class only fixes the reference so its variable properties can still be mutated.

    The card explains that let makes a struct fully immutable, while for a class it only fixes the reference, allowing variable properties to still be mutated. Option B is wrong because it assumes let behaves identically for both, which is a common misconception.

    Read the full bite: Swift struct vs class: differences and when to choose each

  8. Question 8 of 30

    In Swift, what is a key advantage of providing default behavior through a protocol extension rather than an abstract base class?

    Show the answer

    Answer: d · It enables structs and enums to reuse behavior without being forced into an inheritance hierarchy.

    Protocol extensions let structs and enums gain shared behavior without being forced into an inheritance hierarchy, unlike abstract base classes. They cannot add stored properties, so any answer suggesting they extend state is incorrect.

    Read the full bite: Explain protocols and how extensions provide default implementations

  9. Question 9 of 30

    Why does a stored closure property that references self inside its body cause a memory leak under ARC?

    Show the answer

    Answer: a · Because the instance holds the closure strongly while the closure captures self strongly, preventing deallocation

    The correct answer describes the mutual strong reference that prevents ARC from zeroing out either reference. Option D is a tempting distractor because it plays on the common misconception that closures might be value types, but they are reference types that strongly capture self by default.

    Read the full bite: What is a retain cycle in ARC with closures?

  10. Question 10 of 30

    When writing a swap function in Swift, what advantage does using a generic placeholder T provide over accepting parameters of type Any?

    Show the answer

    Answer: d · Generics enforce that both parameters are the same concrete type and avoid runtime casting

    Generics preserve compile-time type information, ensuring both arguments share the same type and eliminating unsafe downcasting. Distractor A is tempting because Any permits heterogeneous values, but a generic swap<T> explicitly prevents mixing types, which is exactly why it is type-safe.

    Read the full bite: What are Swift generics, why useful, and write a swap function?

  11. Question 11 of 30

    Which condition justifies using [unowned self] instead of [weak self] in a Swift closure?

    Show the answer

    Answer: a · The closure is owned by the instance and guaranteed not to execute after deallocation

    [unowned self] is only safe when the closure and instance share identical lifetimes, such as when the instance owns the closure and it cannot execute after deallocation. Option B is tempting because network handlers are common, but they require [weak self] since the user can dismiss the view controller before the response arrives, making unowned unsafe.

    Read the full bite: What is a closure capture list? Explain [weak self] versus [unowned self].

  12. Question 12 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

  13. Question 13 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?

  14. Question 14 of 30

    What is the primary reason to open an xcworkspace instead of the underlying xcodeproj when your app depends on a separate framework project?

    Show the answer

    Answer: d · The workspace provides the shared build directory and dependency resolution context

    The workspace establishes a shared build directory and dependency context that lets projects discover each other's products, which an individual project file cannot do. Option C is tempting because it sounds like a higher-level container might replace its contents, but a workspace always contains the underlying project files rather than replacing them.

    Read the full bite: Difference between .xcodeproj and .xcworkspace, and when to use a workspace

  15. Question 15 of 30

    Paused in LLDB with a UIView named header and an Int named count, which commands yield a human-readable view summary and the raw typed integer value?

    Show the answer

    Answer: c · po header and p count

    po header invokes description or debugDescription for a human-readable summary, while p count prints the raw value with type info and creates a persistent variable. Option B is tempting because p is a general evaluator, but it would likely output the UIView's pointer or struct layout instead of its friendly description.

    Read the full bite: What LLDB command prints a UIView description versus an Int?

  16. Question 16 of 30

    Which approach correctly creates a reusable Staging workflow without hardcoding environment details in Swift?

    Show the answer

    Answer: a · Duplicate the app scheme, create a Staging build configuration, pair them in the scheme editor, add the API URL as a launch environment variable, and share the scheme.

    A scheme orchestrates which build configuration to use and can inject runtime values via the Arguments tab; duplicating and sharing it to xcshareddata creates a reusable workflow without code changes. Option D misapplies SWIFT_ACTIVE_COMPILATION_CONDITIONS, which is a compile-time flag, not a mechanism for runtime environment variables like an API URL.

    Read the full bite: What is an Xcode Scheme? How do you configure a custom one?

  17. Question 17 of 30

    Why does a symbolic breakpoint on UIViewAlertForUnsatisfiableConstraints help debug Auto Layout in UIKit?

    Show the answer

    Answer: c · It resolves to a runtime address without needing UIKit source code and lets you trace back to your code

    A symbolic breakpoint stops on a function by name inside closed-source UIKit without requiring source code, and the backtrace reveals which of your methods added the conflicting constraints. Distractor D is tempting because both breakpoints halt execution, but exception breakpoints catch all thrown exceptions broadly, whereas a symbolic breakpoint is precisely targeted to a specific function entry.

    Read the full bite: Explain symbolic breakpoints and debug Auto Layout with one

  18. Question 18 of 30

    You are investigating rhythmic frame drops during table view scrolling. Which profiling strategy best isolates the specific subsystem causing the hitches?

    Show the answer

    Answer: c · Record with the Core Animation instrument to find hitch spikes, then cross-reference Time Profiler on the main thread during those frame boundaries

    Core Animation reveals frame-level hitch ratios and exact frame boundaries, while Time Profiler pinpoints main-thread blocking work during those frames; print statements lack frame precision and alter performance, while Leaks and raw CPU metrics do not correlate specific frames to code paths.

    Read the full bite: Which Instrument diagnoses scroll stuttering and dropped frames?

  19. Question 19 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?

  20. Question 20 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?

  21. Question 21 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?

  22. Question 22 of 30

    Which situation most justifies using frame-based layout instead of Auto Layout?

    Show the answer

    Answer: c · A custom view that redraws particle effects at 60 frames per second

    Frame-based layout avoids the runtime overhead of constraint solving, making it ideal for high-frequency updates like particle systems at 60 fps. Auto Layout is the right choice for adaptive scenarios such as split-screen multitasking, localized text expansion, and Dynamic Type.

    Read the full bite: When should you use frame-based layout versus Auto Layout?

  23. Question 23 of 30

    What is the immediate effect of mutating a @State property in a SwiftUI view?

    Show the answer

    Answer: d · SwiftUI marks the view as invalid and schedules an asynchronous body re-evaluation

    Mutating @State marks the view invalid and schedules an asynchronous re-evaluation of body rather than redrawing immediately. The synchronous redraw distractor is wrong because SwiftUI batches state changes and updates the render tree in a future pass, not on the next line of code.

    Read the full bite: What is @State in SwiftUI and how does it affect the view?

  24. Question 24 of 30

    Which approach correctly places a one-time network request and a bounds-dependent layout update in a UIViewController?

    Show the answer

    Answer: d · Start the request in viewDidLoad and update frames in viewDidLayoutSubviews

    viewDidLoad runs once after the view is created, making it the right place for an initial network request, while viewDidLayoutSubviews fires after Auto Layout resolves final bounds so frames are accurate there. Updating frames in viewDidLoad is a common mistake because safe area insets and final bounds are not yet guaranteed at that point.

    Read the full bite: UIViewController lifecycle states: network vs geometry updates

  25. Question 25 of 30

    When does UIKit allocate a new table or collection view cell during scrolling?

    Show the answer

    Answer: c · When the reuse pool has no cell with the requested identifier

    UIKit only creates a new cell when the reuse pool cannot supply one matching the requested identifier; the tempting belief that a new instance is created for every visible row ignores that scrolling primarily recycles existing cells.

    Read the full bite: How do UITableView and UICollectionView reuse cells?

  26. Question 26 of 30

    Which statement accurately describes a key architectural difference between UIKit Auto Layout and SwiftUI?

    Show the answer

    Answer: b · UIKit uses an external constraint solver while SwiftUI uses parent-child two-pass size negotiation.

    UIKit Auto Layout depends on an external solver to resolve linear equation constraints into frames, while SwiftUI layout is driven by an internal two-pass size negotiation between parent and child. Option C represents a common misconception; SwiftUI does not wrap or compile down to Auto Layout constraints but instead uses its own compositional layout engine.

    Read the full bite: Compare UIKit Auto Layout and SwiftUI layout systems

  27. Question 27 of 30

    A child view receives an ObservableObject from its parent, must update when it publishes changes, and must not manage its lifecycle. Which wrapper fits?

    Show the answer

    Answer: d · @ObservedObject, because the child receives a reference-type object it does not own

    @ObservedObject is correct because the child gets an externally created ObservableObject and must react to its updates without taking ownership. @Binding is tempting because the data comes from the parent, but it is meant for value types, not reference-type ObservableObjects.

    Read the full bite: Differences between @State, @Binding, @ObservedObject, and @EnvironmentObject

  28. Question 28 of 30

    When wrapping a delegate-based UIKit view for use in SwiftUI, what is the correct way to handle callbacks and avoid retain cycles?

    Show the answer

    Answer: a · Provide a Coordinator object via makeCoordinator and use it as the delegate

    A Coordinator provides a stable reference to handle delegate callbacks without retain cycles. Making the representable struct the delegate is wrong because structs cannot reliably act as reference-type delegates and it introduces mutability and lifecycle issues.

    Read the full bite: Integrate SwiftUI into UIKit and wrap UIKit for SwiftUI

  29. Question 29 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?

  30. Question 30 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?

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.

Get it on Google PlayiPhone app coming soon