Skip to content
tezvyn:

Top 30 Advanced Mobile Dev Interview Questions and Answers

30 advanced multiple-choice Mobile Dev interview questions, the deep end: internals, failure modes, and the design calls a senior engineer is expected to defend. They come from 30 bites in the Mobile Dev library, the hardest slice of the 652 Mobile Dev interview questions in the library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.

Mobile app development across 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 primary effect of enabling inline requires in a React Native app?

    Show the answer

    Answer: a · It defers a module's evaluation until the first time it is actually used

    Inline requires transform top-level imports so a module is evaluated lazily on first use, cutting startup work. It does not compile to machine code (that is closer to Hermes bytecode) nor perform tree shaking, which is a separate bundler concern.

    Read the full bite: Improving React Native startup time

  2. Question 2 of 30

    When a native module method reads a live device value to return to JavaScript, why should it use a Promise or callback rather than a direct return?

    Show the answer

    Answer: d · Because crossing the native-to-JS boundary is asynchronous, so results are delivered via promise or callback

    Values crossing from native into JavaScript are delivered asynchronously, so a Promise or callback is the correct pattern. Native code can return constants synchronously via getConstants, and package registration is unrelated to whether methods are async.

    Read the full bite: Building a native module from scratch

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

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

  5. Question 5 of 30

    When a variable is declared as dynamic, why does calling toIntOrDefault on it fail to compile?

    Show the answer

    Answer: b · Because extension methods are resolved statically and dynamic bypasses static type information

    The card explains that extension methods are static sugar resolved at compile time based on the declared type, so dynamic variables bypass static extension resolution. Option A is tempting because it uses precise-sounding terminology, but it incorrectly claims runtime resolution, which contradicts the fundamental static nature of Dart extensions.

    Read the full bite: What are Dart extension methods? Implement toIntOrDefault on String.

  6. Question 6 of 30

    To model a state machine where each state might carry unique, type-specific data (e.g., a success state holding a user object, an error state holding a message), which Kotlin construct is most appropriate?

    Show the answer

    Answer: b · A sealed class

    Sealed classes are specifically designed for representing restricted hierarchies where subclasses can be distinct types (like data classes or objects) and hold different associated data. Enum classes cannot hold different types of data for each constant, making them unsuitable for states with unique payloads.

    Read the full bite: When to use a sealed class instead of an enum?

  7. Question 7 of 30

    To model a network request's state—`Loading`, `Success` (with data), or `Error` (with a message)—which Kotlin construct is most suitable?

    Show the answer

    Answer: d · A sealed class, because each of its subclasses can carry different data types specific to that state.

    A sealed class is ideal because its subclasses can represent states with different associated data (e.g., data for Success, a message for Error) in a type-safe way. An enum is a set of constants of the same type and cannot elegantly model states with different data requirements.

    Read the full bite: When would you use a sealed class instead of an enum?

  8. Question 8 of 30

    When modeling a closed set of UI states where Loading needs no data, Success carries a User, and Error carries a String, why is a sealed class preferable to an enum?

    Show the answer

    Answer: b · Enum constants share a single constructor signature, making it awkward to attach different data to each state, whereas sealed subclasses can define their own parameters

    The correct answer captures the core structural distinction: enums force all constants into one uniform signature, while sealed subclasses can each carry heterogeneous state. Option C is tempting because data classes appear to model payloads well, but without the sealed modifier the hierarchy is open, so when expressions lose compile-time exhaustiveness and external code could add unhandled states.

    Read the full bite: When would you use a sealed class instead of an enum?

  9. Question 9 of 30

    You must convert a nullable `user` into a `Session`, logging the user's ID first. Which implementation is the most idiomatic and correct for the function body?

    Show the answer

    Answer: b · return user?.let { log(it.id); Session(it) }

    `let` is ideal as it executes a block on a non-null object and returns the lambda's result, which is the new `Session` object. The `apply` function is a tempting but incorrect alternative because it would return the original `user` object, not the `Session`.

    Read the full bite: Compare and contrast Kotlin's `apply` and `let` scope functions

  10. Question 10 of 30

    Given val result = name?.apply { trim().length } where name is String?, what is the inferred type of result and why?

    Show the answer

    Answer: c · String? because apply always returns the original receiver object, ignoring the lambda's final value

    apply returns the context object itself, so the expression yields String?, not the lambda's Int result. Option B confuses apply with let, which returns the lambda's final value.

    Read the full bite: Compare Kotlin apply and let scope functions

  11. Question 11 of 30

    Which Kotlin scope function is most idiomatic for configuring an object's properties and then returning the configured object itself?

    Show the answer

    Answer: a · apply

    apply is specifically designed for object configuration; it executes a block of code on the receiver (available as 'this') and returns the receiver object itself. In contrast, let returns the result of its lambda, making it unsuitable for directly returning the configured object.

    Read the full bite: Compare and contrast `apply` and `let` scope functions

  12. Question 12 of 30

    What is the primary advantage of using a sealed class instead of an enum for exhaustiveness-checked state machines in Dart 3?

    Show the answer

    Answer: c · Sealed classes let each subtype carry distinct constructor parameters and fields while keeping exhaustive switch safety.

    Sealed classes enable algebraic data types where each variant holds distinct payload data while the compiler verifies exhaustive handling, unlike enums which share a single shape. Distractor A is wrong because sealed subclasses must remain in the same library for the compiler to prove exhaustiveness; adding them elsewhere breaks that guarantee.

    Read the full bite: Explain Dart switch exhaustiveness for enums versus sealed classes

  13. Question 13 of 30

    What is the fundamental reason `reified` type parameters can only be used with `inline` functions in Kotlin?

    Show the answer

    Answer: c · inline allows the compiler to substitute the concrete type argument for the generic parameter directly into the bytecode at the call site, bypassing JVM type erasure.

    Option C correctly identifies that inline enables the compiler to replace the generic type with its concrete type at the call site, thus preserving it from JVM type erasure for runtime access. Option D is a common misconception; while inline offers performance, its necessity for reified is about enabling type information availability, not just optimizing checks.

    Read the full bite: What problem does `inline` solve, and how does `reified` relate?

  14. Question 14 of 30

    Why must a Kotlin function be declared `inline` to use a `reified` type parameter?

    Show the answer

    Answer: d · Because inlining moves the function's bytecode to the call site, where the compiler can access the concrete type argument.

    The `reified` keyword needs to know the actual type at runtime, which is normally erased by the JVM. The `inline` keyword enables this by copying the function's code to the call site, where the compiler knows the concrete type (e.g., `String`) and can substitute it directly into the bytecode. Option C is incorrect because `inline` doesn't prevent type erasure in general; it provides a clever workaround for a specific call.

    Read the full bite: Explain `inline` and `reified` in Kotlin

  15. Question 15 of 30

    In a Dart isolate, what is the consequence of a microtask that keeps rescheduling new microtasks recursively?

    Show the answer

    Answer: a · The event queue is starved until the recursive microtask chain ends

    The event loop always drains the entire microtask queue before processing any event queue tasks, so a self-rescheduling microtask chain blocks the event queue indefinitely. Option C is wrong because Dart does not interleave the two queues one-for-one.

    Read the full bite: Explain Dart's event loop, microtask queue, event queue, and await behavior

  16. Question 16 of 30

    Why does a reified type parameter require the enclosing function to be marked inline?

    Show the answer

    Answer: c · Because inlining copies the body to the call site so the compiler can substitute the concrete type into bytecode despite JVM erasure.

    Inlining copies the function body to each call site, letting the compiler replace the type parameter with the exact type argument in the generated bytecode and bypass JVM erasure. Distractor D is wrong because reified does not merely inject a Class reference; it performs compile-time type substitution to enable runtime type checks.

    Read the full bite: What does inline solve for higher-order functions, and what is reified?

  17. Question 17 of 30

    Why does a nested Text inherit color and fontSize from its parent while a nested View inherits nothing?

    Show the answer

    Answer: c · Text maps to platform attributed-text where spans inherit attributes, while Views are independent boxes with no general style cascade

    Text inheritance reflects native attributed-text systems where text runs cascade attributes; Views are deliberately independent layout boxes with no cascade. React Native does not implement a general CSS cascade, so the first and last options are wrong.

    Read the full bite: Why Text inherits styles but View does not

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

  19. Question 19 of 30

    Under the New Architecture, what is the most accurate primary reason StyleSheet.create outperforms inline literal styles in long lists?

    Show the answer

    Answer: c · It provides stable object references, avoiding per-render allocations and reducing diff and GC pressure

    The durable benefit is referential stability that cuts allocations and garbage during frequent list re-renders. The bridge-ID serialization story is much diminished under Fabric and JSI, so attributing the gain primarily to it today overstates the mechanism.

    Read the full bite: How StyleSheet.create reduces styling overhead

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

  21. Question 21 of 30

    In Dart, given class C extends S with M1, M2 where all define foo(), if C overrides foo() and calls super.foo(), which implementation does the call reach?

    Show the answer

    Answer: a · It reaches M2's implementation because the rightmost mixin becomes the immediate superclass.

    Dart applies mixins left-to-right, so the last mixin becomes the immediate superclass and shadows earlier ones; thus super.foo() from C resolves to M2. Distractor B confuses application order with inheritance priority, but left-to-right application means M2 is layered on top of M1, not beneath it.

    Read the full bite: How does Dart resolve mixin method conflicts and application order?

  22. Question 22 of 30

    Which statement accurately distinguishes the backpressure behavior of buffer(), conflate(), and collectLatest() in Kotlin Flow?

    Show the answer

    Answer: d · buffer() suspends the emitter when full, conflate() silently drops intermediate values, and collectLatest() cancels the active collector

    buffer() suspends the producer when its Channel is full rather than dropping values, conflate() silently discards intermediate emissions while the collector runs, and collectLatest() cancels the active collection block on each new emission. Option B is tempting because it uses the correct vocabulary but assigns the behaviors to the wrong operators.

    Read the full bite: How do buffer, conflate, and collectLatest manage Kotlin Flow backpressure?

  23. Question 23 of 30

    When processing a rapid stream of UI events, where each event triggers an expensive, cancellable background task, which Flow operator ensures only the latest event's task runs, cancelling any prior in-progress tasks?

    Show the answer

    Answer: c · collectLatest()

    collectLatest() is designed to cancel the processing block for a previous item if a new one is emitted, restarting the operation with the latest data. In contrast, conflate() only drops intermediate values, ensuring the collector receives the most recent value, but it does not cancel any long-running work already initiated for a prior item.

    Read the full bite: Explain backpressure in Kotlin Flows and its management operators

  24. Question 24 of 30

    A Flow emits frequent updates, but the collector's work is slow. To ensure resources are only spent on the latest item by cancelling work on stale ones, which approach is best?

    Show the answer

    Answer: c · Use `collectLatest()` to cancel the collector's current work block as soon as a new item is emitted from the producer.

    `collectLatest` is correct because it cancels the collector's ongoing work when a new value arrives, preventing wasted resources on stale data. `conflate` is a common misconception; it drops values on the producer side but does not cancel any ongoing work in the collector.

    Read the full bite: Explain backpressure in Kotlin Flows and its management operators

  25. Question 25 of 30

    A coroutine executes a CPU-intensive `while` loop without any suspend function calls. If its job is cancelled, what is required for the coroutine to actually stop?

    Show the answer

    Answer: a · The loop must periodically check the `isActive` property of the coroutine context and manually exit.

    Coroutine cancellation is cooperative, not preemptive. For code without suspension points, like a tight loop, the coroutine must manually check its `isActive` state to participate in cancellation.

    Read the full bite: How does coroutine cancellation work internally?

  26. Question 26 of 30

    Why does a CPU-intensive while-loop inside a coroutine ignore job.cancel() and keep running?

    Show the answer

    Answer: d · The loop never hits a suspension point where the coroutine checks its Job state

    Kotlin coroutine cancellation is cooperative, so a tight loop without suspension points never checks the Job's cancelling state and continues running. Distractor B is wrong because cancel() does not forcibly interrupt the underlying thread; it only sets a flag that standard suspend functions check at suspension points.

    Read the full bite: Describe coroutine cancellation mechanics and cooperative suspend functions

  27. Question 27 of 30

    Which statement accurately describes how job.cancel() influences a running coroutine?

    Show the answer

    Answer: b · It sets a flag, causing cooperative suspend functions or explicit isActive checks to throw a CancellationException.

    The correct answer (B) reflects that cancellation is cooperative: job.cancel() sets a flag, and the coroutine itself must check this flag (via suspend functions or isActive) to throw a CancellationException. Option (D) is a common misconception, as coroutine cancellation does not directly stop the underlying thread; it's an abstraction above threads.

    Read the full bite: How does coroutine cancellation work internally?

  28. Question 28 of 30

    Why can a Reanimated and Gesture Handler animation stay smooth even when the JavaScript thread is blocked?

    Show the answer

    Answer: d · Gesture tracking and worklet-driven animation run on the native UI thread via shared values, independent of the JS thread per frame

    Worklets and shared values let per-frame gesture and animation work run on the UI thread, so a busy JS thread does not stall it. The Animated native driver is more limited and does not provide gesture-driven worklets, so equating them is incorrect.

    Read the full bite: Gesture Handler and Reanimated on the UI thread

  29. Question 29 of 30

    What is the fundamental reason Kotlin's `reified` type parameters must be used with `inline` functions?

    Show the answer

    Answer: b · The inline mechanism enables the compiler to replace the generic type parameter with the actual type at the call site before JVM type erasure occurs.

    Correct answer (B) states that `inline` allows the compiler to substitute the generic type with its concrete type at the call site, effectively baking the type information into the bytecode before JVM type erasure. Option C is incorrect because `reified` does not bypass JVM type erasure; instead, the compiler works around it by substituting the type during inlining.

    Read the full bite: Explain Kotlin's `reified` type parameters and their use case

  30. Question 30 of 30

    Why can a reified type parameter be checked with is T at runtime despite JVM type erasure?

    Show the answer

    Answer: c · The compiler substitutes the concrete type into the inlined body at each call site.

    Inline expansion lets the compiler replace T with the concrete type at the call site, emitting a real instanceof check. Option A is tempting but wrong because reified does not rely on reflection or pass a Class object; it works by bytecode substitution during compilation.

    Read the full bite: What is a reified type parameter in Kotlin?

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