Apple renderer: a virtualising container, so LazyColumn stops mounting every row #5

Open
opened 2026-09-28 21:57:05 -07:00 by rleibman · 0 comments
Owner

docs/12 records this as the worst of the current gaps, because it works in the demo and dies in an app: AppleRenderer does not override supportsVirtualRows, so it inherits false and LazyColumn falls back to mounting every row. On the 10 000-row screen that is 30 000 widgets on the main thread of a phone.

GTK and Android both implement it and the contract needed no changes for either, so RowSource[Handle] is already proven as an abstraction rather than a description of Android.

What to do

  1. override def supportsVirtualRows: Boolean = true.
  2. createVirtualList(source: RowSource[Handle]): NSTableView (view-based, one column, makeView(withIdentifier:) for recycling) on AppKit; UITableView with dequeueReusableCell on UIKit.
  3. The shim needs the same inverted-control shape GTK's GtkSignalListItemFactory bind callback has: a trampoline that calls back into Scala with the row index and the recycled handle, and RowSource.onInvalidate wired to reloadData.
  4. RowSource.bind returns the row's root handle. Rows must have a single root widget — a Fragment row is rejected with a message saying so, exactly as the other two renderers do; do not mount something the table cannot place.

How to verify. TodoApp.Route.Big is the 10 000-row screen and the shared self-test already pushes it. Add the check the other two have: after pushing, count materialised rows and assert it is bounded. Android reports ~66, GTK ~205. Record the Apple number in the report — a number, not "works".

See docs/12-component-status.md §12.3 and §12.5.

`docs/12` records this as the worst of the current gaps, because it works in the demo and dies in an app: `AppleRenderer` does not override `supportsVirtualRows`, so it inherits `false` and `LazyColumn` falls back to mounting **every** row. On the 10 000-row screen that is 30 000 widgets on the main thread of a phone. GTK and Android both implement it and the contract needed no changes for either, so `RowSource[Handle]` is already proven as an abstraction rather than a description of Android. **What to do** 1. `override def supportsVirtualRows: Boolean = true`. 2. `createVirtualList(source: RowSource[Handle])`: `NSTableView` (view-based, one column, `makeView(withIdentifier:)` for recycling) on AppKit; `UITableView` with `dequeueReusableCell` on UIKit. 3. The shim needs the same inverted-control shape GTK's `GtkSignalListItemFactory` bind callback has: a trampoline that calls back into Scala with the row index and the recycled handle, and `RowSource.onInvalidate` wired to `reloadData`. 4. `RowSource.bind` returns the row's root handle. **Rows must have a single root widget** — a `Fragment` row is rejected with a message saying so, exactly as the other two renderers do; do not mount something the table cannot place. **How to verify.** `TodoApp.Route.Big` is the 10 000-row screen and the shared self-test already pushes it. Add the check the other two have: after pushing, count materialised rows and assert it is bounded. Android reports ~66, GTK ~205. Record the Apple number in the report — a number, not "works". See `docs/12-component-status.md` §12.3 and §12.5.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rleibman/scala-ui#5
No description provided.