← All courses

iOS Interview Questions & Answers

🗓 May 31, 2026 ⏱ 21 min read

This set covers 88 iOS interview questions with point-wise answers, written from a working iOS developer's point of view. Each answer is short, focused, and structured the way you should actually speak in an interview.

Swift Language Fundamentals

1. What is the difference between let and var in Swift?

  • let declares a constant — its value cannot be reassigned after initialisation.
  • var declares a variable that can be changed later.
  • Prefer let by default; it makes intent clear and lets the compiler optimise and catch accidental mutation.
  • For reference types, let only fixes the reference, not the object's internal mutable state.

2. What are optionals and why does Swift use them?

  • An optional (T?) represents a value that may be present or absent (nil).
  • They make the absence of a value explicit in the type system, eliminating null-pointer surprises.
  • You must unwrap an optional before using it, forcing you to handle the nil case.
  • Under the hood it is an enum with .some(value) and .none cases.

3. What are the safe ways to unwrap an optional?

  • if let / guard let for conditional binding.
  • Nil-coalescing ?? to provide a default value.
  • Optional chaining obj?.property to short-circuit safely.
  • Avoid force unwrap ! unless you can guarantee the value is non-nil.

4. What is the difference between if let and guard let?

  • if let creates a scope where the unwrapped value is valid only inside the braces.
  • guard let unwraps and keeps the value available for the rest of the function, exiting early on nil.
  • guard improves readability by handling failure first and avoiding deep nesting.
  • Use guard for preconditions; use if let for optional, localised branches.

5. What is the difference between value types and reference types?

  • Value types (struct, enum, tuple) are copied on assignment — each variable owns its data.
  • Reference types (class, closure) share a single instance via a pointer.
  • Value types reduce shared mutable state and are thread-safer by default.
  • Swift favours structs; use classes only when identity, inheritance, or shared state is needed.

6. Why are structs preferred over classes in Swift?

  • Value semantics avoid unintended sharing and many concurrency bugs.
  • No ARC overhead, so they are often faster and stack-allocated.
  • They encourage immutability and predictable behaviour.
  • Use a class only when you need reference identity, inheritance, or deinit.

7. What is the difference between struct and class?

  • Struct is a value type; class is a reference type.
  • Classes support inheritance and deinit; structs do not.
  • Structs get a free memberwise initialiser; classes do not.
  • Copying a struct duplicates data; copying a class copies only the reference.

8. What are computed properties versus stored properties?

  • A stored property holds a value directly in memory.
  • A computed property runs code to return (and optionally set) a value each time.
  • Computed properties cannot be assigned a default value and have no backing storage.
  • Use lazy stored properties to defer expensive initialisation.

9. What are property observers willSet and didSet?

  • willSet runs just before a stored property changes; didSet runs right after.
  • They are useful for reacting to state changes, e.g. reloading a view.
  • They do not fire during initialisation, only on later assignments.
  • Avoid heavy work inside them to keep assignments cheap.

10. What is the difference between Array, Set, and Dictionary?

  • Array is an ordered collection allowing duplicates.
  • Set is an unordered collection of unique values with fast membership checks.
  • Dictionary is an unordered collection of key-value pairs.
  • Sets and dictionary keys require Hashable conformance.

11. What does the mutating keyword do?

  • It allows a method on a value type to modify its own properties.
  • Without it, struct/enum methods cannot change self.
  • It signals to callers that the method changes the instance's state.
  • You cannot call a mutating method on a let constant instance.

12. What are enums with associated values?

  • Enum cases can carry extra data, e.g. case success(Data) / case failure(Error).
  • They model finite states cleanly and are pattern-matched with switch.
  • Associated values differ per instance; raw values are fixed per case.
  • They are great for representing results, view states, and API responses.

13. What is the difference between raw values and associated values in enums?

  • Raw values are constant, same-typed values attached to each case (e.g. Int, String).
  • Associated values are per-instance payloads that can differ each time.
  • Raw-value enums get a failable initialiser init?(rawValue:).
  • An enum can use one or the other, not both simultaneously.

