Skip to content

Refactor FXIOS-13122 [Swift 6] Remove @Sendable from closures marked as @MainActor#33

Open
tomerqodo wants to merge 3 commits intocoderabbit_full_base_refactor_fxios-13122_swift_6_remove_sendable_from_closures_marked_as_mainactor_pr7from
coderabbit_full_head_refactor_fxios-13122_swift_6_remove_sendable_from_closures_marked_as_mainactor_pr7
Open

Refactor FXIOS-13122 [Swift 6] Remove @Sendable from closures marked as @MainActor#33
tomerqodo wants to merge 3 commits intocoderabbit_full_base_refactor_fxios-13122_swift_6_remove_sendable_from_closures_marked_as_mainactor_pr7from
coderabbit_full_head_refactor_fxios-13122_swift_6_remove_sendable_from_closures_marked_as_mainactor_pr7

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 29, 2026

Benchmark PR from agentic-review-benchmarks#7

Summary by CodeRabbit

  • Refactor
    • Removed @Sendable constraints from completion handlers and callback closures throughout the codebase while preserving @MainActor annotations. This change relaxes concurrency requirements across dispatch queues, UI delegates, managers, and related utilities.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Walkthrough

This pull request systematically removes the @Sendable concurrency annotation from closure parameters and type aliases across BrowserKit and firefox-ios, while preserving @MainActor isolation. The changes relax concurrency constraints on completion handlers, action callbacks, and reducer functions throughout the codebase.

Changes

Cohort / File(s) Summary
Core Dispatch & Concurrency Infrastructure
BrowserKit/Sources/Common/Utilities/DispatchQueueHelper.swift, BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift, firefox-ios/Client/Utils/MainThreadThrottler.swift
Removed @Sendable from dispatch queue helper methods and throttler completion handlers; updated implementations to return values correctly in generic dispatch variants.
Redux State Management
BrowserKit/Sources/Redux/Reducer.swift
Removed @Sendable from Reducer typealias while keeping @MainActor isolation.
WebKit Integration Layer
BrowserKit/Sources/WebEngine/WKWebview/WKEngineWebView.swift, BrowserKit/Tests/WebEngineTests/Mock/MockWKEngineWebView.swift, firefox-ios/RustFxA/FxAWebViewController.swift
Removed @Sendable from evaluateJavaScript and navigation decision handler closures across protocol, implementations, and mocks.
BrowserViewController Delegate Extensions
firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift, firefox-ios/Client/Frontend/Browser/BrowserViewController/WebEngineIntegration/BrowserWebUIDelegate.swift
Removed @Sendable from WKUIDelegate and WKNavigationDelegate completion/decision handlers for JavaScript dialogs, context menu, media permissions, and authentication challenges; replaced some ensureMainThread wrapping with direct calls.
UI & Context Menu Actions
BrowserKit/Sources/MenuKit/MenuElement.swift, firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/WebContextMenuActionsProvider.swift
Removed @Sendable from action closure properties and method parameters for tab additions and image saving callbacks.
Authentication & Permission Handlers
firefox-ios/Client/Frontend/AuthenticationManager/AppAuthenticator.swift, firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift, firefox-ios/Client/Frontend/Settings/PasswordDetailViewController.swift
Removed @Sendable from completion closures in authentication state queries, device owner authentication, password login queries, and profile sync callbacks.
Search & Engine Management
firefox-ios/Client/Frontend/Browser/SearchEngines/SearchEngineProvider.swift, firefox-ios/Client/Frontend/Browser/SearchEngines/SearchEnginesManager.swift
Removed @Sendable from SearchEngineCompletion typealias and deleteCustomEngine completion closure.
Browser History & Data Queries
firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift, firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift, firefox-ios/Client/Frontend/Library/ClearHistorySheetProvider.swift
Removed @Sendable from search completion, pin/unpin action success handlers, and history deletion completion closures.
Tab & Queue Management
firefox-ios/Client/TabManagement/Tab.swift, firefox-ios/Storage/Queue.swift, firefox-ios/Storage/SQL/SQLiteQueue.swift, firefox-ios/Client/Frontend/Browser/TabScrollController/TabProviderAdapter.swift
Removed @Sendable from session cookie completion handlers, queued tabs callbacks, and tab state change listeners.
Download & Action Helpers
firefox-ios/Client/Frontend/Browser/DownloadHelper/DownloadHelper.swift, firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift, firefox-ios/Client/Frontend/Browser/RelayController.swift, firefox-ios/Client/AccountSyncHandler.swift
Removed @Sendable from download action, toolbar actions, relay completion, and debouncer action closures.
UI Components & View Models
firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift, firefox-ios/Client/Frontend/Home/Homepage/SectionHeader/LabelButtonHeaderView.swift, firefox-ios/Client/Frontend/Onboarding/Models/UpdateViewModel.swift, firefox-ios/Extensions/ShareTo/InitialViewController.swift
Removed @Sendable from tap handlers, custom render/height closures, button actions, and share completion callbacks.
WebServer & Platform Utilities
firefox-ios/Client/Application/WebServer.swift, BrowserKit/Sources/Shared/UIDeviceDetails.swift, firefox-ios/RustFxA/FirefoxAccountSignInViewController.swift
Removed @Sendable from handler and completion closures; also removed final modifier from WebServer class.
Test Mocks
firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Homepage/Mock/MockThrottler.swift, firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/Mock*.swift (AppAuthenticator, Profile, TabProvider, TabWebView)
Removed @Sendable from closure parameters in mock implementations to align with updated protocol signatures.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Sendable closures, now unbound,
Hop freely on the MainActor's ground,
No more constraints to hold them tight,
Main thread safety, still in sight! 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: removing @Sendable from closures marked @MainActor as part of Swift 6 refactoring, with issue reference FXIOS-13122.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
BrowserKit/Sources/MenuKit/MenuElement.swift (1)

