AceMobileInterview

Interview guides / ios

iOS Debugging Interview Questions

iOS debugging interview questions covering crashes, retain cycles, main-thread jank, races, and how to investigate under time pressure.

Debugging rounds test how you investigate, not whether you memorized an API. Interviewers watch your hypothesis order: reproduce, isolate, fix, verify.

AceMobileInterview includes buggy Xcode zips with planted crashes, jank, and races so you can rehearse the same muscle.

  1. 1.A table view scrolls fine with 20 rows and janks with 2,000. Where do you look first?

    Main-thread work in cell configuration: JSON parse, image decode, heavy Auto Layout, synchronous disk. Profile with Time Profiler / Hitches, move work off main, reuse cells correctly, and decode images at display size.

  2. 2.A view controller never deinits. How do you find the leak?

    Check escaping closures capturing self strongly, timer/NotificationCenter observers, and delegate ownership. Use Memory Graph, add deinit logs, and break cycles with weak/unowned appropriately.

  3. 3.You see intermittent UI corruption after concurrent network responses. What is your approach?

    Look for UI updates off MainActor, unsynchronized mutable state, and completion handlers racing. Serialize UI application on the main actor and protect shared models with actors or clear queue boundaries.

  4. 4.App crashes only after backgrounding overnight. What do you suspect?

    Process footprint and state restoration issues, expired tasks, force-unwrap of cleared session state, or background task expiry. Reproduce with background fetch simulations and nil-safe session restore paths.

  5. 5.Download the buggy Xcode zip. The home feed freezes/stutters on load with a large JSON payload. Find and fix the planted performance issues (≈45–60 min).

    ## Scenario Candidates get a UIKit feed that “works” but freezes on large payloads. ## How to investigate - Reproduce with the bundled large `feed.json` - Time Profiler / Main Thread Checker - Search for `Data(contentsOf` / `JSONDecoder().decode` on the main path ## Planted bug 1: Synchronous network + decode on main **Symptom:** UI freezes ~1s on appear **Root cause:** `FeedViewController.load()` calls `Data(contentsOf:)` and `JSONDecoder` on the main thread **Fix:** `DispatchQueue.global(qos: .userInitiated).async` (or async/await) for load+decode; hop back to main only to assign `items` and `reloadData()` ## Planted bug 2: Image downsample on main **Symptom:** Scroll hitch when cells appear **Root cause:** `UIImage(data:)` + redraw in `cellForRowAt` on main **Fix:** Decode/downsample off-main; cache `UIImage`; cancel work on reuse ## Planted bug 3: Architecture smell **Symptom:** Hard to test / reason about **Root cause:** Networking lives inside the view controller **Fix:** Extract `FeedLoading` protocol + `LocalFeedLoader`; VC only binds results ## Optimization tips - Prefer `URLSession` even for local files when teaching async patterns - Use `autoreleasepool` when decoding many images - Avoid doing I/O inside `cellForRowAt` ## What to say in the interview - How you proved the main-thread hitch before changing code - What you measured after (scroll FPS / time to first content)

  6. 6.Download the buggy zip. Deleting notes or opening detail can crash. Find unsafe indexing/force-unwraps and harden the flow (≈30–45 min).

    ## Scenario Notes list crashes on empty state and after rapid delete + detail tap. ## How to investigate - Turn on Exception Breakpoint - Reproduce: delete last note; delete then immediately tap a row - Search for `!` force unwraps and `notes[indexPath.row]` ## Planted bug 1: Force unwrap on selected note **Symptom:** Crash when opening detail with no selection **Root cause:** `detailNote!` in segue / push helper **Fix:** `guard let note else { return }`; disable detail when empty ## Planted bug 2: Stale index after delete **Symptom:** Out-of-bounds after delete animation **Root cause:** Action handler captures `indexPath.row` then mutates array before use **Fix:** Capture `note.id`; look up by id; or delete via `IndexPath` inside `performBatchUpdates` consistently ## Planted bug 3: Missing empty state **Symptom:** Table assumes ≥1 row elsewhere **Root cause:** `numberOfRows` can be 0 but toolbar still calls `notes[0]` **Fix:** Empty view; guard all `notes[i]` access ## Optimization tips - Prefer value ids over raw indexes across async UI callbacks - `fatalError` in production paths is a smell—use `assertionFailure` + recovery ## What to say in the interview - How you mapped crash stacks to the exact line - How you added a regression check (empty list + rapid tap)

  7. 7.Memory grows each time Profile is opened. Console also prints an access token. Find the retain cycle and unsafe logging (≈30–45 min).

    ## Scenario Profile screen leaks; security review flags tokens in logs. ## How to investigate - Debug Memory Graph / Allocations — filter `ProfileViewController` - Confirm `deinit` never prints - Grep for `print(` / `NSLog` / token fields ## Planted bug 1: Retain cycle in callback **Symptom:** VC count increases every push **Root cause:** `api.onProfile = { self.render($0) }` strong capture; `APIClient` owned by VC **Fix:** `{ [weak self] in self?.render($0) }`; break cycle in `deinit` / `viewDidDisappear` ## Planted bug 2: Timer / NotificationCenter not removed **Symptom:** Extra strong refs **Root cause:** `Timer.scheduledTimer` without `invalidate`; observer never removed **Fix:** Store timer, invalidate in `deinit`; use block-based observers carefully with weak self ## Planted bug 3: Logging secrets **Symptom:** Access token in console **Root cause:** `print("auth \(token)")` in `Session.store` **Fix:** Remove; if needed log only `token.prefix(4)` in DEBUG with a redaction helper—never full secrets ## Optimization tips - Prefer `[weak self]` as default in escaping closures owned by long-lived objects - Add a `deinit { print("Profile deinit") }` while debugging (remove later) ## What to say in the interview - How you confirmed the cycle in Memory Graph - Why logging tokens fails security review even in debug builds shipped to testers

  8. 8.SettingsViewController does networking, parsing, disk I/O, and UI—and embeds an API key. Refactor the worst smells (≈45–60 min).

    ## Scenario Classic “temporary” VC that absorbed every concern. ## How to investigate - Skim `SettingsViewController.swift` size and responsibilities - Grep for `api_key` / `sk_live` / secrets - Note `Data.write` on the button action (main thread) ## Planted bug 1: Hardcoded API key **Symptom:** Secret in source control **Root cause:** `let apiKey = "sk_live_..."` in the VC **Fix:** Move to xcconfig / info plist placeholder / env; never commit real keys; use a stub in the exercise ## Planted bug 2: God object **Symptom:** Untestable blob **Root cause:** URLSession + JSONDecoder + UserDefaults + UI in one type **Fix:** Extract `SettingsRepository` (load/save remote+local); VC becomes binder ## Planted bug 3: Main-thread disk write **Symptom:** Brief hitch on Save **Root cause:** Synchronous `data.write(to:)` on main **Fix:** Background write; atomic replace; main-thread success toast only ## Optimization tips - Interview bar is “extract boundaries,” not a perfect architecture diagram - Call out how you’d add unit tests around the repository ## What to say in the interview - How you prioritized secret removal first - What you left as intentional stretch

  9. 9.Pay can fire twice on double-tap; cart totals flicker under concurrency. Fix races and shared mutable state (≈45–60 min).

    ## Scenario Checkout is not idempotent under rapid taps; cart is mutated off-main unsafely. ## How to investigate - Double-tap Pay with network delay enabled (mock sleep) - Thread Sanitizer if available - Trace `cart.items` writes ## Planted bug 1: No re-entry guard on Pay **Symptom:** Two `checkout()` network calls **Root cause:** Button stays enabled; async method has no `isCheckingOut` flag **Fix:** Disable button / set flag at start; clear in `defer` on main; ignore taps while in flight ## Planted bug 2: Unsynchronized cart mutation **Symptom:** Flickering totals / rare crash **Root cause:** `Cart.shared.items.append` from background + main **Fix:** Isolate mutations on one queue (actor / serial queue) or only mutate on main ## Planted bug 3: UI update from background **Symptom:** Occasional UIKit threading warnings **Root cause:** Completion handler sets `totalLabel.text` off-main **Fix:** `DispatchQueue.main.async` for all UIKit ## Optimization tips - Client-side single-flight is necessary but say you’d also want server idempotency keys - Prefer structured concurrency (`Task` + cancellation) over fire-and-forget ## What to say in the interview - Difference between UI guard vs true idempotency - How you’d test with stress taps

  10. 10.Drafts sometimes vanish; truncated JSON can crash the next launch. Fix persistence races and atomicity (≈45–60 min).

    ## Scenario Draft store writes JSON unsafely and crashes on bad data. ## How to investigate - Force-kill mid-save (or call save repeatedly while editing) - Inspect the drafts file after a crash - Trace `JSONSerialization` / `Data.write` ## Planted bug 1: Non-atomic write **Symptom:** Truncated file after kill **Root cause:** Writing directly to the final path **Fix:** Write to `*.tmp` then `FileManager.replaceItemAt` / atomic options ## Planted bug 2: Crash on decode failure **Symptom:** Launch crash loop **Root cause:** `try!` decode of cache **Fix:** `try?` / `do-catch` → empty draft; optionally quarantine bad file ## Planted bug 3: Unserialized read/write **Symptom:** Intermittent corruption **Root cause:** Saves from textDidChange without coalescing; overlapping writes **Fix:** Serial queue or actor; debounce saves (e.g. 300ms) ## Optimization tips - Mention File Coordination / `NSFileCoordinator` for shared containers - For modern apps, SwiftData/Core Data reduce some of these footguns—but know the file-level answer ## What to say in the interview - How you reproduced partial writes - Why atomic replace matters on mobile (process death is normal)

Keep going with AceMobileInterview

Reading helps. Timed practice locks it in. Jump into the interactive track for algorithms with LeetCode links, studio projects, debugging zips, and mobile system design.

iOS trackAndroid trackMore guides