Repository navigation
fix(ios): emit every plugin event on the main thread - #932
Conversation
CAPPlugin's listener storage is not thread-safe (ionic-team/capacitor#8157). appReady and updateAvailable were still emitted from background queues, which can crash in -[CAPPlugin notifyListeners:data:retainUntilConsumed:] when JS adds or removes a listener at the same time. Route every event through notifyListenersOnMain, which now forwards retainUntilConsumed, and reuse it for CapgoUpdater's notifyListeners bridge.
Beta npm buildThis PR comes from a fork, so beta publish is disabled for security. If you need a beta package, move the branch into this repository first. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (2)
🔗 Linked repositories identifiedCodeRabbit considers these linked repositories for cross-repo context during reviews:
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe iOS plugin routes listener notifications through a main-thread helper. The helper forwards optional event data and retention settings. A test checks that ChangesiOS listener dispatch
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to No concrete merge-blocking issue was found in the reviewed iOS listener changes. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change is confined to iOS event delivery and preserves event payloads and retention settings. Background events may arrive later than before, but the review found no newly introduced security weakness. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
|



What
notifyListenersOnMain, so it's delivered on the main thread. That coversappReady,updateAvailable,set,noNeedUpdate,updateFailed,appReloaded,updateCheckResult,breakingAvailable/majorAvailableand the download-failure events.notifyListenersOnMaingains aretainUntilConsumedparameter (defaultfalse), so retained events keep their behavior.CapgoUpdater.notifyListenersreuses the same helper instead of its own copy of the main-thread hop.Why
CAPPluginstores listeners in an unsynchronizedNSMutableDictionary([Bug]: CAPPlugin Event Handling Causing Data Races on iOS ionic-team/capacitor#8157).addListener/removeListenerrun on the plugin's queue, so notifying from another thread can crash in-[__NSDictionaryM objectForKey:]inside-[CAPPlugin notifyListeners:data:retainUntilConsumed:].notifyListenersOnMain, butappReadyis still emitted fromDispatchQueue.global()insendReadyToJs, andupdateAvailablefrom background download work. We shipped a patch for exactly these calls in our app after seeing this crash in production.How
self.notifyListeners(...)calls withself.notifyListenersOnMain(...). Calls already on the main thread stay synchronous, so ordering and existing behavior on main are unchanged.Testing
testSendReadyToJsNotifiesAppReadyOnMainThread: callssendReadyToJsand assertsappReadyarrives on the main thread withretainUntilConsumed: true.XCTAssertEqual failed: ("Optional(false)") is not equal to ("Optional(true)")xcodebuild test -scheme CapgoCapacitorUpdater -destination 'platform=iOS Simulator,name=iPhone 17'(iOS 26.5, Xcode 26.6): 202 tests, 0 failures (main: 201, 0 failures).@ionic/swiftlint-config: same warning count before and after on both changed files, none new.Not Tested
bun run verify/bun run fmt(no Bun on this machine); ranxcodebuildtests and SwiftLint directly instead.This PR was prepared with an AI agent (Claude Code); I reviewed the change and the test results.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by CodeRabbit