7-20: Change closure type to @Sendable @mainactor () -> Void for proper Sendable conformance.

MenuElement conforms to Sendable but the action property is typed as (@mainactor () -> Void)?. In Swift 6, a @MainActor closure is not implicitly Sendable—it must be explicitly marked as @Sendable. Update the property and initializer parameter to use (@sendable @mainactor () -> Void)? to satisfy the Sendable protocol requirement.

Suggested change
// Line 20
public let action: (`@Sendable` `@MainActor` () -> Void)?

// Line 37 (initializer parameter)
action: (`@Sendable` `@MainActor` () -> Void)?
firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift (1)

1329-1351: Potential actor isolation issue: completionHandler called from background queue.

At Line 1343, completionHandler(.performDefaultHandling, nil) is called directly inside dispatchQueue.async { ... }, which runs on a background thread. The completionHandler is marked @MainActor, so calling it from a non-main-actor context may violate Swift 6's strict concurrency model.

Line 1348-1349 correctly wraps the call in ensureMainThread, but Line 1343 does not.

🔧 Proposed fix to ensure main actor isolation
             guard let trust = challenge.protectionSpace.serverTrust,
                   let cert = SecTrustCopyCertificateChain(trust) as? [SecCertificate],
                   self.profile.certStore.containsCertificate(cert[0], forOrigin: origin)
             else {
-                completionHandler(.performDefaultHandling, nil)
+                ensureMainThread {
+                    completionHandler(.performDefaultHandling, nil)
+                }
                 return
             }
firefox-ios/Client/Frontend/Browser/BrowserViewController/WebEngineIntegration/BrowserWebUIDelegate.swift (1)

38-50: Change @Sendable to @MainActor to match protocol definition.

Line 42 incorrectly uses @Sendable () -> Void for the completion handler, violating the WKUIHandler protocol definition which requires @MainActor () -> Void. This inconsistency with the confirm panel (line 56) and all other implementations in the codebase suggests an oversight.

