Skip to content

perf(ios/chat): skip re-rendering unchanged bubbles while typing#3454

Merged
BunsDev merged 3 commits into
mainfrom
cody/perf-chat-render
Jul 19, 2026
Merged

perf(ios/chat): skip re-rendering unchanged bubbles while typing#3454
BunsDev merged 3 commits into
mainfrom
cody/perf-chat-render

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 18, 2026

Copy link
Copy Markdown
Member

Summary

First PR in the iOS performance-optimization pass. Fixes slow/laggy typing in the chat composer.

Root cause

ChatView.body renders the composer's @State draft and the full message list (LazyVStack ForEach of MessageBubble) in the same view body. Each MessageBubble hosts a MarkdownWebView (WKWebView). Every keystroke mutated draft → invalidated ChatView.body → SwiftUI re-diffed every WebView-backed bubble → per-character typing lag that worsened with thread length.

Fix

  • MessageBubble: added Equatable conformance over only render-affecting value inputs (message, isGroup, familiar id, isLast, operator name/avatar, and presence of the optional action closures). Closures are excluded — they capture fresh state each render but don't change what's drawn; optional-action closures compared by presence since that toggles which buttons appear.
  • ChatView: added .equatable() to the bubble in the ForEach.

SwiftUI now short-circuits unchanged bubbles, so a keystroke no longer re-renders the transcript. Streaming still re-renders only the active bubble (its text changes each token → == returns false for that one).

Verification

  • xcodebuild build (scheme CovenCave, iOS Simulator) → BUILD SUCCEEDED, no new warnings/errors.
  • @State in the bubble (replyDrag/mdHeight/markdownFailed) is preserved across equality skips — identity is pinned by .id(message.id).

Blast radius

Small — 2 files, +23 lines, no behavior change beyond render skipping. Already shipped to TestFlight as build 2026071803 and validated.

Notes

CI (ci.yml) does not build the native Swift app; local xcodebuild is the iOS verification gate. Follow-up perf PRs (WebView reuse, streaming token coalescing, off-main thread persistence, async startup) will land separately.

ChatView.body renders the composer's $draft state and the full message
list in the same view body. Each MessageBubble hosts a MarkdownWebView
(WKWebView), so every keystroke invalidated ChatView.body and re-diffed
every WebView-backed bubble — producing per-character typing lag that
worsened with thread length.

Make MessageBubble Equatable over only its render-affecting value inputs
(message, isGroup, familiar id, isLast, operator name/avatar, and the
presence of optional action closures) and mark it .equatable() in the
ForEach. SwiftUI now short-circuits unchanged bubbles, so a keystroke no
longer re-renders the transcript. Streaming still re-renders only the
active bubble (its text changes each token).

Verified: xcodebuild build (scheme CovenCave, iOS Simulator) succeeds.
Copilot AI review requested due to automatic review settings July 18, 2026 22:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets iOS chat typing latency by preventing SwiftUI from re-diffing/re-rendering unchanged MessageBubble views (each hosting a WKWebView) on every composer keystroke, using Equatable + .equatable().

Changes:

  • Added Equatable conformance to MessageBubble using a subset of inputs intended to reflect render-affecting state.
  • Applied .equatable() to MessageBubble instances in ChatView’s ForEach message list to enable SwiftUI short-circuiting.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
apps/ios/CovenCave/CovenCave/Views/MessageBubble.swift Adds Equatable conformance to allow skipping updates for unchanged bubbles.
apps/ios/CovenCave/CovenCave/Views/ChatView.swift Wraps each MessageBubble with .equatable() inside the message list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/ios/CovenCave/CovenCave/Views/MessageBubble.swift
@BunsDev
BunsDev merged commit 077d142 into main Jul 19, 2026
15 checks passed
@BunsDev
BunsDev deleted the cody/perf-chat-render branch July 19, 2026 14:25
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.

3 participants