Skip to content

fix(ios): emit every plugin event on the main thread - #932

Merged
riderx merged 1 commit into
Cap-go:mainfrom
OrLeib:fix/ios-notify-listeners-on-main
Sep 29, 2026
Merged

riderx merged 1 commit into
Cap-go:mainfrom
OrLeib:fix/ios-notify-listeners-on-main

Conversation

@OrLeib

@OrLeib OrLeib commented Sep 29, 2026 •

Copy link
Copy Markdown

What

  • Every event the iOS plugin emits now goes through notifyListenersOnMain, so it's delivered on the main thread. That covers appReady, updateAvailable, set, noNeedUpdate, updateFailed, appReloaded, updateCheckResult, breakingAvailable / majorAvailable and the download-failure events.
  • notifyListenersOnMain gains a retainUntilConsumed parameter (default false), so retained events keep their behavior.
  • CapgoUpdater.notifyListeners reuses the same helper instead of its own copy of the main-thread hop.

Why

How

  • Mechanical: replaced the remaining direct self.notifyListeners(...) calls with self.notifyListenersOnMain(...). Calls already on the main thread stay synchronous, so ordering and existing behavior on main are unchanged.

Testing

  • New testSendReadyToJsNotifiesAppReadyOnMainThread: calls sendReadyToJs and asserts appReady arrives on the main thread with retainUntilConsumed: true.
    • Without the fix: XCTAssertEqual failed: ("Optional(false)") is not equal to ("Optional(true)")
    • With the fix: passes.
  • Full iOS suite, 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).
  • SwiftLint 0.65.1 with @ionic/swiftlint-config: same warning count before and after on both changed files, none new.

Not Tested

  • Android is unchanged and not affected.
  • Didn't run bun run verify / bun run fmt (no Bun on this machine); ran xcodebuild tests and SwiftLint directly instead.
  • Not exercised in the example app or Maestro flows.

This PR was prepared with an AI agent (Claude Code); I reviewed the change and the test results.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved delivery of app-ready notifications, including when events are emitted outside the main thread.
    • App-ready notifications are now retained until consumed, helping ensure the application receives readiness updates.

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.
@github-actions

Copy link
Copy Markdown
Contributor

Beta npm build

This 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.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 210c3f26-131d-4bb2-a2e4-cf77e8193e03

📥 Commits

Reviewing files that changed from the base of the PR and between eec733a and 1b4c65b.

📒 Files selected for processing (2)
  • ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift
  • ios/Tests/CapacitorUpdaterPluginTests/CapacitorUpdaterPluginTests.swift
🔗 Linked repositories identified

CodeRabbit 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.


📝 Walkthrough

Walkthrough

The iOS plugin routes listener notifications through a main-thread helper. The helper forwards optional event data and retention settings. A test checks that appReady is notified on the main thread with retention enabled.

Changes

iOS listener dispatch

Layer / File(s) Summary
Shared listener notifications
ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift, ios/Tests/CapacitorUpdaterPluginTests/CapacitorUpdaterPluginTests.swift
The plugin routes listener events through a helper that forwards optional data and the retention flag. The test checks the main-thread and retention properties of appReady.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: riderx

Merge Risk: ⚪ Minimal · up to 1b4c6

No concrete merge-blocking issue was found in the reviewed iOS listener changes.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 1b4c6

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
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed exposure is the iOS plugin's listener delivery to application JavaScript. The reviewed changes do not show a new externally reachable operation or a change in listener privileges.

Trust Boundaries and Controls

  • observed — Changed emissions pass through a helper that calls the existing listener API on the main thread, rather than adding a new subscription or authorization path.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main iOS change: routing all plugin event emissions through the main thread.
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.
  • Fix all pre-merge checks with AI

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.

@sonarqubecloud

Copy link
Copy Markdown

@riderx
riderx merged commit cd66b01 into Cap-go:main Sep 29, 2026
8 checks passed
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.

2 participants