Skip to content

iOS Developer interview questions

100 real questions with model answers and explanations for Senior candidates.

See a iOS Developer resume example

Practice with flashcards

Spaced repetition · Hunter Pass

Questions

uikitswiftuinavigation

I would keep the UIKit coordinator as the flow owner and embed the SwiftUI checkout behind one UIHostingController factory.

  • The factory accepts a typed checkout input and returns completion actions such as paid, cancelled, or authenticationRequired, without exposing SwiftUI state to UIKit.
  • One @Observable or TCA store owns the checkout state; the hosting controller only bridges navigation bar, dismissal, and environment dependencies.
  • I would test push, modal dismissal, deep link entry, and memory release across 20 repeated presentations before adding a second SwiftUI flow.

Why interviewers ask this: The interviewer is checking whether SwiftUI adoption preserves one navigation and state owner at the framework boundary.

uikitswiftuiswift

I would use UIViewControllerRepresentable with a Coordinator that owns the delegate bridge and keeps capture lifecycle out of updateUIViewController.

  • makeUIViewController creates the controller once, while updateUIViewController applies only changed configuration such as zoom or selected camera.
  • The Coordinator converts delegate callbacks into typed actions and does not retain the parent wrapper or controller in a cycle.
  • dismantleUIViewController stops the session, clears the delegate, and is verified by presenting and dismissing the screen 50 times with a stable object count.

Why interviewers ask this: A strong answer respects representable identity, delegate ownership, and teardown instead of recreating an expensive UIKit controller on every update.

resiliencearchitecture-patterns

I would choose TCA for this checkout because explicit state transitions and effect cancellation outweigh its ceremony at this complexity.

  • Address, quote, payment, and submission remain one feature state with scoped child reducers instead of four view models mutating shared services.
  • Each request receives a cancellation ID, and changing the address cancels the stale quote before a new effect starts.
  • I would prove the choice with reducer tests for 9 actions, stale responses, and double-submit, while a simple receipt screen stays plain SwiftUI or MVVM.

Why interviewers ask this: The interviewer is evaluating a pattern choice against concrete state and side-effect complexity rather than preference.

queries

I would debounce the query and make the search effect cancellable by one stable cancellation ID.

  • A queryChanged action updates local state immediately, then starts a 300 ms clock-driven debounce before requesting results.
  • cancelInFlight ensures a new query cancels the previous effect, and the response action carries the query or request ID for a final stale-result check.
  • A TestStore with a TestClock advances 299 and 1 milliseconds and proves that typing five characters produces one request and ignores the cancelled response.

Why interviewers ask this: A strong answer makes timing, cancellation, and stale-response protection deterministic in both implementation and tests.

swiftuinavigationonboarding

I would store typed route state outside the views and derive NavigationStack paths from that single source of truth.

  • The route enum carries only stable identifiers, while validation maps the deep link to steps 1 through 3 and rejects missing prerequisites.
  • Scene storage persists a versioned codable route snapshot, but sensitive form values stay in the feature store or Keychain as appropriate.
  • Tests cover cold launch, malformed links, back navigation, and a schema version change so restoration never builds an impossible path.

Why interviewers ask this: The interviewer is checking state-driven navigation, validated deep links, and versioned restoration rather than imperative view pushes.

I would break cycles at owned contracts and optimize the measured high-churn dependency paths, not merge all 42 targets blindly.

  • Xcode build timing and the package graph identify shared targets that invalidate 30 or more dependents after small edits.
  • Cross-feature route and result protocols move into narrow interface targets, while the app composition target supplies implementations and orchestration.
  • CI rejects cycles and unauthorized imports, and I keep the change only if representative incremental builds fall below 90 seconds without excessive package overhead.

Why interviewers ask this: A strong answer treats modularization as dependency-graph design with measurable build outcomes.

api

