HireHireInterview Quizzes › Mobile App Developer

Mobile App Developer Interview Questions

Think you're ready? These are the questions that actually decide Mobile App Developer interviews. Warm up on Easy — then face the Hard round, where 95% of candidates crumble. 30 questions across 3 levels, instant score, completely free.

30Questions
3Difficulty levels
95%Fail the hard round
FreeInstant score
Easy
Warm-up · 10 Qs
Medium
Practical · 10 Qs
Hard
Brutal · 10 Qs
⚡ Take the Mobile App Developer quiz — get your score →

The Mobile App Developer interview questions

Below are the real questions, grouped by difficulty. Expand any one to reveal the correct answer and why — or take the timed quiz for a score you can share. Can you clear the Hard round?

Easy round 10 questions

In Android, which component is best suited to perform a long-running background task that must continue even if the user leaves the app (e.g., uploading a large file)?
  • A. Activity
  • B. BroadcastReceiver
  • C. Foreground Service ✓
  • D. Fragment
Correct answer: C. A Foreground Service continues running for user-initiated long tasks and shows a persistent notification so the system does not kill it.
On iOS, what is the primary purpose of the 'weak' keyword when declaring a property such as a delegate?
  • A. To make the property thread-safe
  • B. To avoid strong reference retain cycles that cause memory leaks ✓
  • C. To store the value in persistent storage
  • D. To make the property immutable
Correct answer: B. Declaring a delegate as weak prevents a retain cycle between the two objects, allowing ARC to deallocate them and avoid a memory leak.
In the Android Activity lifecycle, which callback is guaranteed to be called when an Activity becomes visible to the user but not yet in the foreground/interactive?
  • A. onCreate()
  • B. onStart() ✓
  • C. onDestroy()
  • D. onRestart()
Correct answer: B. onStart() is called when the Activity becomes visible to the user, before onResume() makes it interactive.
In Flutter, what is the key difference between a StatelessWidget and a StatefulWidget?
  • A. StatelessWidget cannot render UI; StatefulWidget can
  • B. StatefulWidget can hold mutable state that changes over the widget's lifetime; StatelessWidget cannot ✓
  • C. StatelessWidget runs on a separate isolate
  • D. StatefulWidget cannot be rebuilt once created
Correct answer: B. A StatefulWidget maintains mutable state via a State object and can rebuild when state changes, whereas a StatelessWidget is immutable once built.
You are building an app that must securely store the user's authentication token on the device. What is the recommended approach on Android?
  • A. Store it in SharedPreferences as plain text
  • B. Store it in the Android Keystore / EncryptedSharedPreferences ✓
  • C. Hardcode it in the app's source code
  • D. Store it in a public external storage file
Correct answer: B. The Android Keystore (or EncryptedSharedPreferences backed by it) provides hardware-backed encryption designed for storing sensitive credentials securely.
In React Native, which mechanism traditionally allows JavaScript code to communicate with native platform modules?
  • A. The Bridge ✓
  • B. The Garbage Collector
  • C. The Gradle build system
  • D. The Info.plist file
Correct answer: A. React Native's Bridge serializes and passes messages asynchronously between the JavaScript thread and native modules (now being replaced by the JSI/New Architecture).
Which HTTP status code should a mobile client interpret as 'the request succeeded but the user's session/token is no longer valid and re-authentication is required'?
  • A. 200 OK
  • B. 301 Moved Permanently
  • C. 401 Unauthorized ✓
  • D. 500 Internal Server Error
Correct answer: C. HTTP 401 Unauthorized indicates the request lacks valid authentication credentials, signaling the client to re-authenticate.
To keep the UI responsive, where should a network API call be performed in a mobile app?
  • A. On the main/UI thread synchronously
  • B. On a background thread/coroutine, updating the UI on the main thread when done ✓
  • C. Inside the layout XML file
  • D. In the app manifest
Correct answer: B. Network calls must run off the main thread to avoid blocking/ANR, and results are marshaled back to the main thread only to update the UI.
In Android's Jetpack architecture, what is the main responsibility of a ViewModel?
  • A. To directly draw pixels on the screen
  • B. To hold and manage UI-related data in a lifecycle-conscious way, surviving configuration changes ✓
  • C. To handle low-level Bluetooth communication
  • D. To replace the AndroidManifest.xml
