Interview Preparation

Follow role- and difficulty-specific interview questions that point back to shared canonical engineering answers.

~16 min reading~3 min practice estimate
Suggest content

Interview Paths: Senior, Lead, Staff, and Architect

Use these maps to practice by role and difficulty. Answers live in the concept library; each question below links to the canonical explanation rather than copying it. The role-specific notes describe what an interviewer expects you to emphasize.

Senior iOS Developer

Intermediate

How do you choose between a concrete dependency, a protocol existential, and a generic?

Answer focus: Preserve useful type relationships, introduce an abstraction at a real boundary, and avoid protocolizing every type. Compare tradeoffs using the protocol and generic guide.

Follow-up: When does an associated type make an existential awkward?

How do you give a SwiftUI child view editable state?

Answer focus: The owner stores the source of truth; a child receives a Binding when it needs read/write access. See SwiftUI state ownership.

Follow-up: How is @Bindable different when using Observation?

How do you prevent an older search response from replacing newer results?

Answer focus: Own and cancel prior work, check cancellation after suspension points, and enforce the latest-query invariant before publishing state. Review the stale-result example.

Explain value semantics, copy-on-write, and reference identity.

Answer focus: Value semantics describe independent logical values; reference identity means two references can point to the same instance. Copy-on-write can defer a value's physical buffer copy until mutation, so it preserves value semantics without eagerly copying all storage. See struct versus class.

When would you choose an enum with associated values instead of a protocol hierarchy?

Answer focus: Choose a finite, centrally owned set with exhaustive handling as an enum; choose an open set of implementations behind behavior as a protocol. Compare enum and protocol tradeoffs.

What is the difference between Sendable and Codable?

Answer focus: Sendable is about safe concurrency transfer; Codable is about serialization. A type can need both, but neither implies the other. See the concurrency contract.

How do primary associated types improve a protocol API?

Answer focus: They let an API name important associated types at a use site and apply same-type constraints, while the conformer still chooses the associated type. Review associated types and primary associated types.

Advanced

How do you apply dependency inversion without adding needless layers?

Answer focus: Put an abstraction at a meaningful change, ownership, or test boundary; inject it at the composition root; keep the contract small. The dependency inversion explanation is authoritative.

Follow-up: Where should the live implementation be constructed, and how would you test the consumer?

How do you decide what belongs in a unit, integration, or UI test?

Answer focus: Test behavior at the narrowest reliable boundary, reserve UI tests for user-visible flows, and inject nondeterministic inputs. See testing boundaries.

Follow-up: How do you make cancellation and time deterministic in tests?

How do you diagnose a production regression without guessing?

Answer focus: Establish impact and a baseline, correlate release/version and device cohorts, use privacy-safe telemetry, mitigate first, then verify a targeted fix. See reliability and incident response.

How do escaping closures affect ownership and actor isolation?

Answer focus: An escaping closure can outlive its call, so inspect captured references for retain cycles and make actor isolation explicit at the call boundary. [weak self] is appropriate when the callback should not keep its owner alive, but it is not a universal fix. Review ARC and actor isolation.

When is @unchecked Sendable legitimate?

Answer focus: Only when a real, documented synchronization invariant makes cross-task access safe and the compiler cannot prove it. It suppresses checking; it does not synchronize. See the unchecked conformance guidance.

How do you bridge a callback API to async/await safely?

Answer focus: Use a checked continuation for one completion, guarantee exactly one resume, cover errors and every terminal path, and propagate cancellation when supported. See continuation bridging.

What is priority inversion, and how do you respond to it?

Answer focus: A high-priority task can be blocked behind a lower-priority lock/resource holder while other work delays the holder. Keep critical sections short and measure before tuning priorities; see priority inversion.

What can change after an await inside an actor method?

Answer focus: The actor can run other work at a suspension point, so state may no longer satisfy assumptions made before await. Revalidate invariants or reserve a deliberate state transition; see actor reentrancy.

How do structured tasks, Task, and Task.detached differ?

Answer focus: Structured child tasks are scoped to a parent; Task is unstructured but inherits context; detached work deliberately drops actor, task-local, and priority inheritance. See task lifetime choices.

How do you choose between unit, integration, and UI tests?

Answer focus: Test at the narrowest stable boundary that proves the behavior; use UI tests for important user-visible journeys, not every branch. See testing boundaries.

How do you make time, randomness, UUIDs, and networking deterministic in tests?

Answer focus: Inject controllable capabilities such as a clock, ID generator, and network client; tests advance or resolve them explicitly instead of relying on sleeps or live services. See testing boundaries.

