LavaUI docs

struct ForEach

Swift

struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable, Content : View

Variable-length children with explicit key identity. Fragment: children splice into the parent (inherits row/column). No buildArray — plain for in builders is intentionally unsupported.

Children are built in init, not at mount time. That is a correctness decision, not a performance one, and it is the whole reason this type looks the way it does.

ViewNode.computeBody evaluates view.body inside withObservationTracking, so a read registers a dependency only if it happens while that call runs. When this type stored its content as an @escaping closure and called it later — during mount and reconcile — every observable read inside that closure landed outside the tracking scope and subscribed to nothing. A row written as

ForEach(fits, id: \.value) { fit in
    PickerRow(..., selected: store.fit == fit.value)   // registers nothing
}

drew the right answer once and then never updated, while the same comparison hoisted into body worked. LavaSettings shipped that bug on its Background page: the wallpaper changed and the radio button did not move.

Deferring the closure could not be fixed by tracking it separately. The closure captures the enclosing body's locals and data came from that body too, so ForEachFragmentNode cannot regenerate children on its own — the only correct response to such a dependency changing is "run the parent body again", which is exactly what registering during the parent body already means. Building here makes the reads happen in the parent body, where they belong, and the trap becomes unwriteable rather than merely documented.

The cost is that every element's view value is built on each body pass. Those are cheap structs; the retained work — nodes, Yoga, draw lists — is still reconciled by key below, not rebuilt. Nothing in this repo feeds an unbounded collection through here: the long lists cap themselves first (prefix(visibleLimit)), and a terminal's scrollback or an editor's lines never take this path at all.

Sources/LavaUI/ForEach.swift:40

Constructors

init(_ data: Data, id: KeyPath<Data.Element, ID>, @ViewBuilder content: (Data.Element) -> Content)

Not documented.

Sources/LavaUI/ForEach.swift:48

init(_ data: Data, @ViewBuilder content: (Data.Element) -> Content)

Not documented.

Sources/LavaUI/ForEach.swift:82

Properties

var dumpDetail: String { get }

Not documented.

Sources/LavaUI/ForEach.swift:56

var rows: [(id: ID, content: Content)]

One built child per element, in order, with the key it is identified by.

Sources/LavaUI/ForEach.swift:46

Methods

func mountPrimitive() -> any AnyViewNode

Not documented.

Sources/LavaUI/ForEach.swift:66

func reconcilePrimitive(_ node: any AnyViewNode) -> any AnyViewNode

Not documented.

Sources/LavaUI/ForEach.swift:72

func structureLines(indent: Int = 0) -> [String]

Not documented.

Sources/LavaUI/ForEach.swift:58