Tutorials › Launch Lab Notes › Chapter 15

Ten Thousand Rows

LabChapter 15 of the Launch Lab Notes22 minAugust 19, 2026Intermediate

How many row bodies does SwiftUI run for 10,000 shipments?

If the answer is "ten thousand," your scroll view just paid for rows the user cannot see. If the answer is "about a screenful," you picked a container that stays near the viewport. This bench runs the same data three ways and prints the body count on the cards.

Filmstrip: List settles at 15 bodies, Lazy at 23, then Eager climbs to 10,000 bodies in about 3.2 seconds.

Three containers, one counter

Every row calls counter.bump() on the way into body. The cards read that counter after a short settle. The data set is always 0..<10_000. The only variable is the container.

List(0..<Self.rowCount, id: \.self) { i in
  ShipRow(index: i, counter: counter)
}
 
ScrollView {
  LazyVStack { ForEach(0..<Self.rowCount, id: \.self) { … } }
}
 
ScrollView {
  VStack { /* every index — the control */ }
}

List — fifteen bodies

LIST active: 15 bodies, 746 ms. Visible rows 0000 through 0013. LIVE 15 bodies 746 ms.
CardBodiesms
LIST15746

List built about a screen of ShipRows and stopped. The footer matches the card: LIVE 15 bodies 746 ms.

LazyVStack — twenty-three bodies

LAZY active: 23 bodies, 746 ms. Visible rows 0000 through 0022. LIVE 23 bodies 746 ms.
CardBodiesms
LAZY23746

LazyVStack in a ScrollView also stayed near the viewport — 23 bodies for rows 0000…0022. Same settle window as List, same 746 ms on this run. Slightly more rows than List because the row chrome is thinner without list separators.

Eager VStack — the control

EAGER active: 10,000 bodies, 3,202 ms. NOW EAGER VStack (control). LIVE 10,000 bodies 3,202 ms.
CardBodiesms
EAGER10,0003,202

The rose card is the control: a plain VStack inside ScrollView that owns every index. Observed result: 10,000 bodies in 3,202 ms — about 4× the settle time of List/Lazy, and ~667× the body count of List.

Full-height 10k rows froze the shutter

The first control put 10,000 normal ShipRows in one VStack. The simulator spent tens of seconds laying them out; captures froze on a stale List frame with 0 bodies. The shipping control keeps a 24-row full-height prefix, then 1pt rows for the rest so every index still runs body once and the money shot can finish. The lesson is the body count, not the point size.

ForEach(0..<Self.eagerVisiblePrefix, id: \.self) { i in
  ShipRow(index: i, counter: counter)
}
ForEach(Self.eagerVisiblePrefix..<Self.rowCount, id: \.self) { i in
  TinyShipRow(index: i, counter: counter) // Color.clear.frame(height: 1)
}
Lazy is a contract, not a synonym for ScrollView

Wrapping an eager VStack in ScrollView does not make it lazy. Only List, LazyVStack, LazyHStack, and friends defer work. The control exists so that sentence is a measurement, not a slogan.

Mini-exercise

Change eagerVisiblePrefix to Self.rowCount so every eager row is a full ShipRow. Re-run on a device or sim. Watch whether the EAGER card still reaches 10,000 before you get bored — that is the cost the 1pt stand-ins were hiding.

Challenges

  1. Add a fourth card that scrolls the List to row 9_000 and report how many new bodies run after the jump.
  2. Swap LazyVStack for LazyVGrid with two columns and photograph the body count against the same 10k data.
  3. Instrument with os_signpost around measure and compare Instruments' hitch time to the on-screen ms.
  4. Replace TinyShipRow with full ShipRows but only for a 2_000-row data set — find the size where eager becomes unusable on your machine.
  5. XCUITest: assert the EAGER card's static text contains 10000 (or 10,000) after the settle, and that LIST never contains it on a pinned -mode list launch.

Key Points

Next up: navigation — a NavigationStack value path that deep-links three levels, pops to root, and restores after relaunch, with the path printed on screen the whole time.

SwiftUI
SwiftUI tutorials for building native app screens, layouts, navigation, and state-driven interfaces.
Swift
Swift fundamentals for app developers who want to understand the language behind real iOS and macOS apps.
Ship iOS
Shipping workflows for iOS apps.
📚 Go deeper with LIPAI WANG’s hands-on Udemy bootcampsBrowse all courses →
← Ch 14: The Schema MovedCh 16: Three Levels, One Path→
SwiftUIUltimate SwiftUI SeriesSwiftUI tutorials for building native app screens, layouts, navigation, and state-driven interfaces.SwiftUltimate Swift SeriesSwift fundamentals for app developers who want to understand the language behind real iOS and macOS apps.Ship iOSShip iOS Apps SeriesShipping workflows for iOS apps: signing, TestFlight, App Store Connect, CI, and release hygiene.

Ship your apps faster

When you're ready to publish your Swift app to the App Store, Simple App Shipper handles metadata, screenshots, TestFlight, and submissions — all in one place.

Try Simple App Shipper
5 free articles remainingSubscribe for unlimited access