How do you test cancellation and actor-isolated state without timing sleeps?

Answer focus: Use a controllable async dependency or test clock, trigger cancellation explicitly, and await a known state/result instead of guessing with a delay. See testing boundaries and cancellation patterns.

How do feature flags support rollout and rollback?

Answer focus: They decouple release from exposure and can limit blast radius, but require default behavior, owner, expiry, both-branch tests, and a rollback plan that accounts for old clients. See CI/CD and release design.

How would you evolve a public Swift API without breaking clients?

Answer focus: Prefer additive changes, stage deprecation, avoid unsatisfied protocol requirements, and provide migration guidance for unavoidable breaks. See public API evolution.

How do you propagate cancellation through networking, parsing, and UI state?

Answer focus: Let the owner cancel the task, pass cancellation through child work and supported APIs, check at expensive boundaries, and avoid publishing stale state after cancellation. See cancellation patterns.

How do you prevent duplicate token refresh requests?

Answer focus: Use one refresh coordinator so concurrent failures await the same in-flight operation, update credentials atomically, retry eligible calls once, and clear the session if refresh fails. See authentication and token refresh.

How do you define dependency direction for UI, domain, and data?

Answer focus: High-level policy should not import transport or persistence details; depend on consumer-facing capabilities and compose implementations at the boundary. See dependency direction and the canonical DIP answer.

How do DTOs, domain models, and view state differ?

Answer focus: DTOs reflect transport shape, domain models express stable app meaning, and view state represents what a screen can render. Map at the data boundary and do not leak HTTP or JSON details into UI state. See domain model and DTO.

Answer focus: Parse and validate the external URL at the app boundary, convert it into a typed destination, then let the owning feature/coordinator construct the route and required dependencies. Keep raw URL strings out of view state; see state-driven navigation.

How do Auto Layout priorities help resolve a layout conflict?

Answer focus: Hugging resists growth and compression resistance resists shrinking; distinguish an ambiguous system from unsatisfiable required constraints, then inspect the actual constraint graph. See Auto Layout priorities.

How do you preserve a multi-step flow across app relaunch?

Answer focus: Persist the minimal validated route state needed to restore the user's intent, not transient view objects; reconstruct the flow through its owner and handle removed or expired destinations. See state-driven navigation.

How do you decide what to cache, and how do you invalidate it?

Answer focus: Cache at the layer that removes measured work, with explicit freshness, invalidation, memory limits, and privacy policy. Distinguish HTTP revalidation, decoded data, and rendered-image caches; see persistence choices and performance investigation.

How do ETags, retries, and idempotency interact?

Answer focus: Use validators to revalidate cached reads, retry only transient errors with bounded backoff, and retry writes only when idempotent or protected by an idempotency key. See HTTP caching and retry policy.

How do you choose an integration point for SwiftUI inside a UIKit app?

Answer focus: Keep navigation and state ownership explicit, migrate a bounded surface, and use hosting/representable interoperability only where it reduces risk. See the UIKit-to-SwiftUI migration plan.

What do memory graphs, Allocations, and Time Profiler each tell you?

Answer focus: Use memory graphs to inspect ownership paths, Allocations to see object creation and lifetime, and Time Profiler to find CPU hot spots; choose based on the symptom and compare to a baseline. See performance investigation.

How do you distinguish a stub, fake, spy, and mock?

Answer focus: A stub returns controlled responses, a fake has a lightweight working implementation, a spy records calls, and a mock verifies expectations. Prefer result/state assertions unless interaction order is itself contractual. See testing boundaries and doubles.

What release signals prove a rollout is healthy without exposing user data?

Answer focus: Track privacy-safe, appropriately aggregated crash, latency, launch, hitch, and key-journey signals by version/cohort; minimize payloads and exclude credentials or sensitive response data. See mobile scale and observability.

How do you review an architectural proposal constructively?

Answer focus: Restate the problem and constraints, test options against agreed criteria, surface failure modes and migration cost, then record a decision owner and revisit trigger. See architecture decision-making.

Why should side effects not run in SwiftUI body?

Answer focus: body is a repeatable description that SwiftUI may recompute; start lifecycle-bound work in .task or a model/service and make it cancellable. See lifecycle work and cancellation.

How do view identity and list identifiers affect SwiftUI state?

Answer focus: SwiftUI associates state with identity; unstable IDs can move/reset row state and break animations. Use stable domain identity and change .id only when resetting state is intended. See view identity and rendering.