Correct answer: B. A ViewModel stores and manages UI-related data across configuration changes like screen rotation, separating it from the View.
What is the correct way to make a mobile UI adapt to different screen sizes and densities rather than breaking on some devices?
  • A. Use fixed pixel (px) values for all dimensions
  • B. Use density-independent units (dp/pt) and responsive/constraint-based layouts ✓
  • C. Design only for one specific device resolution
  • D. Disable rotation and scaling entirely
Correct answer: B. Density-independent units and flexible layouts (ConstraintLayout, Auto Layout, Flexbox) let the UI scale correctly across screen sizes and densities.

Medium round 10 questions

In Android, you need to perform a network request when a button is tapped. Doing this directly on the main thread will cause a NetworkOnMainThreadException. Which approach is the recommended modern way to handle this?
  • A. Run the request inside a Kotlin coroutine launched on Dispatchers.IO ✓
  • B. Wrap the request in a try/catch block on the main thread
  • C. Increase the main thread priority before the call
  • D. Use runOnUiThread { } to execute the network call
Correct answer: A. Network I/O should run off the main thread, and launching a coroutine on Dispatchers.IO is the standard modern approach.
In iOS development, you fetch data from an API in a background closure and want to update a UILabel with the result. What must you do?
  • A. Update the label directly since closures run on the main thread
  • B. Dispatch the UI update to DispatchQueue.main.async ✓
  • C. Call the update inside DispatchQueue.global().async
  • D. Wrap the update in an autoreleasepool block
Correct answer: B. All UIKit updates must happen on the main thread, so UI changes from a background context must be dispatched to DispatchQueue.main.
A user reports that your app loses their form input when they rotate the screen on Android. What is the most likely cause?
  • A. The app is missing INTERNET permission
  • B. The Activity is recreated on configuration change and state was not saved ✓
  • C. The layout XML uses the wrong root element
  • D. The device has low memory and killed the process
Correct answer: B. A rotation triggers a configuration change that recreates the Activity, so unsaved state (e.g., via onSaveInstanceState or a ViewModel) is lost.
You are building a scrollable list of thousands of items in React Native. Which component should you use for best performance?
  • A. A ScrollView containing all items mapped with .map()
  • B. FlatList with a keyExtractor and renderItem ✓
  • C. A View with overflow set to scroll
  • D. Nested ScrollViews for pagination
Correct answer: B. FlatList virtualizes rendering so only visible items are mounted, unlike ScrollView which renders every child at once.
In Flutter, you have a StatefulWidget and change a variable that affects the UI. What must you call for the change to appear on screen?
  • A. build()
  • B. setState() ✓
  • C. initState()
  • D. notifyListeners()
Correct answer: B. setState() marks the widget dirty and schedules a rebuild so the UI reflects the updated state.
Your app must store a user's authentication token securely on the device. Which storage option is appropriate?
  • A. SharedPreferences on Android / UserDefaults on iOS
  • B. A plain text file in the app's cache directory
  • C. Android Keystore / iOS Keychain ✓
  • D. A global in-memory variable
Correct answer: C. The Keystore and Keychain are the OS-provided encrypted stores designed to hold sensitive credentials like tokens.
You submitted an Android app but it was rejected for requesting the ACCESS_FINE_LOCATION permission. The reviewer says location is only needed briefly when the user taps a button. What is the correct fix?
  • A. Request the permission at runtime only when the feature is used, and justify it ✓
  • B. Remove all permissions from the manifest
  • C. Move the permission request to Application.onCreate()
  • D. Request the permission silently in the background at launch
Correct answer: A. Runtime permissions should be requested contextually when the feature is used, with a clear justification, per platform policy.
In Git, you are working on a feature branch and want to incorporate the latest changes from main while keeping a clean, linear history before opening a PR. Which command is appropriate?
  • A. git rebase main ✓
  • B. git reset --hard main
  • C. git cherry-pick main
  • D. git push --force origin main