14. What is type inference in Swift?

  • The compiler deduces a variable's type from its initial value.
  • It reduces boilerplate while staying strongly typed.
  • Explicit annotations are still useful for clarity or to force a specific type.
  • Inference happens at compile time, so there is no runtime cost.

15. What are tuples and when would you use them?

  • A tuple groups multiple values into one compound value.
  • Useful for returning several values from a function without a dedicated type.
  • Elements can be named for readability, e.g. (code: 200, message: "OK").
  • For anything passed around widely, prefer a struct over a tuple.

Memory Management & ARC

16. How does ARC (Automatic Reference Counting) work?

  • ARC tracks how many strong references point to each class instance.
  • When the count drops to zero, the instance is deallocated automatically.
  • It works at compile time by inserting retain/release calls, not a runtime garbage collector.
  • ARC applies only to reference types (classes), not structs or enums.

17. What is a retain cycle and how do you break it?

  • A retain cycle occurs when two objects hold strong references to each other, so neither is freed.
  • It causes memory leaks because the reference count never reaches zero.
  • Break it by making one reference weak or unowned.
  • Common cases: delegates, closures capturing self, parent-child object links.

18. What is the difference between weak and unowned?

  • weak references become nil when the target is deallocated and must be optional.
  • unowned assumes the target outlives the reference and is non-optional.
  • Use weak when the other object can legitimately disappear (e.g. delegates).
  • Use unowned only when you are certain the lifetime guarantees hold, else it crashes.

19. Why do delegates use weak references?

  • The delegating object usually does not own its delegate.
  • A strong delegate reference would create a retain cycle with its owner.
  • weak var delegate lets the delegate deallocate normally.
  • This is the standard pattern for view-controller and view callbacks.

20. How do you avoid retain cycles in closures?

  • Use a capture list [weak self] or [unowned self].
  • With [weak self], unwrap with guard let self else { return }.
  • Closures are reference types, so capturing self strongly keeps it alive.
  • Escaping closures stored as properties are the most common cycle source.

21. What is the difference between a strong, weak, and unowned capture in a closure?

  • Strong (default) keeps the captured object alive as long as the closure lives.
  • Weak makes the capture optional and nil-safe if the object dies.
  • Unowned is non-optional and crashes if accessed after deallocation.
  • Choose based on whether the captured object can outlive the closure.

22. What does deinit do?

  • deinit runs automatically just before a class instance is deallocated.
  • Use it to release resources like observers, timers, or file handles.
  • Only classes have deinit; value types do not.
  • Seeing (or not seeing) deinit fire is a quick way to detect leaks.

23. What is the difference between @escaping and non-escaping closures?

  • A non-escaping closure is called before the function returns and cannot outlive it.
  • An @escaping closure may be stored and called later, e.g. completion handlers.
  • Escaping closures require explicit self capture, surfacing retain-cycle risk.
  • Non-escaping is the default and allows compiler optimisations.

24. What is a memory leak and how do you detect it in iOS?

  • A leak is memory that is never released because references still point to it.
  • In iOS, retain cycles are the usual cause.
  • Detect with Instruments' Leaks/Allocations tools and the Memory Graph Debugger.
  • Confirm by checking whether deinit is called when an object should be freed.

25. What is copy-on-write in Swift?

  • Value-type collections share storage until one copy is mutated.
  • On mutation, Swift makes a real copy, avoiding unnecessary duplication.
  • It gives value semantics with the performance of references for reads.
  • Standard types like Array, String, and Dictionary use it internally.

Protocols, Generics & Closures

26. What is a protocol and how is it used?

  • A protocol defines a contract of methods and properties a type must implement.
  • It enables polymorphism without inheritance and supports value types.
  • Types adopt protocols to guarantee shared behaviour, e.g. Equatable.
  • Protocols are central to delegation and protocol-oriented programming.

27. What is protocol-oriented programming?

  • A Swift paradigm favouring protocols and composition over class inheritance.
  • Behaviour is shared through protocol extensions with default implementations.
  • It works with structs and enums, not just classes.
  • It reduces fragile deep inheritance hierarchies.

