fix(ios): capture finishAcceptInvoked by reference in DispatchWorkItem closure#7219
Conversation
…m closure Wrap finishAcceptInvoked in [false] array so DispatchWorkItem closure sees mutations. Prevents 10s timeout from firing finishAccept(false) after user already accepted call. Closes #6918
WalkthroughChanged the deduplication flag in Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ios/Libraries/VoipService.swift (1)
460-501:⚠️ Potential issue | 🟠 MajorArray capture semantics create invisible state divergence between closures
The
finishAcceptInvokedarray at line 460 is captured by value in thetimeoutWorkItemclosure (line 499) via the explicit capture list. WhenfinishAcceptmutatesfinishAcceptInvoked[0]at line 463, that mutation occurs on a different Array instance than the copy held by the timeout closure. Copy-on-write semantics ensure these independent copies diverge—the timeout guard at line 501 will not observe the mutation, defeating the dedupe logic.Proposed fix (use a reference wrapper for shared mutable state)
- var finishAcceptInvoked = [false] + final class FinishAcceptState { + var invoked = false + } + let finishAcceptState = FinishAcceptState() let finishAccept: (Bool) -> Void = { [weak payload] success in - guard !finishAcceptInvoked[0] else { return } - finishAcceptInvoked[0] = true + guard !finishAcceptState.invoked else { return } + finishAcceptState.invoked = true guard let payload else { return } stopDDPClientInternal(callId: payload.callId) if success { @@ - let timeoutWorkItem = DispatchWorkItem { [weak payload, finishAcceptInvoked] in + let timeoutWorkItem = DispatchWorkItem { [weak payload] in guard let payload else { return } - guard !finishAcceptInvoked[0] else { return } + guard !finishAcceptState.invoked else { return }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ios/Libraries/VoipService.swift` around lines 460 - 501, The dedupe flag currently uses an Array (finishAcceptInvoked) which gets copied into timeoutWorkItem's capture list causing divergent state; replace that shared mutable state with a reference type (e.g., a small class wrapper like FinishAcceptInvokedBox with a Bool property or an Atomic/ThreadSafeBool) and update references in finishAccept and timeoutWorkItem to read/write that single shared instance (e.g., finishAcceptInvoked.value) so both closures observe the same mutation; ensure the same reference is captured (no value-capture in the closure capture lists) and keep existing guard checks and mutation logic otherwise unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@ios/Libraries/VoipService.swift`:
- Around line 460-501: The dedupe flag currently uses an Array
(finishAcceptInvoked) which gets copied into timeoutWorkItem's capture list
causing divergent state; replace that shared mutable state with a reference type
(e.g., a small class wrapper like FinishAcceptInvokedBox with a Bool property or
an Atomic/ThreadSafeBool) and update references in finishAccept and
timeoutWorkItem to read/write that single shared instance (e.g.,
finishAcceptInvoked.value) so both closures observe the same mutation; ensure
the same reference is captured (no value-capture in the closure capture lists)
and keep existing guard checks and mutation logic otherwise unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1ca81cb6-d700-4511-a70f-17a2f5a48700
📒 Files selected for processing (1)
ios/Libraries/VoipService.swift
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
|
iOS Build Available Rocket.Chat Experimental 4.72.0.108608 |
Summary
Test plan
Summary by CodeRabbit