eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

πŸ€– Reproducing the iOS threading issues (#580)

Working notes for validating the fixes on this branch. The user-visible hang is intermittent because it needs a main-thread stall to coincide with a tap β€” so instead of chasing the rare hang, reproduce the mechanism signals (console warnings), which fire far more reliably, and treat "signal gone" as the pass condition.

The signals to watch (device console β€” Xcode debug console or Console.app)

| Signal | What it means | |---|---| | onChange(of: CGRect) action tried to update multiple times per frame | The globalFrame layout feedback loop in image/link/YouTube bubbles (MessageBubble.swift, YouTubeCard.swift, ChatView.swift). Main-thread layout churn. | | System gesture gate timed out | The system back/edge gesture waited on the feed's gesture stack (per-row swipe-to-reply + long-press + iOS-26 content-pop) and gave up. | | Got a keyboard will change frame / will hide notification, but keyboard was not even present | Spurious keyboard notifications β€” tied to the slow-to-focus symptom. |

Filter Console.app on the app process with text multiple times per frame or gesture gate.

Setup that maximizes the odds

  • A space with many chats (warming starts 5 live feed-model sockets).
  • Open a chat that has images and/or a link-preview or YouTube card on screen (those bubbles carry the globalFrame tracker that fuels the loop).
  • A second device / the web client generating churn into the space: flap presence (foreground/background), type, send messages.
  • Use the slowest device available β€” main-thread stalls are amplified there.

Per-symptom recipe

  1. Layout loop β€” open the media chat; raise/dismiss the keyboard and scroll, repeatedly. Expect the onChange(of: CGRect) warning to spam (near deterministic).
  2. Back delay β€” with churn active, open the media chat and immediately tap back; repeat ~20Γ—. Watch for the visible delay + System gesture gate.
  3. Phantom second copy (the clearest one) β€” tap a chat row; if nothing commits, tap it again a few times; when it finally opens, hit back. On the buggy build "back" lands on a duplicate copy of the same chat.
  4. Blank feed β€” from the list, tap the media chat β†’ immediately back β†’ immediately tap it again, ~10Γ— fast. Feed opens empty; a tiny scroll redraws.
  5. Slow focus β€” open the media chat, immediately tap the composer. Delay before the caret/keyboard; correlate with the CGRect warning.

Fixes on this branch

  • Phantom second copy (#580, symptom 6): navigation push is now idempotent β€” ChatNavGuard.shouldPush skips a push when that chat is already on top, so queued taps can't stack duplicate .thread(id) entries (ThreadListView.onSelect). Deterministic regression test: ChatNavGuardTests. This makes navigation robust to a stall; it does not remove the stall.
  • onChange(of: CGRect) layout churn (likely a root-cause contributor): the image / video / YouTube cards tracked their .global frame with a GeometryReader + onChange. That's a continuous observation of a value that changes on every layout pass β€” and a chat open runs several layout passes per displayed frame (scroll-settle pin, avatar spring, composer inset, keyboard), so the frame changes several times per frame and SwiftUI flags onChange(of: CGRect) action tried to update multiple times per frame. (Not a self-invalidation loop β€” moving the write off @State did NOT silence it, confirmed by breakpoint at MessageBubble image bubble: the trigger is observing a high-frequency value at all.) The frame is read only at tap time and only on macOS (the lightbox zooms out from it; iOS ignores it β€” every onImageTap consumer takes _), so the tracker is compiled out on iOS via trackingFrame(into:) and kept on macOS. Validate via the console signal β€” the warning should no longer appear while scrolling / animating the keyboard in a media chat on iOS (runtime behaviour the test suite can't cover).

Still to land (tracked, not yet on this branch): apply EdgeOnlyBackGesture to NativeChatScreen on iOS 26; coalesce presence frames / skip no-op row reconfigures; make the feed reveal robust to a missed opening pin (blank feed).