Skip to content

Make LazyVStack and LazyHStack actually lazy - #52

Merged
colemancda merged 6 commits into
masterfrom
feature/lazy-stacks
Jul 23, 2026
Merged

Make LazyVStack and LazyHStack actually lazy#52
colemancda merged 6 commits into
masterfrom
feature/lazy-stacks

Conversation

@colemancda

Copy link
Copy Markdown
Member

LazyVStack/LazyHStack were typealiases to VStack/HStack — they had the SwiftUI name but built every element eagerly. They're now real lazy containers.

Where the laziness comes from

List takes its data directly, so it can register an item provider. A lazy stack takes a ViewBuilder, so the data is one level down — inside the ForEach. Since buildBlock returns a single child unwrapped, LazyVStack { ForEach(…) } hands the stack the ForEach itself; a new internal _LazyElementProvider conformance exposes its count, per-element identity keys, and on-demand resolution. Elements are keyed by identity exactly as the eager path was, so element @State still survives scrolling and reordering.

Content that isn't a single ForEach (mixed or static) falls back to resolving children eagerly — correct, just not lazy — rather than failing.

The nesting hazard

ScrollView { LazyVStack { … } } is the idiomatic SwiftUI shape, but it maps to a LazyColumn inside a verticalScroll Column, which measures the child with unbounded height and crashes Compose. So when a ScrollView's only child is a lazy stack, the ScrollView now yields scrolling to it.

Verification

  • swift test — 4 new tests, 79 total passing. The load-bearing one builds a 10,000-element LazyVStack and asserts a probe counter is 0 after evaluation, then exactly 1 after requesting index 42 — i.e. nothing is built until asked for. Others cover identity keys, the eager fallback, and LazyHStack.
  • Emulator (new "Lazy Stacks" screen): a 500-item LazyHStack and a 10,000-row LazyVStack on one screen, each scrolling independently — reached item 40+ and Row 2964 respectively, with no crash from the ScrollView nesting.

Note

LazyVGrid/LazyHGrid already had their own lazy path and are unchanged.

@colemancda
colemancda merged commit d133364 into master Jul 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant