Skip to main content

Mobile engineer frontend interview questions

3 questions on frontend for mobile engineer candidates. Expect prompts such as “Debug a slow React Native screen crossing the native bridge” and “Explain SwiftUI state management”, each with a worked answer outline and the follow-ups interviewers push on.

Questions

Showing 1 to 3 of 3 frontend questions.

As asked

A React Native feed screen drops frames when scrolling because it sends frequent events between JavaScript and native code. How do you diagnose and fix it?

Sample answer outline

Start with measurement in Flipper, React DevTools, native profilers, and frame timing rather than assuming React is the bottleneck. Look for expensive renders, oversized serialized payloads crossing the bridge, synchronous native calls, image decode work, and layout thrash. Fixes include moving high-frequency work to native or JSI, reducing event payloads, memoising row components, using FlatList correctly, and pre-sizing media. A senior answer explains the old bridge versus the new architecture without pretending migration alone fixes bad rendering. Candidates often miss that JavaScript can be idle while the UI thread is blocked.

Expect these follow-ups

  • What changes under Fabric and TurboModules?
  • How do you separate JavaScript thread stalls from UI thread stalls?
  • When would you write a native module instead of staying in JavaScript?
performancenative-bridgemobile

As asked

Explain the difference between @State, @StateObject, @ObservedObject, @EnvironmentObject, and @Bindable in SwiftUI. When do you reach for each?

Sample answer outline

@State: value-type local state owned by the view. @StateObject: reference-type owned by the view, lifecycle tied to the view. @ObservedObject: reference-type passed in, lifecycle owned elsewhere. @EnvironmentObject: injected through the environment, for app-wide singletons like a user session. @Bindable (iOS 17+, with the Observation framework): for two-way binding on @Observable classes, replacing the older pattern. The rule of thumb: pick the simplest one that owns the lifecycle correctly. State leaks happen when you mix @StateObject and @ObservedObject incorrectly and the model gets recreated on every render.

Reference implementation (swift)

Swift
import SwiftUI

@Observable
final class CartModel {
    var items: [LineItem] = []
    var total: Decimal { items.reduce(0) { $0 + $1.price } }
}

struct CartView: View {
    // Owned here, lives as long as the view
    @State private var cart = CartModel()

    var body: some View {
        NavigationStack {
            List(cart.items) { item in
                LineItemRow(item: item)
            }
            .navigationTitle("Cart")
            CheckoutButton(cart: cart)
        }
    }
}

struct CheckoutButton: View {
    // Passed in, two-way bindings via @Bindable when needed
    @Bindable var cart: CartModel

    var body: some View {
        Button("Checkout (\(cart.total))") { /* ... */ }
    }
}

Expect these follow-ups

  • Why is the @Observable macro a better default than ObservableObject?
  • When does @EnvironmentObject hurt testability?
  • What is the equivalent of useEffect in SwiftUI?
swiftuistate-managementios

As asked

Implement pull-to-refresh on a FlatList in React Native with proper handling of in-flight refreshes and error states.

Sample answer outline

FlatList accepts a refreshing prop (boolean) and onRefresh callback. Manage local state for refreshing. On pull, set refreshing=true, fetch new data, on settle (success or error) set refreshing=false. Handle the case where the user pulls while a refresh is already in flight: ignore the second trigger. Show an error toast on failure rather than swallowing it. For long lists with cursor pagination, pull-to-refresh resets to the first page; consider whether you want to keep the user's scroll position.

Reference implementation (typescript)

TypeScript
import { FlatList, RefreshControl } from "react-native";
import { useState, useCallback } from "react";

export function Feed() {
  const [items, setItems] = useState<Item[]>([]);
  const [refreshing, setRefreshing] = useState(false);

  const onRefresh = useCallback(async () => {
    if (refreshing) return;
    setRefreshing(true);
    try {
      const next = await fetchFeed();
      setItems(next);
    } finally {
      setRefreshing(false);
    }
  }, [refreshing]);

  return (
    <FlatList
      data={items}
      keyExtractor={(i) => i.id}
      renderItem={({ item }) => <FeedRow item={item} />}
      refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
    />
  );
}

Expect these follow-ups

  • How does this differ on iOS vs Android visually?
  • How would you implement infinite scroll alongside pull-to-refresh?
  • When does pull-to-refresh hurt UX?
react-nativeuxlists