Question 1
Diagnosing scrolling jank
You’re building a feed with complex cells, images, gradients, async content, dynamic text, and scrolling feels janky. Walk me through how you would diagnose and fix it.
Follow-ups
- What tools would you use?
- How do you tell layout, rendering, image decoding, and main-thread work apart?
Answer outline
Reproduce first: same device class, Release schema, real data volume. Note whether jank is constant (every frame) or spikes when new rows appear or images.
Profile before guessing: Hook up a real device to Instruments → Time Profiler (main thread + heaviest symbols). Look for main-thread stacks doing decode, JSON, regex, or layout thrash.
Separate the four causes:
- 1.Layout (long
_UIViewLayoutEnginestacks, constraint thrash) — simplify constraints, split heavy cells, cache row heights, eliminatelayoutIfNeeded()storms. - 2.Rendering (offscreen layers, blur, shadows) — reduce layer effects or rasterize deliberately.
- 3.Images (large assets hitting the main thread) — downsample to display size, decode off main thread.
- 4.Main-thread work (JSON, I/O) — move to
async/awaitpipelines; only touch UI on main.
Principles
- One hypothesis at a time—profile, change one thing, compare traces.
- Heavy feeds need budgets: max work per frame for decode + layout.
- Identity (SwiftUI) and reuse (UIKit) wrong → extra rebuilds that feel like jank.
Load data in the background, create UIImage off main if needed, assign on MainActor.
Task {
let data = try await loader.data(for: url)
let image = await decodeOffMain(data) // background
await MainActor.run { imageView.image = image }
}
Follow-up angles
- Signposts:
os_signpostaround cell configure and image apply to correlate spikes with code.