28. What are protocol extensions?

  • They add default method/property implementations to a protocol.
  • Conforming types inherit the behaviour for free, overriding when needed.
  • They let you share logic across unrelated types.
  • Beware static vs dynamic dispatch differences when overriding.

29. What is the delegate pattern?

  • One object delegates responsibility or callbacks to another via a protocol.
  • It decouples components, e.g. a table view asking its delegate for behaviour.
  • The delegate property is usually weak to avoid cycles.
  • It is a one-to-one communication pattern.

30. What is the difference between delegation and closures for callbacks?

  • Delegation suits multiple related callbacks grouped in a protocol.
  • Closures suit single, inline callbacks like a completion handler.
  • Closures keep related code together; delegates centralise it in one type.
  • Closures risk retain cycles unless self is captured weakly.

31. What are generics and why are they useful?

  • Generics let you write flexible code that works with any type.
  • They keep type safety while avoiding duplicated implementations.
  • Example: func swap<T>(_ a: inout T, _ b: inout T).
  • The standard library's collections are built on generics.

32. What are associated types in protocols?

  • An associatedtype is a placeholder type a conforming type specifies.
  • It makes protocols generic, e.g. Element in a container protocol.
  • It enables flexible, type-safe abstractions.
  • Protocols with associated types cannot be used as simple existential types pre-Swift-5.7.

33. What is a closure in Swift?

  • A closure is a self-contained block of functionality that can be passed around.
  • It can capture and store references to variables from its surrounding scope.
  • Closures are reference types.
  • Functions are a special, named form of closure.

34. What is trailing closure syntax?

  • When a closure is the last argument, it can be written outside the parentheses.
  • It improves readability for callbacks and DSL-style APIs.
  • Swift also supports multiple trailing closures with labels.
  • Example: UIView.animate(withDuration: 0.3) { ... }.

35. What is @autoclosure?

  • It automatically wraps an argument expression in a closure.
  • It delays evaluation until the closure is called.
  • Used by assert and ?? to avoid evaluating unused expressions.
  • Use sparingly because it hides that an argument is a closure.

Concurrency

36. What is Grand Central Dispatch (GCD)?

  • GCD is Apple's low-level API for managing concurrent work via dispatch queues.
  • It abstracts thread creation and pooling for you.
  • You submit work to serial or concurrent queues with async/sync.
  • UI work must always run on the main queue.

37. What is the difference between serial and concurrent queues?

  • A serial queue runs one task at a time in order.
  • A concurrent queue can run multiple tasks simultaneously.
  • Serial queues help protect shared state without locks.
  • The main queue is serial and dedicated to UI updates.

38. What is the difference between async and sync dispatch?

  • async submits work and returns immediately without waiting.
  • sync blocks the caller until the work finishes.
  • Calling sync on the current serial queue causes a deadlock.
  • Use async for background work and UI dispatch.

39. Why must UI updates run on the main thread?

  • UIKit and SwiftUI are not thread-safe and expect main-thread access.
  • Updating UI off the main thread causes glitches or crashes.
  • Dispatch back with DispatchQueue.main.async or @MainActor.
  • Network callbacks often run off-main, so always hop back for UI.

40. What is async/await in Swift?

  • It is structured concurrency that writes asynchronous code in a linear style.
  • await suspends without blocking the thread until the result is ready.
  • It replaces nested completion handlers and reduces callback pyramids.
  • Async functions are called from an async context or a Task.

41. What are actors in Swift concurrency?

  • An actor protects its mutable state by serialising access.
  • Only one task touches an actor's state at a time, preventing data races.
  • Access from outside is asynchronous via await.
  • @MainActor is a special actor that runs work on the main thread.

42. What is the difference between GCD and async/await?

  • GCD uses queues and closures; async/await uses suspension points and linear code.
  • async/await offers compile-time concurrency checking and cancellation support.
  • GCD is older and more manual; async/await is structured and safer.
  • Both can coexist, but new code should prefer async/await.

