Skip to content

fix(ui): keep the home transaction list responsive during sync - #792

Merged
QuantumExplorer merged 1 commit into
swift-sdk-integrationfrom
claude/transaction-scroll-responsiveness-50f03a
Jul 9, 2026
Merged

fix(ui): keep the home transaction list responsive during sync#792
QuantumExplorer merged 1 commit into
swift-sdk-integrationfrom
claude/transaction-scroll-responsiveness-50f03a

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 9, 2026

Copy link
Copy Markdown
Member

Problem

Scrolling the home transaction list was janky, especially while the wallet was syncing, and (secondarily) the list rendered empty for ~0.7s at startup before rows popped in.

Causes

  1. Unthrottled reload storm. Every SwiftData save — one per Core SPV / BLAST batch — plus every DSWalletBalanceDidChange fired an immediate full reload of the tx list; during sync that's several full reloads per second. The save notification was also delivered via RunLoop.main (default runloop mode only), so saves arriving mid-drag queued up and burst right as the finger lifted, stalling the hand-off frame.
  2. Each reload blocked the main thread. SwiftDashSDKWalletSource fetched and wrapped all transactions through DispatchQueue.main.sync on mainContext, including a per-tx SwiftData relationship walk (outputs → coreAddress → account) for CoinJoin classification — far beyond a frame budget, landing repeatedly mid-scroll.
  3. Per-row dictionary copies. resolveMetadata copied each metadata provider's entire dictionary through its serial queue per transaction per reload (O(n) queue hops + copies).

Fixes

  • All wallet-change triggers funnel into one PassthroughSubject throttled to one reload per second (latest: true guarantees the trailing batch lands; a lone event while idle passes through immediately).
  • SwiftDashSDKWalletSource now reads only the @MainActor host handles (modelContainer + walletId) in a brief main hop, then runs the fetch + wrap on the caller's thread with a private ModelContext — reloads never block the main thread. Safe because Transaction snapshots every UI-read field at wrap time, and a private context reads the last saved state — exactly what the did-save trigger guarantees exists. Stale "main-bound" doc comments updated to match.
  • Provider metadata is snapshotted once per reload (gift-card filter included).
  • Startup: the first load was only scheduled behind the 0.5s sync-state debounce; init now kicks it off immediately.

Verification

  • Clean dashpay scheme build (arm64 simulator, per DASHSYNC_MIGRATION verification standard).
  • Testnet smoke on simulator with an existing wallet: home list scrolls smoothly during active sync; transactions appear promptly on cold start; list still updates live (new tx while idle appears immediately).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved wallet refresh behavior to reduce repeated reloads during sync and save activity.
    • Fixed the initial wallet view so transactions and shortcuts appear immediately at startup.
    • Smoothed transaction updates when balances or fiat currency settings change.
  • Performance

    • Optimized transaction loading and metadata handling for faster, more efficient refreshes.
    • Reduced unnecessary work during transaction row rendering.

Scrolling the home transaction list stuttered whenever the wallet was
syncing, and the list rendered empty for ~0.7s at startup. Three causes,
three fixes:

- Every SwiftData save (one per SPV/BLAST batch) plus each balance
  notification triggered an immediate full reload. All wallet-change
  triggers now funnel into one PassthroughSubject throttled to a reload
  per second (latest: true, so the trailing batch always lands; a lone
  event while idle still passes through immediately). The save
  notification also previously arrived via RunLoop.main, which runs in
  the default runloop mode only — saves during a drag queued up and
  burst right as the finger lifted.

- Each reload fetched and wrapped ALL transactions on the main thread
  (DispatchQueue.main.sync + mainContext), including a per-tx SwiftData
  relationship walk for CoinJoin classification — far beyond a frame
  budget mid-scroll. SwiftDashSDKWalletSource now grabs only the
  @mainactor host handles in a brief main hop and runs the fetch + wrap
  on the caller's thread with a private ModelContext. Safe because
  Transaction snapshots every field at wrap time, and a private context
  reads the last saved state — exactly what the did-save trigger
  guarantees exists.

- resolveMetadata copied every provider's whole metadata dictionary
  through its serial queue per transaction per reload; the reload loop
  now snapshots each provider once (gift-card filter included).

Startup pop-in: the first load was only scheduled behind the 0.5s
sync-state debounce; init now kicks it off immediately (nothing to
coalesce yet, and the fetch is off-main anyway).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be18b9f7-fd97-4759-be9e-1dea2e85e512

📥 Commits

Reviewing files that changed from the base of the PR and between 5781422 and 580ef1b.

📒 Files selected for processing (3)
  • DashWallet/Sources/Models/CrowdNode/Services/TransactionObserver.swift
  • DashWallet/Sources/Models/Transactions/Model/Transaction.swift
  • DashWallet/Sources/UI/Home/Views/HomeViewModel.swift

📝 Walkthrough

Walkthrough

Changes

Wallet data flow

Layer / File(s) Summary
Background transaction fetching
DashWallet/Sources/Models/CrowdNode/Services/TransactionObserver.swift, DashWallet/Sources/Models/Transactions/Model/Transaction.swift, DashWallet/Sources/UI/Home/Views/HomeViewModel.swift
SwiftData reads, transaction wrapping, and CoinJoin detection move to caller-owned fetch contexts, with threading documentation updated accordingly.
Throttled wallet reload orchestration
DashWallet/Sources/UI/Home/Views/HomeViewModel.swift
Initialization and wallet-change notifications send reload requests through a throttled subject while preserving trailing updates.
Snapshot-based transaction metadata
DashWallet/Sources/UI/Home/Views/HomeViewModel.swift
Metadata and gift-card identifiers are snapshotted once per reload and reused during filtering and transaction row creation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WalletNotification
  participant HomeViewModel
  participant SwiftDashSDKWalletSource
  participant ModelContext
  WalletNotification->>HomeViewModel: enqueue wallet reload request
  HomeViewModel->>HomeViewModel: throttle latest request
  HomeViewModel->>SwiftDashSDKWalletSource: reload transaction data
  SwiftDashSDKWalletSource->>ModelContext: fetch and wrap transactions
  ModelContext-->>SwiftDashSDKWalletSource: return persistent transactions
  SwiftDashSDKWalletSource-->>HomeViewModel: return wrapped transaction rows
Loading

Possibly related PRs

Suggested reviewers: HashEngineering

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: improving home transaction list responsiveness during sync.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/transaction-scroll-responsiveness-50f03a

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@QuantumExplorer
QuantumExplorer changed the base branch from master to swift-sdk-integration July 9, 2026 21:43
@QuantumExplorer QuantumExplorer changed the title fix(home): keep the tx list responsive during sync fix(ui): keep the home transaction list responsive during sync Jul 9, 2026
@QuantumExplorer

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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