I would publish consumer-shaped capabilities instead of making the concrete 18-method service a cross-package API.

  • Checkout receives CurrentAccountProviding and AddressUpdating, while profile receives only the broader mutations it actually owns.
  • Domain values crossing the boundary are Sendable structs, and transport DTOs, URLSession, and persistence models remain internal.
  • Contract tests run against the live implementation and fakes, while the composition root adapts one AccountService to each narrow protocol.

Why interviewers ask this: The interviewer is evaluating interface segregation, package visibility, and concurrency-safe boundary models.

I would split stable design primitives from volatile product components and remove umbrella imports from feature code.

  • Color, typography, spacing, and five mature controls form a low-churn DesignCore target with a deliberately small public API.
  • Product-specific banners and experiments stay with their owning features instead of entering SharedUI after a second use.
  • I would compare 20 incremental builds before and after, requiring lower invalidation time without increasing launch time or duplicate resources.

Why interviewers ask this: A strong answer uses change frequency and ownership to reduce build invalidation instead of creating another generic shared package.

primary-keystesting

I would define one typed feature dependency value and supply it at the package entry point from the app composition root.

  • Required services have no silent production singleton fallback, so a missing dependency fails during construction rather than during checkout.
  • Clock and UUID dependencies use controllable implementations, while networking and analytics expose only the operations the feature calls.
  • A test builds the feature with four fakes in under 10 milliseconds, and package code contains zero global container lookups.

Why interviewers ask this: The interviewer is checking explicit dependency ownership and deterministic seams without a hidden service locator.

fixtures

I would declare the resources on the owning target and resolve them through Bundle.module inside that package.

  • Process localized catalogs and assets, but copy the JSON only when its directory structure or raw bytes must remain unchanged.
  • The public feature API returns semantic image and string values rather than exposing bundle paths to consumers.
  • Package tests load one localized string in two locales, decode the fixture, and verify the release archive contains one copy of each resource.

Why interviewers ask this: A strong answer knows SPM resource ownership and avoids coupling consumers to package bundle layout.

uikitswiftuiswift

I would expose a feature factory and typed inputs and outputs, with separate hosting adapters over one state and domain implementation.

  • The SwiftUI adapter returns the feature view, while the UIKit adapter wraps the same view in UIHostingController and owns containment details.
  • Availability-specific behavior stays in adapters, so the domain target does not import UIKit or branch on iOS versions.
  • Integration tests run the same three user journeys through both hosts and assert identical actions, navigation results, and deallocation.

Why interviewers ask this: The interviewer is checking whether reusable feature logic stays independent of its UIKit and SwiftUI hosts.

swiftuiuikitswift

I would profile cell updates and identity first because hosting SwiftUI is not by itself proof of the bottleneck.

  • Time Profiler and the Core Animation instrument separate body work, Auto Layout, image decoding, and diffable snapshot application during the same scroll trace.
  • Stable item IDs and presentation-ready row models prevent full subtree replacement, while image work stays outside cell configuration.
  • I would keep the boundary if p95 hitch duration stays below one 16.67 ms frame on target hardware after 10 identical scroll runs.

Why interviewers ask this: A strong answer measures the UIKit and SwiftUI seam and corrects identity or work placement before rewriting it.

closuresownership

I would remove the strong cycle at the closure edge and make the completion lifetime explicit.

  • The controller owns the closure, so a strong self capture creates controller to closure to controller; the memory graph should show that exact path.
  • If completion is optional after dismissal, capture self weakly and return when it is gone; if completion must finish, move the work to a longer-lived owner.
  • Clear one-shot callbacks after invocation and add a test that presents and dismisses the controller 30 times with zero surviving instances.

Why interviewers ask this: The interviewer is checking ARC graph reasoning and whether weak capture matches the required lifetime semantics.

architecture-patternsmemory

I would use weak unless the design can prove the parent outlives every access to the child reference.

  • weak becomes nil safely when the parent is released and fits asynchronous completion or teardown that may arrive late.
  • unowned avoids optional handling but traps on access after deallocation, so it is suitable only for a strict shared-lifetime invariant.
  • A lifecycle test releases the parent before a delayed child callback; if that sequence is legal, unowned is invalid by construction.