Update line 42 from:

completionHandler: `@escaping` `@Sendable` () -> Void

to:

completionHandler: `@escaping` `@MainActor` () -> Void
🤖 Fix all issues with AI agents
In `@BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift`:
- Line 13: MockDispatchQueue’s ensureMainThread signature must match the
protocol DispatchQueueInterface: change the implementation in MockDispatchQueue
(the ensureMainThread method) to accept an escaping closure annotated with
`@MainActor` and `@convention`(block) and the same return type (Swift.Void). In
short, update MockDispatchQueue.ensureMainThread(execute:) to: func
ensureMainThread(execute work: `@escaping` `@MainActor` `@convention`(block) () ->
Swift.Void) so it conforms to the DispatchQueueInterface protocol.

In `@firefox-ios/Client/Utils/MainThreadThrottler.swift`:
- Around line 26-28: The throttle logic in MainThreadThrottler is wrong because
lastExecutionTime is set before the guard, making
lastExecutionTime.timeIntervalSinceNow near zero and causing the guard to always
return; move the assignment so you check the guard first (use threshold and
lastExecutionTime as-is), then when the guard passes set lastExecutionTime =
Date() and finally call DispatchQueue.main.async(execute: completion) so
completion actually runs.
🧹 Nitpick comments (1)
firefox-ios/Client/Application/WebServer.swift (1)

16-19: Consider keeping WebServer final while it remains @unchecked Sendable.

Removing final widens subclassing surface; subclasses could add non‑sendable or mutable state that undermines the unchecked sendability and singleton assumptions. If subclassing isn’t required, restoring final keeps the safety envelope tight.