Correct answer: A. Rebasing your feature branch onto main replays your commits on top of the latest main, producing a linear history.
Your iOS app is crashing with 'unexpectedly found nil while unwrapping an Optional value.' What is the safest way to prevent this class of crash?
  • A. Force unwrap with ! after checking the debugger
  • B. Use optional binding (if let / guard let) or nil-coalescing ✓
  • C. Declare all variables as implicitly unwrapped optionals
  • D. Wrap the code in a do/catch block
Correct answer: B. Safe unwrapping via if let, guard let, or ?? handles the nil case explicitly instead of crashing on a forced unwrap.
A REST API call in your app returns HTTP 401. What does this typically indicate your app should do?
  • A. Retry the same request immediately with no changes
  • B. Refresh or re-obtain the authentication credentials ✓
  • C. Show a 'server is down' message
  • D. Switch the request from HTTPS to HTTP
Correct answer: B. A 401 Unauthorized means the credentials are missing or invalid, so the app should refresh the token or prompt re-authentication.

Hard round 10 questions

A ViewModel launches `viewModelScope.launch { val a = async { fetchA() }; val b = async { fetchB() }; combine(a.await(), b.await()) }`. `fetchB()` throws. What happens?
  • A. Only `b.await()` throws; `a` keeps running and the parent coroutine survives, logging the error
  • B. The exception from `fetchB()` propagates, cancels the parent coroutine (and sibling `a`), and crashes unless a handler is installed on the scope ✓
  • C. `async` swallows the exception until `a.await()` is also called, so nothing happens
  • D. A `CoroutineExceptionHandler` on `async` catches it and resumes normally
Correct answer: B. Because these `async` builders inherit the regular `Job` of `viewModelScope`, a child failure propagates upward, cancelling the parent and sibling regardless of when `await()` is called.
A Compose list item recomposes on every scroll frame even when its data is unchanged. The item takes `List<Item>` and a `() -> Unit` lambda as parameters. What is the MOST likely root cause and fix?
  • A. `List<Item>` is inferred unstable, so the composable is never skippable; use `ImmutableList`/`@Immutable` or `kotlinx.collections.immutable` ✓
  • B. The lambda allocates each recomposition; wrapping it in `remember` fixes stability of the list too
  • C. `derivedStateOf` must wrap the list read to make it observable and skippable
  • D. Marking the item `@Composable @Stable` forces Compose to skip regardless of parameter stability
Correct answer: A. The Compose compiler treats `List` as unstable (it could be a mutable implementation), making the function non-skippable; using a provably-immutable collection type restores skippability.
You call `stateFlow.collect { }` inside a `Fragment.onViewCreated` using `lifecycleScope.launch { ... }` (not `repeatOnLifecycle`). What is the concrete bug?
  • A. Collection stops at `onStop` and correctly restarts at `onStart`, so it is safe
  • B. StateFlow is cold, so nothing is ever collected without `repeatOnLifecycle`
  • C. The coroutine keeps collecting while the Fragment is in the background and holds the destroyed View, wasting work and risking a leak ✓
  • D. `StateFlow` conflates emissions, so background collection is automatically paused
Correct answer: C. `lifecycleScope.launch` only cancels at `onDestroy`, so the collector stays active in the background updating a stopped/destroyed view; `repeatOnLifecycle(STARTED)` is what pauses and resumes it.
Two offline devices edit the same record's different fields while disconnected, then both sync. The backend must remain single source of truth and no user edit should be silently lost. Which strategy best satisfies this?
  • A. Last-write-wins by wall-clock timestamp on the whole record
  • B. Per-field merge / CRDT-style reconciliation on the server so non-conflicting field edits both survive ✓
  • C. Reject the later sync with a 409 and force the user to re-enter everything
  • D. Vector clocks that only detect conflicts but always discard the older sibling
Correct answer: B. Whole-record LWW loses one device's field edit; per-field (or CRDT) merge lets independent field changes coexist while the server stays authoritative for true conflicts.
In Swift, a `URLSession` completion closure captures `self` and stores the returned task in a `self.task` property. Even after the request finishes, the object leaks. Why, and what is the minimal fix?
  • A. `URLSession` is a singleton so it always retains delegates; switch to `.shared`
  • B. The closure strongly captures `self` while `self` retains the task via `self.task`, forming a retain cycle; use `[weak self]` in the capture list ✓
  • C. ARC cannot free `URLSessionTask`; call `task.cancel()` in `deinit`
  • D. Completion closures are always `@escaping` and leak by design; mark it non-escaping