43. What is a Task in Swift concurrency?

  • A Task is a unit of asynchronous work that runs concurrently.
  • It bridges synchronous code into the async world.
  • Tasks support priorities and cancellation via Task.isCancelled.
  • Task.detached runs without inheriting the current context.

44. What is a race condition and how do you prevent it?

  • A race condition happens when threads access shared mutable state unsafely.
  • Results become unpredictable depending on timing.
  • Prevent with serial queues, locks, or actors.
  • Swift's actor model and Sendable checks catch many at compile time.

45. What is the difference between DispatchQueue.main.async and DispatchQueue.global().async?

  • main.async schedules work on the UI thread.
  • global().async runs work on a background concurrent queue.
  • Do heavy work on global, then hop to main for UI updates.
  • Global queues come in quality-of-service levels for prioritisation.

UIKit & View Controllers

46. What is the UIViewController lifecycle?

  • viewDidLoad runs once after the view loads into memory.
  • viewWillAppear/viewDidAppear run each time the view shows.
  • viewWillDisappear/viewDidDisappear run as it leaves the screen.
  • Use viewDidLoad for one-time setup and viewWillAppear for refresh-on-show logic.

47. What is the difference between viewDidLoad and viewWillAppear?

  • viewDidLoad fires once when the view is created.
  • viewWillAppear fires every time the view is about to be shown.
  • Put expensive one-time setup in viewDidLoad.
  • Put data refresh and UI state updates in viewWillAppear.

48. What is the difference between frame and bounds?

  • frame is the view's position and size in its superview's coordinate system.
  • bounds is the view's own coordinate system, usually starting at origin (0,0).
  • Changing bounds.origin scrolls the content inside the view.
  • Rotations and transforms make frame and bounds diverge.

49. How does Auto Layout work?

  • It positions views using constraints describing relationships, not fixed coordinates.
  • The layout engine solves constraints to compute frames at runtime.
  • It adapts to different screen sizes and orientations.
  • Conflicting or missing constraints cause ambiguous or broken layouts.

50. What is content hugging and compression resistance?

  • Content hugging resists a view growing larger than its content.
  • Compression resistance resists a view shrinking smaller than its content.
  • Both are priorities used to resolve layout conflicts.
  • They decide which view stretches or clips when space is tight.

51. How does UITableView reuse cells?

  • Cells are recycled via dequeueReusableCell(withIdentifier:).
  • Offscreen cells go into a reuse pool instead of being destroyed.
  • This keeps memory low and scrolling smooth for large lists.
  • Always reset cell state in prepareForReuse to avoid stale content.

52. What is the difference between UITableView and UICollectionView?

  • UITableView shows a single-column vertical list.
  • UICollectionView supports flexible grids and custom layouts.
  • Collection views use a layout object for full control of positioning.
  • Use table views for simple lists, collection views for richer layouts.

53. What is a diffable data source?

  • It manages table/collection data using snapshots of sections and items.
  • It computes and animates differences automatically.
  • It removes error-prone manual insert/delete index bookkeeping.
  • Items must be Hashable for diffing to work.

54. What is the responder chain?

  • It is the ordered list of objects that get a chance to handle an event.
  • Events travel from the first responder up through the chain.
  • It powers touch handling, menu actions, and keyboard events.
  • UIResponder is the base class participating in it.

55. What is the difference between pushViewController and present?

  • pushViewController adds a controller to a navigation stack with a back button.
  • present shows a controller modally over the current one.
  • Push needs a UINavigationController; present does not.
  • Use push for drill-down flows, present for self-contained tasks.

56. What is the difference between Storyboards, XIBs, and programmatic UI?

  • Storyboards visualise multiple screens and their transitions.
  • XIBs define a single reusable view or cell visually.
  • Programmatic UI builds views in code for full control and easier diffs.
  • Large teams often prefer code to avoid storyboard merge conflicts.

57. What is a segue?

  • A segue defines a transition between two storyboard scenes.
  • prepare(for:sender:) passes data to the destination.
  • Segues can be triggered automatically or manually with performSegue.
  • Unwind segues navigate back to an earlier controller.

