You get 60 to 90 minutes to build a small app live (a photo search, a movie list from JSON, a recipe app, a list with counters). How do you spend the time and what do interviewers look for?

29.3.1 · L3 · Interview

Reported at: Uber, Swiggy, DoorDash, Robinhood

Predict first

Does a debounced, cancellable search view model with an injected service and a SwiftUI screen compile against the iOS SDK?

import SwiftUI

struct Photo: Identifiable, Decodable, Hashable { let id: String; let title: String }

protocol PhotoService: Sendable { func search(_ text: String) async throws -> [Photo] }

@MainActor @Observable final class SearchViewModel {
    enum State: Equatable { case idle, loading, loaded([Photo]), failed(String) }
    private(set) var state: State = .idle
    private let service: PhotoService
    private var task: Task<Void, Never>?
    init(service: PhotoService) { self.service = service }

    func queryChanged(_ text: String) {
        task?.cancel()                                   // only the latest query may update the screen
        guard !text.isEmpty else { state = .idle; return }
        task = Task {
            try? await Task.sleep(for: .milliseconds(300))      // debounce
            guard !Task.isCancelled else { return }
            state = .loading
            do { let r = try await service.search(text); if !Task.isCancelled { state = .loaded(r) } }
            catch { if !Task.isCancelled { state = .failed("Could not load photos") } }
        }
    }
}

struct SearchScreen: View {
    @State private var model: SearchViewModel
    @State private var text = ""
    init(service: PhotoService) { _model = State(initialValue: SearchViewModel(service: service)) }
    var body: some View {
        NavigationStack {
            Group {
                switch model.state {
                case .idle: ContentUnavailableView("Search photos", systemImage: "magnifyingglass")
                case .loading: ProgressView()
                case .loaded(let photos): List(photos) { Text($0.title) }
                case .failed(let message): ContentUnavailableView(message, systemImage: "exclamationmark.triangle")
                }
            }
            .searchable(text: $text)
            .onChange(of: text) { _, new in model.queryChanged(new) }
            .navigationTitle("Photos")
        }
    }
}
  • Yes, it compiles
  • No: @Observable cannot be used with @MainActor
  • No: ContentUnavailableView needs a description

Mental model. Building a bridge on a clock: first one plank all the way across so people can walk, then handrails and paint, never a beautiful half bridge.

Reported formats include a Flickr-style photo search built in about 90 minutes (Uber), a movie or recipe app from provided JSON (Swiggy, DoorDash), and a tappable list with counters from a blank Xcode project (Robinhood), all from anonymous candidate write-ups, so treat them as typical rather than guaranteed. A workable plan: spend 5 to 10 minutes asking about requirements, platform (UIKit or SwiftUI), offline, errors and what “done” means, and write the plan in a comment. Build a thin vertical slice first: the model, a stub or real service, a view model that holds state, and a screen that shows loading, loaded and failed. Only then add polish: search debounce, cancel of stale requests, empty states, caching, accessibility.

Interviewers score structure and judgement more than features: a view model separate from the view, an injected service behind a protocol (so it is testable, as the compile-checked skeleton shows), explicit state instead of several booleans, cancellation of an older request when a newer one starts, and error and empty states. Keep the code compiling and runnable at every step, name things clearly, talk through trade-offs, and leave the last 5 to 10 minutes to run it, test one failure path and say what you would add (pagination, image caching, tests). Do not spend the time on pixel-perfect UI, and avoid a giant view controller or view with networking inside. The check compiles such a skeleton against the iOS SDK; it does not prove the app works, only that this shape is valid.

It compiles: this proves the shape is valid, not that the app behaves correctly.

Common traps

  • Starting with the UI details. A pretty screen with no data layer cannot be extended or tested.
  • Using several booleans (isLoading, hasError) for state. Impossible combinations appear; use one enum.

They might follow up with

  • SwiftUI or UIKit? Use what you are fastest and cleanest in, say why, and confirm the interviewer has no constraint.
  • What do you do when you are stuck? Say so, state the smallest next step, and keep the app working.

Related questions

  • 29.3.2 Machine-coding round: build a generic networking layer in about 45 minutes. What do you build, and how do you…
  • 29.3.3 In a live round you are given existing code to review and then asked to add a feature to it. How do you…
  • 29.2.2 Implement a hash map from scratch (get, set, remove), then extend it: what do you test and how does resizing…

Machine-coding round: build a generic networking layer in about 45… →