How do you cancel image loading when a reusable view disappears?

Answer focus: Tie the request to the view/task lifetime, propagate cancellation into the loader, and prevent late results from updating a reused destination. See SwiftUI task lifetime and cancellation patterns.

What accessibility behavior should a custom control provide?

Answer focus: Expose a meaningful label, value, traits, and actions; support Dynamic Type, adequate hit targets, and non-color cues; then verify with VoiceOver and accessibility tests. See layout, accessibility, and platform fit.

What causes scrolling hitches, and what work should leave the main actor?

Answer focus: Measure frame-time, then inspect main-thread work, layout, image decode, and repeated computation. Move safe expensive work off the UI actor without losing cancellation or synchronization; see performance investigation.

How do you migrate a local store while preserving user data?

Answer focus: Use versioned migrations, test realistic prior stores and interrupted upgrades, retain a safe recovery path, and monitor failure rates. See persistent-store migration.

How do you resolve offline data conflicts?

Answer focus: Choose a domain-specific policy—merge independent fields, use version checks, or ask the user—and make retries idempotent. Do not assume last-write-wins is safe for every record. See offline-first design.

How do you avoid excess copies when decoding a very large response?

Answer focus: Measure peak memory, paginate or stream when the API supports it, decode once, and avoid retaining raw, decoded, and mapped copies together longer than needed. See memory and performance investigation.

How should state flow from a SwiftUI parent through two child views?

Answer focus: Keep one owner for mutable state, pass read-only values where enough, and pass a Binding only to the child that edits it. See SwiftUI state ownership.

Why can recreating an observed model in body lose state?

Answer focus: body is recomputed; object construction there can replace the reference and reset subscriptions or state. Give the model a stable owner (@State for Observation or @StateObject for legacy ObservableObject) and inject it into children. See state ownership and legacy object ownership.

How do you model loading, empty, content, and failure states?

Answer focus: Represent mutually exclusive states with an enum and let the view render each case; model stale content during refresh explicitly if the product needs it. See the feature blueprint.

How do Combine subscribe(on:) and receive(on:) differ?

Answer focus: subscribe(on:) affects upstream subscription/request work; receive(on:) schedules downstream values. Choose the UI delivery scheduler deliberately and avoid assuming either operator makes all work thread-safe. See Combine scheduling.

How do you identify and fix a Combine retain cycle?

Answer focus: Inspect the ownership path from the owner to its cancellable and back through a stored closure; capture weakly where the callback should not extend owner lifetime, and cancel at the right lifecycle boundary. See Combine memory leaks.

When do you prefer async/await over Combine?

Answer focus: Prefer async/await for one-shot request/response work; use Combine for long-lived or composed streams when its operators model the changing inputs clearly. See the Combine mental model.

When would you protect a cache with an actor versus GCD barriers?

Answer focus: Prefer an actor for isolated async mutable state and clear ownership; a concurrent queue with barrier writes can fit a synchronous legacy boundary, but callers must respect its synchronization contract. See actors and barrier writes.

How do Swift task priority and GCD QoS compare?

Answer focus: Both are scheduling hints, not correctness or real-time guarantees; use task priority with structured Swift concurrency and QoS for GCD work, and avoid priority escalation as a substitute for fixing dependency bottlenecks. See task lifetime choices.

What causes a main-thread GCD deadlock?

Answer focus: A thread synchronously waiting for work that must execute on that same blocked queue cannot make progress. Avoid sync self-dispatch and blocking waits on the main queue; see GCD deadlocks.

Where do access and refresh tokens belong?

Answer focus: Keep credentials out of preferences and logs; protect refresh secrets using Keychain-backed storage, keep authorization server-owned, and define expiry/revocation behavior. See authentication and token refresh.

Certificate pinning vs public-key pinning: what changes operationally?

Answer focus: Certificate pins bind to a certificate and rotate more often; public-key pins can survive certificate renewal when the key stays constant. Both require a tested backup and rollout plan. See pinning types.

How do you rotate pins safely?

Answer focus: Ship future and backup pins before server rotation, wait for sufficient client adoption, monitor trust failures, and keep an emergency release path. See the safe pin rotation plan.

How do you distinguish authentication errors from business errors?

Answer focus: Preserve typed transport, authorization, decoding, and domain failures until the feature boundary can map them to appropriate user actions; do not turn every error into a retry or login prompt. See network error categories.

Lead iOS Developer and Mobile Tech Lead

Advanced

How would you introduce dependency injection across a feature or module boundary?