58. How do you pass data between view controllers?

  • Forward: set properties on the destination or pass via prepare(for:).
  • Backward: use delegates, closures, or notifications.
  • Shared state: a view model, singleton, or dependency-injected service.
  • Prefer dependency injection over global singletons for testability.

Networking, Data & Persistence

59. How do you make a network request in iOS?

  • Use URLSession with a URLRequest.
  • Handle the response on a background thread, then update UI on main.
  • Modern code uses try await URLSession.shared.data(for:).
  • Always handle errors, status codes, and decoding failures.

60. What is Codable and how is it used?

  • Codable combines Encodable and Decodable for JSON conversion.
  • Conforming types serialise/deserialise automatically.
  • CodingKeys map JSON keys to differently named properties.
  • Use JSONDecoder/JSONEncoder to convert data.

61. How do you handle JSON keys that differ from property names?

  • Define a CodingKeys enum mapping properties to JSON keys.
  • Or set the decoder's keyDecodingStrategy to .convertFromSnakeCase.
  • For complex shapes, implement custom init(from:).
  • This keeps Swift naming clean while matching the API.

62. What is the difference between URLSession data, upload, and download tasks?

  • Data tasks return response data in memory.
  • Upload tasks send files or data to a server.
  • Download tasks save responses to a file, good for large payloads.
  • Download/upload tasks support background transfers.

63. What options exist for data persistence in iOS?

  • UserDefaults for small key-value settings.
  • Core Data or SwiftData for structured object graphs.
  • SQLite/GRDB for direct relational storage.
  • File system or Keychain for documents and secrets respectively.

64. What is Core Data?

  • Apple's object-graph and persistence framework.
  • It manages models, relationships, and change tracking.
  • It is not a database itself but typically backs onto SQLite.
  • It supports faulting and undo for efficient large datasets.

65. What is the difference between Core Data and SwiftData?

  • SwiftData is a modern Swift-first wrapper over the same persistence stack.
  • It uses @Model macros instead of the visual model editor.
  • It integrates cleanly with SwiftUI and async/await.
  • Core Data offers more mature, fine-grained control for complex needs.

66. When should you use UserDefaults?

  • For small pieces of user settings and preferences.
  • Examples: theme choice, onboarding-seen flag, last tab index.
  • Never store large data or sensitive secrets in it.
  • It persists across launches as a simple plist.

67. How do you store sensitive data securely?

  • Use the Keychain, which is encrypted and access-controlled.
  • Never store tokens or passwords in UserDefaults or plain files.
  • Use Secure Enclave / biometric protection for high-value secrets.
  • Enable data protection so files are encrypted at rest.

68. What is the App Transport Security (ATS) requirement?

  • ATS enforces secure HTTPS connections by default.
  • It blocks plain HTTP unless explicitly exempted in Info.plist.
  • It improves user privacy and data integrity.
  • Exceptions should be rare and well justified at review time.

App Lifecycle, Architecture & Performance

69. What are the iOS app lifecycle states?

  • Not running, inactive, active, background, and suspended.
  • The app moves between them as the user and system interact.
  • Use lifecycle callbacks to save state and release resources.
  • Suspended apps stay in memory but execute no code.

70. What is the role of AppDelegate and SceneDelegate?

  • AppDelegate handles app-level events like launch and notifications.
  • SceneDelegate manages UI scenes for multi-window support.
  • SwiftUI's App protocol can replace much of this boilerplate.
  • Use @UIApplicationDelegateAdaptor to keep a delegate in SwiftUI apps.

71. What is MVC in iOS?

  • Model holds data, View shows UI, Controller mediates between them.
  • It is Apple's default UIKit architecture.
  • Controllers often grow huge ("Massive View Controller").
  • Patterns like MVVM emerged to split responsibilities further.

72. What is MVVM and why use it?

  • Model-View-ViewModel adds a view model holding presentation logic and state.
  • The view binds to the view model, keeping views thin.
  • It improves testability since logic lives outside the view.
  • It pairs naturally with SwiftUI and Combine bindings.

