Summary
Currently, timeline updates flow through two layers of diffing:
LiveTimeline.updateTimeline(diff:) receives granular TimelineDiff operations from the SDK (set, append, insert, remove, etc.) and applies them to the timelineItems array
TimelineTableView.updateTimelineItems() receives the full array, compares old vs new IDs, and decides whether to rebuild the NSDiffableDataSourceSnapshot or reload rows in place
This means we discard the SDK diff information and then try to reconstruct it by comparing arrays — re-deriving what we already knew.
Proposed change
Pass the TimelineDiff operations through to TimelineTableView and map them directly to snapshot operations:
| SDK Diff |
Snapshot Operation |
set(index, item) |
snapshot.reloadItems([id]) |
append(values) |
Insert at beginning of snapshot (items are reversed) |
pushBack(item) |
Insert at beginning |
pushFront(item) |
Append to end |
insert(index, item) |
snapshot.insertItems with reversed index |
remove(index) |
snapshot.deleteItems([id]) |
popFront / popBack |
Delete last/first item in snapshot |
truncate(length) |
Delete items beyond reversed length |
reset / clear |
Full snapshot rebuild |
Benefits
- Correctness:
set operations (content-only updates like reactions, read receipts) would reload the exact affected row via snapshot.reloadItems, regardless of visibility — no risk of stale cached views
- Performance: No ID array comparison on every update
- Simplicity: Removes the "are IDs the same?" heuristic
Considerations
- Index reversal:
BottomStickyTableView stores items reversed, so SDK indices need transforming (SDK index i in a list of n → table index n - 1 - i)
- Throttling: Diffs are already throttled and batched in
listenToTimelineChanges() — batched diffs can be applied sequentially
- Other consumers:
timelineItems array on LiveTimeline should remain for other consumers (read receipt marking, reply detail loading, focusEvent)
- The
TimelineViewController could subscribe to diffs directly from LiveTimeline (it already holds a reference) rather than going through the SwiftUI representable update cycle
Context
Discussed in #80 — see review comment by @viktorstrate.
Summary
Currently, timeline updates flow through two layers of diffing:
LiveTimeline.updateTimeline(diff:)receives granularTimelineDiffoperations from the SDK (set,append,insert,remove, etc.) and applies them to thetimelineItemsarrayTimelineTableView.updateTimelineItems()receives the full array, compares old vs new IDs, and decides whether to rebuild theNSDiffableDataSourceSnapshotor reload rows in placeThis means we discard the SDK diff information and then try to reconstruct it by comparing arrays — re-deriving what we already knew.
Proposed change
Pass the
TimelineDiffoperations through toTimelineTableViewand map them directly to snapshot operations:set(index, item)snapshot.reloadItems([id])append(values)pushBack(item)pushFront(item)insert(index, item)snapshot.insertItemswith reversed indexremove(index)snapshot.deleteItems([id])popFront/popBacktruncate(length)reset/clearBenefits
setoperations (content-only updates like reactions, read receipts) would reload the exact affected row viasnapshot.reloadItems, regardless of visibility — no risk of stale cached viewsConsiderations
BottomStickyTableViewstores items reversed, so SDK indices need transforming (SDK indexiin a list ofn→ table indexn - 1 - i)listenToTimelineChanges()— batched diffs can be applied sequentiallytimelineItemsarray onLiveTimelineshould remain for other consumers (read receipt marking, reply detail loading,focusEvent)TimelineViewControllercould subscribe to diffs directly fromLiveTimeline(it already holds a reference) rather than going through the SwiftUI representable update cycleContext
Discussed in #80 — see review comment by @viktorstrate.