Answer focus: Define the consumer-owned capability, keep implementations in the outer layer, compose dependencies once, and make lifetime and failure policy explicit. Use the shared dependency inversion concept and composition-root example.

Follow-up: What would make you keep a dependency concrete instead?

How do you decide when to modularize a mobile codebase?

Answer focus: Start from team ownership, build cost, change isolation, and dependency direction; pilot one bounded feature and measure the result. See incremental modularization and migration.

How do you choose between MVVM, MVVM-C, VIPER, or Clean Architecture?

Answer focus: Compare actual complexity, navigation ownership, test seams, team familiarity, and ongoing ceremony; choose the simplest design that contains likely change. See the architecture decision framework.

Lead-level people and delivery

How do you establish engineering standards and manage technical debt?

Answer focus: Tie debt to delivery or user risk, automate a small set of standards, and measure whether the work improves change cost. See technical debt and standards.

How do you handle a missed deadline or unrealistic commitment?

Answer focus: Raise the risk early, show scope/date options with consequences, and agree on the smallest safe delivery plan. See deadline and scope management.

How do you support an underperforming teammate?

Answer focus: Be specific and private, learn the blocker, agree on measurable support and follow-up, and coordinate with the manager on formal responsibilities. See supporting a teammate.

How do you plan work with uncertain mobile dependencies?

Answer focus: Slice toward demonstrable outcomes, name dependencies and owners, time-box discovery, and update the forecast as evidence changes. See agile planning under uncertainty.

How do you lead multiple mobile developers without becoming a bottleneck?

Answer focus: Set shared direction and ownership, delegate bounded outcomes, and make integration risks visible while growing decision-making capacity across the team. See leading multiple mobile developers.

How does a Mobile Tech Lead differ from an Engineering Manager?

Answer focus: Explain the local split: the tech lead commonly owns technical direction and engineering tradeoffs, while people/staffing and broader execution may sit with the manager. See role responsibilities and boundaries.

How would you design a scalable authentication system?

Answer focus: Keep authorization server-owned, protect refresh credentials, coordinate refresh, define revocation and recovery, and test expiry and duplicate requests. See authentication system design.

How do you manage multiple build environments safely?

Answer focus: Treat endpoints, bundle identity, entitlements, and feature defaults as one explicit configuration; validate the signed artifact and never embed a secret as if it were private. See environment configuration.

How do you prevent performance and reliability regressions?

Answer focus: Set user-journey budgets, combine automated checks with staged rollout, and define owners and mitigations for production signals. See regression prevention.

Staff / Principal

How would you design an offline-first mobile product?

Answer focus: Treat local persistence as the UI read model, make writes durable in an outbox, define conflict and retry semantics, and make sync observable. See the offline-first system design.

How would you scale a mobile app to millions of users?

Answer focus: Clarify whether scale means traffic, data, team, or reliability; address backend capacity and client behavior separately. See mobile scale and reliability.

How do you resolve a high-impact architecture disagreement?

Answer focus: Align on constraints, compare options against explicit criteria, time-box unknowns, record the decision, and define a revisit trigger. See architecture disagreement.

Mobile Software Architect

Advanced

How would you design a secure banking application?

Answer focus: Define threat model and regulatory constraints first; minimize sensitive data, isolate authentication, use server-authoritative operations, and design recoverable flows. See the banking app design.

How would you design a modular super app with multiple teams?

Answer focus: Separate product capabilities behind stable contracts, assign ownership, constrain dependency direction, and avoid a central shared module that becomes a dumping ground. See modularization.

How do you plan a gradual UIKit-to-SwiftUI migration?

Answer focus: Migrate bounded screens or flows, retain clear navigation and state ownership, use interoperability where it lowers risk, and compare quality and delivery metrics before expanding. See incremental UI framework migration.

How do you choose native, shared, or cross-platform implementation?

Answer focus: Compare user experience, platform integration, team skills, release coupling, shared logic, and long-term ownership; avoid treating code sharing as the goal by itself. See cross-platform tradeoffs.

Interview difficulty guide

LevelWhat a strong answer demonstrates
FoundationalCorrect definitions, syntax, ownership, and common failure modes.
IntermediateApplies the concept to an iOS feature and compares a reasonable alternative.
AdvancedMakes constraints, failure behavior, testing, performance, and tradeoffs explicit.
Lead / ArchitectAdds team ownership, migration strategy, risk, observability, delivery, and decision communication.

This is an interview route, not a second copy of the technical reference. Follow each linked answer for the full explanation, code, and related concepts.