73. What is dependency injection and why does it matter?

  • Passing dependencies in from outside rather than creating them internally.
  • It decouples components and makes them swappable.
  • It enables mocking for unit tests.
  • Forms include initialiser, property, and environment injection.

74. What is Combine?

  • Apple's reactive framework for processing values over time.
  • Publishers emit values; subscribers receive them.
  • Operators transform, filter, and combine streams declaratively.
  • It pairs with SwiftUI for binding asynchronous state.

75. How do you improve scrolling performance in a list?

  • Reuse cells and keep cell configuration lightweight.
  • Do heavy work (image decoding, formatting) off the main thread.
  • Cache computed values and downsized images.
  • Avoid blocking the main thread and minimise transparent overlapping layers.

76. How do you reduce an app's memory footprint?

  • Release unused resources and avoid retain cycles.
  • Downsample large images to display size.
  • Use lazy loading and pagination for big datasets.
  • Profile with Instruments to find the real hotspots.

77. What tools does Instruments provide?

  • Time Profiler for CPU usage and slow code.
  • Allocations and Leaks for memory issues.
  • Core Animation for rendering and frame-rate problems.
  • Network and Energy templates for connectivity and battery analysis.

78. What causes the main thread to be blocked and why is it bad?

  • Heavy computation, synchronous I/O, or large decodes on the main thread.
  • It freezes the UI and can trigger the watchdog to kill the app.
  • Move such work to background queues or async tasks.
  • Keep the main thread responsive at all times.

Testing, Tooling & Publishing

79. How do you write unit tests in iOS?

  • Use XCTest with classes subclassing XCTestCase.
  • Assert expectations with XCTAssert family functions.
  • Use expectations for asynchronous code.
  • Inject dependencies so units can be tested in isolation.

80. What is the difference between unit tests and UI tests?

  • Unit tests verify isolated logic quickly without UI.
  • UI tests drive the app through the interface to verify flows.
  • UI tests are slower and more brittle but catch integration issues.
  • A healthy suite favours many unit tests and fewer UI tests.

81. What is the difference between debug and release builds?

  • Debug includes symbols and assertions, with less optimisation.
  • Release is optimised and strips debug aids for performance.
  • Conditional compilation can change behaviour per configuration.
  • Always test performance and crashes on release builds.

82. What is a provisioning profile?

  • It links your app ID, certificates, and devices for signing.
  • It authorises an app to run on devices or be distributed.
  • Development profiles target test devices; distribution profiles target the store.
  • Mismatched profiles are a common cause of signing errors.

83. What is the difference between TestFlight and the App Store?

  • TestFlight distributes beta builds to internal and external testers.
  • The App Store distributes the final public release.
  • TestFlight builds expire and are for feedback, not production.
  • Both go through Apple processing but with different review depth.

84. What is the app review process like?

  • Apple reviews submissions against its App Review Guidelines.
  • Common rejections: crashes, privacy issues, broken links, guideline violations.
  • You provide metadata, screenshots, and notes for reviewers.
  • Rejections include reasons; you fix and resubmit.

85. How do you handle different screen sizes and devices?

  • Use Auto Layout and size classes for adaptive layouts.
  • Use SwiftUI's flexible stacks and geometry for responsiveness.
  • Provide assets at multiple scales and use SF Symbols where possible.
  • Test on the smallest and largest supported devices.

86. What is the difference between @available and runtime availability checks?

  • @available annotates APIs with the OS versions they support.
  • if #available(iOS 17, *) guards usage at runtime.
  • Together they let one binary support multiple OS versions safely.
  • Calling newer APIs without a check crashes on older systems.

87. How do you handle push notifications?

  • Request authorisation, register with APNs, and receive a device token.
  • Send the token to your server, which pushes through APNs.
  • Handle foreground, background, and tapped states in delegates.
  • Use notification service extensions to modify payloads before display.

88. What are the best practices for app security?

  • Store secrets in the Keychain and use HTTPS everywhere.
  • Validate server inputs and never trust client data alone.
  • Avoid hardcoding API keys; obfuscate or fetch them securely.
  • Keep dependencies updated and minimise the data you collect.