Why interviewers ask this: A strong answer chooses weak or unowned from a proven lifetime invariant rather than style.

asyncconcurrency

The task strongly retains the view model for the duration of load, so I would give the task an owner and cancel it when the feature ends.

  • Store the Task handle, cancel it on replacement or teardown, and ensure load propagates cancellation into URLSession instead of swallowing CancellationError.
  • A weak capture alone is insufficient if guard let self promotes a strong reference before a five-second await; narrow the strong lifetime or move durable work to a service.
  • A test dismisses after 100 milliseconds, verifies cancellation reaches the request, and expects the view model to deallocate within one run-loop turn.

Why interviewers ask this: The interviewer is testing closure capture across suspension and explicit ownership of unstructured tasks.

delegationprotocolscombine

I would inspect each registration by who retains whom and where its useful lifetime ends.

  • A block timer retains its closure, a stored AnyCancellable retains the subscription chain, and either can close a cycle through self.
  • Delegate properties are weak when the delegate does not own the source, while selector-based NotificationCenter observers still need correct semantic teardown even if automatic removal prevents a crash.
  • Teardown invalidates the timer, cancels subscriptions, clears callbacks, and a 50-cycle memory test confirms the screen count returns to baseline.

Why interviewers ask this: A strong answer reasons about four different lifetime mechanisms instead of applying weak self mechanically.

swift

I would bound temporary Objective-C objects with nested autorelease pools and process the import in measured batches.

  • Instruments Allocations should show whether bridged NSString, NSDictionary, or formatter objects accumulate until the outer run loop drains.
  • Process 500 or 1,000 records inside autoreleasepool blocks, persist the result, then release the batch before reading the next one.
  • I would tune batch size until peak memory stays below 300 MB without making the 80,000-record import more than 10% slower.

Why interviewers ask this: The interviewer is checking ARC interoperability with autoreleased Objective-C objects and quantitative batching.

swift

No, value semantics guarantees independent observable values, not constant-time copying for every custom type.

  • Data and standard collections often use copy-on-write, so unchanged copies can share storage until a mutation requires uniqueness.
  • A custom struct containing a large reference-backed buffer needs an explicit copy-on-write box and isKnownUniquelyReferenced before mutation.
  • I would measure allocations for 1,000 passes and keep the value design only if mutation copies occur at the intended ownership boundary.

Why interviewers ask this: A strong answer separates value semantics from implementation cost and knows when custom copy-on-write is justified.

I would keep UI state isolated to MainActor but move decoding into a non-main, Sendable service boundary.

  • URLSession awaits without blocking the actor, but synchronous JSONDecoder work executed after resumption can still occupy MainActor.
  • A decoder service returns a Sendable domain snapshot, then the view model performs only the final state assignment on MainActor.
  • Main Thread Checker and signposts must show no main-thread block above 16 ms and the same decoded result across 100 fixture runs.

Why interviewers ask this: The interviewer is checking that actor isolation is not confused with where CPU-heavy synchronous work should execute.

asyncconcurrencyauth

I would treat the await as a reentrancy point and reserve or version the stock before leaving actor isolation.

  • One option decrements to a reserved state atomically, then restores the unit if authorization fails or is cancelled.
  • Another captures a version and revalidates it after await, but then only one buyer may commit and the other needs a defined payment reversal.
  • A deterministic test suspends both authorizations at the same point and proves exactly one order reaches committed state.

Why interviewers ask this: A strong answer understands actor reentrancy and preserves a business invariant across suspension.