Correct answer: B. `self` → `task` → closure → `self` is a strong reference cycle; capturing `[weak self]` breaks it so ARC can deallocate once the request completes.
Under Swift 6 strict concurrency, you have a mutable cache accessed from many `Task`s. Which primitive gives compile-time data-race safety with the least boilerplate?
  • A. A `class` guarded by a manual `NSLock` around every access
  • B. An `actor` wrapping the cache, so all mutable access is serialized and isolated ✓
  • C. A `DispatchQueue.sync` barrier on a concurrent queue
  • D. Marking the cache `@unchecked Sendable` and hoping callers serialize
Correct answer: B. An `actor` provides compiler-enforced isolation and serialized access to its mutable state, which is exactly what strict concurrency checking rewards, without manual locking.
A React Native (old architecture) screen janks when a native module returns a large payload consumed on scroll. Why does JSI/TurboModules fix it where the legacy bridge could not?
  • A. JSI adds more worker threads to the bridge queue so serialization is parallelized
  • B. The legacy bridge serializes all calls to JSON and batches them async over a single queue; JSI lets JS hold direct synchronous references to native objects, removing the serialization bottleneck ✓
  • C. TurboModules move all JS execution onto the UI thread, eliminating frame drops
  • D. Fabric compresses the JSON payload so it crosses the bridge faster
Correct answer: B. The old bridge's cost is asynchronous JSON serialization over one queue; JSI exposes native objects to the JS engine directly (synchronously, no JSON), which TurboModules/Fabric build on to remove that bottleneck.
An Android app is killed under memory pressure while a form is half-filled. On relaunch the user lands back with an empty form. `onSaveInstanceState` was implemented but data still lost. Which is the correct explanation?
  • A. `onSaveInstanceState` is only for config changes; process death also needs the saved Bundle to be restored in `onCreate`/`SavedStateHandle`, and large data exceeding Binder limits is dropped ✓
  • B. Process death never triggers `onSaveInstanceState`, so no state can ever be saved
  • C. `ViewModel` survives process death and should have held the form state
  • D. Restoration fails because `onRestoreInstanceState` runs before `onCreate`
Correct answer: A. `onSaveInstanceState` does fire before process death, but restored state must be read back (via Bundle/SavedStateHandle) and stay within the ~1MB Binder transaction limit or it is silently dropped; ViewModels do NOT survive process death.
A coroutine wrapped in `withContext(NonCancellable)` or doing a tight CPU loop `while(true){}` ignores `job.cancel()`. Why doesn't it stop, and what is the correct fix for the CPU-loop case?
  • A. Cancellation is cooperative; a busy loop never suspends or checks `isActive`, so add `ensureActive()`/`yield()` or check `isActive` in the loop ✓
  • B. `cancel()` only sets a flag for structured concurrency and never affects running code
  • C. Dispatchers.Default disables cancellation for performance
  • D. The loop needs `SupervisorJob` to become cancellable
Correct answer: A. Coroutine cancellation is cooperative and only takes effect at suspension/check points, so a non-suspending busy loop must explicitly call `yield()`/`ensureActive()` or test `isActive`.
You want a `Flow` that (a) has no initial value, (b) never replays to new subscribers, and (c) drops events if there are momentarily no collectors, for one-shot navigation events. Which construct fits?
  • A. `StateFlow` with an initial dummy value
  • B. `SharedFlow(replay = 0, extraBufferCapacity = 0, onBufferOverflow = SUSPEND)` — with an active collector, or `Channel` for strict single-consumer delivery ✓
  • C. `StateFlow(null)` mapped with `filterNotNull`
  • D. `SharedFlow(replay = 1)`
Correct answer: B. `replay = 0` prevents redelivery to late subscribers (StateFlow always replays its latest), matching one-shot event semantics; a `Channel` is the stricter single-consumer alternative.

Prep for another role

Questions are original, written and independently verified for HireHire's role interview quizzes. They reflect the kind of knowledge Mobile App Developer interviews test, not any specific company's questions. HireHire maps live tech & IT jobs across India, updated regularly. Last updated: July 2026.