🔧 Suggested tweak
-class WebServer: WebServerProtocol, `@unchecked` Sendable {
+final class WebServer: WebServerProtocol, `@unchecked` Sendable {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3259331 and 5183c27.

📒 Files selected for processing (40)
  • BrowserKit/Sources/Common/Utilities/DispatchQueueHelper.swift
  • BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift
  • BrowserKit/Sources/MenuKit/MenuElement.swift
  • BrowserKit/Sources/Redux/Reducer.swift
  • BrowserKit/Sources/Shared/UIDeviceDetails.swift
  • BrowserKit/Sources/WebEngine/WKWebview/WKEngineWebView.swift
  • BrowserKit/Tests/WebEngineTests/Mock/MockWKEngineWebView.swift
  • firefox-ios/Client/AccountSyncHandler.swift
  • firefox-ios/Client/Application/WebServer.swift
  • firefox-ios/Client/Frontend/AuthenticationManager/AppAuthenticator.swift
  • firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardInputViewModel.swift
  • firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/WebContextMenuActionsProvider.swift
  • firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift
  • firefox-ios/Client/Frontend/Browser/BrowserViewController/WebEngineIntegration/BrowserWebUIDelegate.swift
  • firefox-ios/Client/Frontend/Browser/DownloadHelper/DownloadHelper.swift
  • firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift
  • firefox-ios/Client/Frontend/Browser/RelayController.swift
  • firefox-ios/Client/Frontend/Browser/SearchEngines/SearchEngineProvider.swift
  • firefox-ios/Client/Frontend/Browser/SearchEngines/SearchEnginesManager.swift
  • firefox-ios/Client/Frontend/Browser/TabScrollController/TabProviderAdapter.swift
  • firefox-ios/Client/Frontend/Home/Homepage/SectionHeader/LabelButtonHeaderView.swift
  • firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift
  • firefox-ios/Client/Frontend/Library/ClearHistorySheetProvider.swift
  • firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift
  • firefox-ios/Client/Frontend/Onboarding/Models/UpdateViewModel.swift
  • firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift
  • firefox-ios/Client/Frontend/Settings/PasswordDetailViewController.swift
  • firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift
  • firefox-ios/Client/TabManagement/Tab.swift
  • firefox-ios/Client/Utils/MainThreadThrottler.swift
  • firefox-ios/Extensions/ShareTo/InitialViewController.swift
  • firefox-ios/RustFxA/FirefoxAccountSignInViewController.swift
  • firefox-ios/RustFxA/FxAWebViewController.swift
  • firefox-ios/Storage/Queue.swift
  • firefox-ios/Storage/SQL/SQLiteQueue.swift
  • firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Homepage/Mock/MockThrottler.swift
  • firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAppAuthenticator.swift
  • firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockProfile.swift
  • firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabProviderProtocol.swift
  • firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabWebView.swift
🧰 Additional context used
🧬 Code graph analysis (9)
firefox-ios/Storage/Queue.swift (2)
firefox-ios/Storage/SQL/SQLiteQueue.swift (1)
  • getQueuedTabs (24-34)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockProfile.swift (1)
  • getQueuedTabs (93-98)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Homepage/Mock/MockThrottler.swift (1)
firefox-ios/Client/Utils/MainThreadThrottler.swift (1)
  • throttle (25-29)
firefox-ios/Storage/SQL/SQLiteQueue.swift (1)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockProfile.swift (1)
  • getQueuedTabs (93-98)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabWebView.swift (1)
firefox-ios/Client/TabManagement/Tab.swift (1)
  • getSessionCookies (858-860)
firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift (1)
firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel+Search.swift (1)
  • performSearch (53-57)
BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift (2)
BrowserKit/Sources/Common/Utilities/DispatchQueueHelper.swift (2)
  • ensureMainThread (10-20)
  • ensureMainThread (22-33)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDispatchQueue.swift (1)
  • ensureMainThread (30-33)
BrowserKit/Sources/Common/Utilities/DispatchQueueHelper.swift (1)
BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift (2)
  • ensureMainThread (38-48)
  • async (24-29)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAppAuthenticator.swift (1)
firefox-ios/Client/Frontend/AuthenticationManager/AppAuthenticator.swift (1)
  • getAuthenticationState (29-46)
firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockProfile.swift (1)
firefox-ios/Storage/SQL/SQLiteQueue.swift (1)
  • getQueuedTabs (24-34)
🔇 Additional comments (61)
firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift (1)

118-122: Looks good — @MainActor isolation remains clear.

Removing @Sendable here aligns with the Swift 6 cleanup and keeps the callback constrained to the main actor.

firefox-ios/Client/AccountSyncHandler.swift (1)

19-19: Looks good: @MainActor isolation is preserved.

firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift (3)

125-127: Signature change looks consistent with @MainActor usage.


164-167: Completion handler isolation remains correct.


543-545: @mainactor closure type is appropriate here.

firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardInputViewModel.swift (1)

258-280: LGTM!

The removal of @Sendable while retaining @MainActor on the completion closure is correct for Swift 6 concurrency. The @MainActor annotation already guarantees main-thread isolation, and the implementation correctly uses ensureMainThread blocks when invoking the completion handler.

firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabProviderProtocol.swift (1)

15-15: LGTM!

The mock correctly mirrors the protocol change. Since the class is @MainActor isolated, removing @Sendable is appropriate—the closure is only set and invoked within the main actor context.

firefox-ios/Client/Frontend/Browser/TabScrollController/TabProviderAdapter.swift (2)

14-14: LGTM!

Removing @Sendable from the protocol property is correct. Since the protocol is @MainActor isolated, the closure is inherently constrained to the main actor context, making @Sendable redundant.


32-35: LGTM!

The implementation correctly reflects the protocol change. The closure type now matches the relaxed concurrency constraint while maintaining @MainActor isolation.

firefox-ios/Client/TabManagement/Tab.swift (2)

857-860: LGTM — @MainActor completion remains clear and aligned.

The change cleanly drops redundant @Sendable while preserving main‑actor isolation.


1210-1216: LGTM — updated override signature stays consistent with Swift 6 intent.

The completion handler remains @MainActor, and the removal of @Sendable is consistent with the refactor goal.

firefox-ios/Client/Frontend/Home/Homepage/SectionHeader/LabelButtonHeaderView.swift (1)

95-121: LGTM! Appropriate removal of redundant @Sendable constraint.

Removing @Sendable from this @MainActor-isolated UI callback closure is correct. Since moreButtonAction is a button tap handler that:

  1. Is already constrained to execute on the main actor
  2. Is configured from UI code that's inherently on the main thread
  3. Is passed to ActionButtonViewModel.touchUpAction, which already has the matching type (@mainactor (UIButton) -> Void)?

The @Sendable requirement was unnecessarily restrictive for callers, and the change is consistent with ActionButtonViewModel's parameter definition.

firefox-ios/Client/Utils/MainThreadThrottler.swift (1)

9-9: LGTM on @Sendable removal.

Removing @Sendable from @MainActor closures aligns with Swift 6 concurrency patterns—@MainActor already guarantees isolation on the main actor, making @Sendable redundant. The protocol and implementation signatures are now consistent with MockThrottler.

Also applies to: 25-25

firefox-ios/Client/Application/WebServer.swift (1)

55-62: LGTM — @MainActor handler without @Sendable is consistent.

This aligns with the goal of relaxing unnecessary sendability on main‑actor closures while keeping the handler isolation intact.

firefox-ios/RustFxA/FirefoxAccountSignInViewController.swift (1)

305-326: LGTM!

The removal of @Sendable from the @MainActor completion closure is correct. Since @MainActor already isolates execution to the main actor, the @Sendable attribute was redundant. The completion is properly invoked within the nested callback chain.

firefox-ios/Client/Frontend/Browser/SearchEngines/SearchEnginesManager.swift (1)

278-289: LGTM!

The removal of @Sendable is appropriate here. The SearchEnginesManager class is already @MainActor isolated, so the completion closure executes within the same actor context. No cross-boundary sendability is required.

firefox-ios/Client/Frontend/Library/ClearHistorySheetProvider.swift (4)

23-31: LGTM!

The removal of @Sendable from the didComplete closure is correct. The class is @MainActor isolated, and the closure is invoked from UIKit alert action handlers which run on the main thread.


52-59: LGTM!

Consistent with the pattern applied throughout this class.


61-84: LGTM!

The didComplete closure is correctly typed as @MainActor without @Sendable, matching the class isolation context.


86-105: LGTM!

Consistent signature change. The closure is invoked within the @MainActor context of the class.

BrowserKit/Sources/Shared/UIDeviceDetails.swift (1)

38-50: LGTM!

The removal of @Sendable from the work closure is appropriate. The function explicitly handles main-thread execution via DispatchQueue.main.sync or MainActor.assumeIsolated, and @MainActor sufficiently constrains the closure's execution context. The return type T: Sendable constraint correctly ensures the returned value can be safely transferred.

firefox-ios/Extensions/ShareTo/InitialViewController.swift (1)

98-109: LGTM!

The removal of @Sendable is correct. The completion closure is invoked within DispatchQueue.main.async, and the @MainActor annotation ensures proper main-thread execution without requiring cross-boundary sendability.

BrowserKit/Sources/Redux/Reducer.swift (1)

9-9: Removal of @Sendable from Reducer typealias is safe.

The Redux architecture uses reducers exclusively as static properties or local captures invoked within the Store's @MainActor context. No code passes Reducer instances across actor boundaries, and no explicit Sendable constraints exist in the codebase. The @MainActor annotation is sufficient to ensure proper concurrency isolation.

firefox-ios/Client/Frontend/Onboarding/Models/UpdateViewModel.swift (1)

76-81: LGTM!

The removal of @Sendable is appropriate here. The closure remains @MainActor-isolated and the implementation correctly invokes it via ensureMainThread, ensuring main thread execution.

firefox-ios/RustFxA/FxAWebViewController.swift (1)

158-179: LGTM!

The @Sendable removal from the decisionHandler is consistent with the PR objectives. The @preconcurrency import WebKit on line 5 appropriately handles any signature differences with Apple's SDK during the Swift 6 migration, allowing this simplified closure type while maintaining correct behavior.

firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Homepage/Mock/MockThrottler.swift (1)

13-18: LGTM!

The mock signature correctly matches the real implementation in MainThreadThrottler.swift (lines 24-28), ensuring consistent protocol conformance between production code and test doubles.

firefox-ios/Client/Frontend/Browser/DownloadHelper/DownloadHelper.swift (1)

63-65: LGTM!

The removal of @Sendable from the okAction parameter is appropriate. Since the method is already @MainActor-isolated and the closure is invoked within this context (line 108), the explicit @Sendable annotation was redundant.

firefox-ios/Client/Frontend/Browser/SearchEngines/SearchEngineProvider.swift (1)

8-21: LGTM!

The @Sendable removal from SearchEngineCompletion typealias is correct. Since this is used as a method parameter type (line 21), it doesn't affect the SearchEngineProvider protocol's Sendable conformance—that applies to conforming types' stored state, not method parameter closures.

firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift (2)

83-83: Signature update is consistent with @MainActor usage.

No behavioral impact beyond relaxing sendability constraints.


162-162: Signature update is consistent with @MainActor usage.

No behavioral impact beyond relaxing sendability constraints.

BrowserKit/Sources/WebEngine/WKWebview/WKEngineWebView.swift (2)

74-79: Protocol signature change looks good.

Keeps @MainActor isolation while relaxing sendability.


85-90: Extension forwarding signature looks good.

Matches protocol change without altering behavior.

BrowserKit/Tests/WebEngineTests/Mock/MockWKEngineWebView.swift (1)

136-140: Mock signature update aligns with new protocol.

Consistent with the production API change.

firefox-ios/Client/Frontend/AuthenticationManager/AppAuthenticator.swift (3)

22-25: Protocol signatures updated consistently.

No behavioral change; only sendability relaxation.


29-30: Implementation signature matches protocol.

Maintains @MainActor isolation.


48-50: Implementation signature matches protocol.

Maintains @MainActor isolation.

firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift (2)

44-57: Closure property types updated consistently.

No change in behavior; still main-actor bound.


74-76: Initializer parameter types updated consistently.

Matches the property types without functional changes.

firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockProfile.swift (1)

93-97: Mock signature update aligns with production API.

No logic change; test behavior preserved.

firefox-ios/Storage/SQL/SQLiteQueue.swift (1)

24-34: LGTM!

The removal of @Sendable is consistent with the TabQueue protocol definition and mock implementations. The @MainActor annotation already provides the necessary isolation guarantees, and the implementation correctly uses MainActor.assumeIsolated after dispatching to the main queue.

firefox-ios/Client/Frontend/Browser/RelayController.swift (1)

10-10: LGTM!

The removal of @Sendable from RelayPopulateCompletion is appropriate since the typealias is used within the @MainActor-isolated RelayController class and protocol methods. The main-actor isolation provides sufficient guarantees without the additional @Sendable requirement.

firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift (1)

130-158: LGTM!

The removal of @Sendable from performSearch's completion closure is correct. The @MainActor annotation ensures the closure runs on the main actor, making @Sendable redundant. The implementation properly uses MainActor.assumeIsolated after dispatching via .uponQueue(.main).

Note that reloadData (line 95) correctly retains @Sendable without @MainActor since its completion is called from a background queue.

firefox-ios/Storage/Queue.swift (1)

8-12: LGTM!

The protocol method signature change is consistent with the implementations in SQLiteQueue and MockProfile. Removing @Sendable while retaining @MainActor is the correct approach—the main-actor isolation provides the necessary thread-safety guarantees for the completion closure.

firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabWebView.swift (1)

112-114: LGTM!

The mock correctly matches the base class signature from Tab.getSessionCookies. The removal of @Sendable is consistent with the parent class change, and the @MainActor isolation is preserved.

firefox-ios/Client/Frontend/Settings/PasswordDetailViewController.swift (1)

402-417: LGTM!

The removal of @Sendable from the completion closure is appropriate. The @MainActor annotation ensures the closure executes on the main actor, which aligns with the DispatchQueue.main.async dispatch at line 405. Since this is an internal method, the change has no API compatibility impact.

BrowserKit/Sources/Common/Utilities/DispatchQueueHelper.swift (2)

10-20: LGTM!

The removal of @Sendable while preserving @MainActor @convention(block) is correct and aligns with the protocol definition in DispatchQueueInterface.swift.


22-32: Return type addition is not a breaking change.

The generic ensureMainThread<T> function signature change to return T? is not a breaking API change. All existing callers pass Void-returning closures and do not attempt to capture any return value, so the addition of the optional return type does not break their usage. This is an enhancement to support returning values when called from the main thread, but existing code patterns continue to work without modification.

Likely an incorrect or invalid review comment.

firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/WebContextMenuActionsProvider.swift (2)

31-31: LGTM!

The @Sendable removal from addTab closures is consistent. The class is already @MainActor isolated, so these closures don't need the additional @Sendable constraint.

Also applies to: 44-44


162-164: Consistent annotation update.

The writeToPhotoAlbum closure correctly retains @MainActor while removing @Sendable. Note that getImageData's inner callback on Line 163 correctly keeps @Sendable since it's invoked asynchronously from a different context.

firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAppAuthenticator.swift (1)

14-18: LGTM!

The mock's signatures correctly mirror the production AppAuthenticator API (as shown in the relevant snippet). The ensureMainThread wrapper properly ensures completion handlers execute on the main actor.

Also applies to: 24-34

firefox-ios/Client/Frontend/Browser/BrowserViewController/WebEngineIntegration/BrowserWebUIDelegate.swift (2)

52-80: LGTM!

The @Sendable removal from runJavaScriptConfirmPanelWithMessage and runJavaScriptTextInputPanelWithPrompt completion handlers is consistent with the PR objective.


86-116: LGTM!

The @Sendable removal from contextMenuConfigurationForElement and requestMediaCapturePermissionFor handlers aligns with the PR's goal of relaxing concurrency constraints while preserving @MainActor isolation.

firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift (9)

76-92: LGTM!

The handleJavaScriptAlert helper's spamCallback parameter correctly uses @MainActor without @Sendable.


94-140: LGTM!

All JavaScript panel delegate methods (runJavaScriptAlertPanelWithMessage, runJavaScriptConfirmPanelWithMessage, runJavaScriptTextInputPanelWithPrompt) consistently use @MainActor closures without @Sendable.


153-196: LGTM!

The contextMenuConfigurationForElement and requestMediaCapturePermissionFor delegate methods correctly adopt @MainActor closures.


200-204: LGTM!

The handleSpammedJSAlert helper's callback parameter correctly uses @MainActor.


313-319: LGTM!

The createActions method's addTab closure parameter aligns with the updated WebContextMenuActionsProvider signatures.


704-710: LGTM!

The navigation response policy decision handler correctly uses @MainActor.


931-933: LGTM!

The downloadAction closure correctly uses @MainActor for UI updates.


1052-1057: LGTM!

The authentication challenge completion handler correctly uses @MainActor.


1234-1235: LGTM!

The showExternalAlert completion handler correctly uses @MainActor.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

execute work: @escaping @Sendable @convention(block) () -> Void)

func ensureMainThread(execute work: @escaping @MainActor @Sendable @convention(block) () -> Swift.Void)
func ensureMainThread(execute work: @escaping @MainActor @convention(block) () -> Swift.Void)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find and examine DispatchQueueInterface.swift
find . -name "DispatchQueueInterface.swift" -type f

Repository: qodo-benchmark/firefox-ios-combined-coderabbit

Length of output: 156


🏁 Script executed:

# Find all mock dispatch queue implementations
find . -name "*Mock*Dispatch*" -o -name "*Dispatch*Mock*" | head -20

Repository: qodo-benchmark/firefox-ios-combined-coderabbit

Length of output: 325


🏁 Script executed:

# Search for ensureMainThread method signatures
rg -n "func ensureMainThread" --type swift -A 2

Repository: qodo-benchmark/firefox-ios-combined-coderabbit

Length of output: 1928


🏁 Script executed:

# Check if MockDispatchQueue conforms to DispatchQueueInterface
rg -A 5 "class MockDispatchQueue" firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDispatchQueue.swift

Repository: qodo-benchmark/firefox-ios-combined-coderabbit

Length of output: 299


🏁 Script executed:

# Check the full DispatchQueueInterface protocol
cat -n BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift

Repository: qodo-benchmark/firefox-ios-combined-coderabbit

Length of output: 2398


🏁 Script executed:

# Verify that the mock's signature is indeed incompatible by checking Swift conformance rules
# Get full context around the mock implementation
sed -n '25,40p' firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDispatchQueue.swift

Repository: qodo-benchmark/firefox-ios-combined-coderabbit

Length of output: 492


MockDispatchQueue must be updated to match the protocol signature.

The protocol (line 13) and extension (line 38) signatures are consistent. However, MockDispatchQueue does not properly conform to the updated protocol—its ensureMainThread implementation lacks the required @MainActor and @convention(block) attributes:

Protocol requires:     func ensureMainThread(execute work: `@escaping` `@MainActor` `@convention`(block) () -> Swift.Void)
Mock provides:         func ensureMainThread(execute work: `@escaping` () -> Void)

Update the mock implementation in firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDispatchQueue.swift (line 30) to include the missing attributes.

🤖 Prompt for AI Agents
In `@BrowserKit/Sources/Common/Utilities/DispatchQueueInterface.swift` at line 13,
MockDispatchQueue’s ensureMainThread signature must match the protocol
DispatchQueueInterface: change the implementation in MockDispatchQueue (the
ensureMainThread method) to accept an escaping closure annotated with `@MainActor`
and `@convention`(block) and the same return type (Swift.Void). In short, update
MockDispatchQueue.ensureMainThread(execute:) to: func ensureMainThread(execute
work: `@escaping` `@MainActor` `@convention`(block) () -> Swift.Void) so it conforms
to the DispatchQueueInterface protocol.

Comment on lines 26 to 28
lastExecutionTime = Date()
guard threshold <= 0 || lastExecutionTime.timeIntervalSinceNow < -threshold else { return }
DispatchQueue.main.async(execute: completion)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Throttle logic is broken—completion will never execute.

Setting lastExecutionTime = Date() before the guard makes lastExecutionTime.timeIntervalSinceNow effectively zero. The condition ~0 < -threshold is always false for any positive threshold (e.g., 0.35), so the guard always returns early and completion is never dispatched.

The assignment should occur after the guard passes.

🐛 Proposed fix
     func throttle(completion: `@escaping` `@MainActor` () -> Void) {
+        guard threshold <= 0 || lastExecutionTime.timeIntervalSinceNow < -threshold else { return }
         lastExecutionTime = Date()
-        guard threshold <= 0 || lastExecutionTime.timeIntervalSinceNow < -threshold else { return }
         DispatchQueue.main.async(execute: completion)
     }
🤖 Prompt for AI Agents
In `@firefox-ios/Client/Utils/MainThreadThrottler.swift` around lines 26 - 28, The
throttle logic in MainThreadThrottler is wrong because lastExecutionTime is set
before the guard, making lastExecutionTime.timeIntervalSinceNow near zero and
causing the guard to always return; move the assignment so you check the guard
first (use threshold and lastExecutionTime as-is), then when the guard passes
set lastExecutionTime = Date() and finally call
DispatchQueue.main.async(execute: completion) so completion actually runs.

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