Locked questions

  • 21

    A gallery must download and downsample 200 images, but launching all child tasks at once causes a memory spike. How do you use structured concurrency?

    concurrencyswiftmemory
  • 22

    A SwiftUI search screen starts a new async request for every query and shows stale results after rapid typing. How do you tie work to state?

    asyncswiftconcurrency
  • 23

    A delegate API can call success, failure, or never call back after cancellation. How do you bridge it with continuations?

    delegationapiresilience
  • 24

    Swift 6 rejects passing a legacy mutable ImageCache class into two tasks because it is not Sendable. What is the correct fix?

    swift
  • 25

    A legacy cache uses a concurrent DispatchQueue with barrier writes and must be called from new async code. Would you replace it with an actor immediately?

    asyncconcurrencycaching
  • 26

    A background queue calls DispatchQueue.main.sync while the main thread waits on that background work. Why does the app hang?

    concurrencydata-structures
  • 27

    A user-initiated task waits behind 5,000 utility-priority operations on one actor, delaying a tap response by two seconds. How do you fix the priority problem?

    concurrency
  • 28

    A Bluetooth callback emits 100 samples per second, but the UI consumes only 10. How do you expose it as AsyncStream without unbounded memory?

    asynccallbacksconcurrency
  • 29

    A profile screen needs account, permissions, and avatar concurrently, while a gallery loads a variable number of albums. Which concurrency primitives fit?

    concurrency
  • 30

    A ProMotion feed targets 120 Hz but p95 frame time is 14 ms and users see hitches. What budget and tools do you use?

  • 31

    Changing one unread-count property causes a SwiftUI dashboard with 12 cards to recompute every card body. How do you narrow invalidation?

    swiftuiswift
  • 32

    A feed downloads 12-megapixel JPEGs for 120 by 120 point thumbnails and reaches 900 MB memory. How do you redesign image handling?

    memory
  • 33

    Applying a diffable snapshot for 10,000 items blocks the main thread for 180 ms even though only 20 rows changed. What do you change?

    snapshotconcurrency
  • 34

    Cold launch on an iPhone three generations old grows from 1.2 to 2.9 seconds after adding analytics and feature SDKs. How do you recover the budget?

  • 35

    A reusable UIKit card creates 70 Auto Layout constraints and layoutSubviews takes 9 ms for each visible cell. What do you inspect?

    uikitlayoutautolayout
  • 36

    A screen feels slow, but Time Profiler shows several small costs and no obvious 100 ms function. How do you find the critical path?

    schedulingcommunication
  • 37

    An @Observable app model contains 40 properties and is injected into every screen through Environment. What state boundary would you create?

  • 38

    A parent creates an @Observable editor model, and three child SwiftUI views must edit its fields without creating copies. Which wrappers do you use?

    swiftuiswift
  • 39

    A Combine type-ahead search issues five requests for five keystrokes and sometimes displays the first response last. Which operators fit?

    combine
  • 40

    A Combine pipeline receives 500 sensor values per second, performs a 4 ms transform per value, and falls behind. How do you handle pressure?

    soft-skillsci-cdcombine
  • 41

    A background Core Data import passes NSManagedObject instances to MainActor and intermittently crashes. What crosses the boundary instead?

    core-data
  • 42

    Importing 100,000 Core Data records takes 90 seconds and grows memory to 1.1 GB. How do you reduce both?

    core-datamemory
  • 43

    A new offline notes feature targets iOS 17+, stores 50,000 records, and may need CloudKit later. Would you choose SwiftData or Core Data?

    core-dataswift
  • 44

    A news app fetches the same 2 MB feed every launch even when the server returns cacheable ETag headers. How do you configure URLSession caching?

    cachingconfignetworking
  • 45

    A payment request times out after the server may have charged the card. Should the URLSession client retry it automatically?

    resiliencenetworking
  • 46

    A 600 MB video upload must continue after the app is suspended or terminated. Which URLSession design fits?

    designnetworking
  • 47

    Twenty requests receive 401 simultaneously when an access token expires, causing 20 refresh calls and repeated failures. How do you serialize refresh?

    tokensserialization
  • 48

    An async view-model test uses Task.sleep and fails intermittently on CI. How do you make time and completion deterministic?

    asyncconcurrency
  • 49

    A reducer starts a request on appear, cancels it on dismiss, and must ignore a late response. What tests prove the behavior?

    testing
  • 50

    A Core Data repository and URLSession client must be tested without a real server or a persistent user store. What test boundaries do you build?

    core-datanetworking
  • 51

    An image feed starts at 180 MB and reaches 720 MB after 12 minutes of scrolling, then receives a memory warning. How do you find and stop the growth?

    memory
  • 52

    Every presentation of a checkout screen leaves another 18 MB alive, and memory grows by 180 MB after ten dismissals. How do you locate the retain cycle?

    memory
  • 53

    After users watch 20 videos, 20 AVPlayer instances and 40 player items remain alive even though every screen was dismissed. What do you inspect?

  • 54

    A background import of 120,000 records peaks at 1.6 GB and is killed on 3 GB devices, but Instruments shows few long-lived domain objects. How do you reduce memory?

    memoryprofiling
  • 55

    A SwiftUI detail flow adds 25 MB each time users push and pop it, while the view structs disappear but the feature store does not. How do you debug it?

    swiftmlopsswiftui
  • 56

    Opening and closing a support chat five times leaves five WKWebView instances and adds 90 MB per visit. What is the likely ownership trap?

    ownership
  • 57

    A C image library adds about 6 MB per export, but Swift Memory Graph shows no retaining Swift objects. How do you investigate?

    memoryswift
  • 58

    Crash-free users fall from 99.92% to 98.7%, and the top crash is EXC_BAD_ACCESS in objc_retain with hexadecimal frames. What is your first analysis sequence?

  • 59

    A force unwrap in receipt parsing causes 34% of all crashes, but only for one locale and invoices older than 2022. How do you fix and verify it?

  • 60

    An Array index out of range crash occurs 70 times per million sessions while a diffable list receives background updates. How do you reproduce it?

    indexessessions
  • 61

    A release crashes only on iOS 16 with unrecognized selector after adding a framework built with a newer SDK. How do you isolate the compatibility fault?

  • 62

    Crash reports show a stack overflow after a settings change, with 1,400 repeated observer frames. How do you break the recursion?

    recursionmemory
  • 63

    The app disappears under image editing with no normal crash stack, and MetricKit reports peak memory near 2.1 GB. How do you confirm an out-of-memory termination?

    memorymonitoring
  • 64

    MetricKit reports watchdog terminations during launch, and the main-thread sample shows a synchronous 18-second database migration. What do you change?

    databasemigrationsmonitoring
  • 65

    Tapping Pay freezes the UI for 1.8 seconds, but the request itself takes only 120 ms. How do you find the main-thread blocker?

    communicationconcurrency
  • 66

    A 120 Hz feed shows p95 frame time of 22 ms after a new image effect ships. How do you identify the jank source?

  • 67

    One typing event makes a SwiftUI form with 60 fields recompute for 45 ms and drops visible keystrokes. How do you narrow the update?

    formsswiftuiswift
  • 68

    Rotating a complex UIKit dashboard blocks the main thread for 320 ms and logs unsatisfiable constraints. What do you inspect first?

    uikitautolayoutconcurrency
  • 69

    Opening History blocks scrolling for 260 ms while Core Data fetches 40,000 rows on the view context. How do you restore responsiveness?

    responsivecore-data
  • 70

    A card expansion animation runs at 30 FPS on older devices after adding shadows and masks. How do you decide what to simplify?

  • 71

    Touches stop responding for 900 ms whenever an analytics event is sent, even though dispatch to its SDK appears asynchronous. How do you prove the hidden wait?

    asyncconcurrency
  • 72

    After migrating callbacks to async/await, a shared Dictionary crashes with EXC_BAD_ACCESS about once per 200,000 requests under load. How do you fix the race?

    asynccallbacksconcurrency
  • 73

    An Auth actor checks that a token is expired, awaits refresh, and three callers each overwrite state with different refresh results. What went wrong?

    asyncconcurrencytokens
  • 74

    A continuation bridge crashes in debug with 'tried to resume more than once' when cancellation races a delegate callback. How do you repair it?

    callbacksprotocolsresilience
  • 75

    A Core Data object created in a background task is read on MainActor and crashes only during heavy sync. How do you remove the concurrency violation?

    core-dataconcurrency
  • 76

    A Task.detached updates an ObservableObject's @Published state and causes rare 'Publishing changes from background threads' failures. What change do you make?

    concurrency
  • 77

    An AsyncStream wrapper continues delivering location updates after its consumer is cancelled, and each reopened screen doubles callbacks. How do you stop it?

    asynccallbacksconcurrency
  • 78

    During a staged GCD-to-actor migration, one path uses queue.sync and another awaits the actor, producing a rare deadlock. How do you unwind the hybrid design?

    asyncdata-structuresconcurrency
  • 79

    Cold launch regresses from 1.4 to 3.2 seconds after three SDKs are added. How do you recover time to first frame?

  • 80

    The first frame appears in 1.2 seconds, but the home screen cannot be used until a 4.5-second configuration request completes. How do you improve TTI?

    config
  • 81

    After a release, daily battery use rises by 35% and background traffic triples for users with notifications enabled. How do you isolate it?

  • 82

    API p95 grows from 400 ms to 1.8 seconds after retry logic ships, while server latency is unchanged. What client behavior do you inspect?

    resilienceapilatency
  • 83

    The app archive grows by 86 MB and clean launch slows by 280 ms after adding two binary frameworks. How do you decide whether to keep them?

  • 84

    An on-device image classifier holds CPU near 85%, heats the phone, and slows from 40 to 12 frames per second after three minutes. How do you respond?

    zero-to-one
  • 85

    Core Data sync creates 6,000 duplicate contacts after two devices reconnect, even though each contact has a server ID. How do you repair the model and data?

    core-data
  • 86

    Two devices edit the same SwiftData note offline, and reconnecting silently loses one user's title change. How do you define conflict handling?

    swift
  • 87

    A Core Data migration fails for 0.8% of users with stores created four app versions ago. What do you do before shipping another attempt?

    migrationscore-data
  • 88

    An offline cart shows 5 items, but the server accepts only 3 after reconnect, and the badge continues to display 5. How do you reconcile state?

  • 89

    A Combine settings screen sometimes shows an older profile after two save requests finish out of order. How do you prevent the rollback?

    combinerollback
  • 90

    A background Core Data save succeeds, but the visible list remains stale until app restart for 7% of sync sessions. What do you inspect?

    core-datasessions
  • 91

    After users install iOS 18, a UIKit navigation flow dismisses its sheet twice and lands on the wrong screen in 4% of sessions. How do you isolate the OS regression?

    uikitnavigationsessions
  • 92

    A multi-window iPad app presents alerts on the wrong scene because legacy code uses UIApplication.shared windows. How do you replace the deprecated assumption?

    alerting
  • 93

    An App Store submission is blocked because a newly updated analytics SDK lacks required privacy-manifest declarations. What is your technical response?

  • 94

    After an iOS update, users with limited Photos access see an empty picker even though they granted access to 12 images. How do you debug it?

  • 95

    Moving to the Swift 6 toolchain produces 1,300 concurrency errors, and a quick patch adds @unchecked Sendable to 90 classes. What do you do?

    concurrencyswift
  • 96

    A UICollectionView self-sizing layout changes after an iOS update, making 8% of message cells jump from 80 to 300 points. How do you contain it?

    uikit
  • 97

    A junior engineer fixes a checkout leak by adding weak self to 14 closures, but payment completion now disappears in 3% of slow sessions. How do you mentor them through the correction?

    closuresmentoringsessions
  • 98

    A mid-level engineer responds to a data race by marking a mutable cache @unchecked Sendable, and Thread Sanitizer still reports failures. How do you handle the review?

    soft-skillscachingvalidation
  • 99

    An engineer tries to fix 120 Hz scrolling by caching every transformed image, reducing hitches but increasing memory from 240 to 850 MB. How do you coach the next iteration?

    cachingmemoryiteration
  • 100

    An engineer ships a force-unwrap crash fix by replacing nil with an empty string, but checkout now submits invalid addresses. How do you turn the review into better judgment?