Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions firefox-ios/Client/Frontend/Browser/SearchLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class SearchLoader: Loader<Cursor<Site>, SearchViewModel>, FeatureFlaggabl
fileprivate func getBookmarksAsSites(
matchingSearchQuery query: String,
limit: UInt,
completionHandler: @escaping (([Site]) -> Void)
completionHandler: @escaping @Sendable (([Site]) -> Void)
) {
profile.places.searchBookmarks(query: query, limit: limit).upon { result in
guard let bookmarkItems = result.successValue else {
Expand All @@ -49,7 +49,7 @@ final class SearchLoader: Loader<Cursor<Site>, SearchViewModel>, FeatureFlaggabl
private func getHistoryAsSites(
matchingSearchQuery query: String,
limit: Int,
completionHandler: @escaping (([Site]) -> Void)
completionHandler: @escaping @Sendable (([Site]) -> Void)
) {
profile.places.interruptReader()
profile.places.queryAutocomplete(matchingSearchQuery: query, limit: limit).upon { result in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,16 +668,19 @@ final class BookmarksViewController: SiteTableViewController,

extension BookmarksViewController: LibraryPanelContextMenu {
func presentContextMenu(for indexPath: IndexPath) {
viewModel.getSiteDetails(for: indexPath) { [weak self] site in
guard let self else { return }
if let site {
presentContextMenu(for: site, with: indexPath, completionHandler: {
return self.contextMenu(for: site, with: indexPath)
})
} else if let bookmarkNode = viewModel.bookmarkNodes[safe: indexPath.row],
bookmarkNode.type == .folder,
isCurrentFolderEditable(at: indexPath) {
presentContextMenu(for: bookmarkNode, indexPath: indexPath)
viewModel.getSiteDetails(for: indexPath) { site in
ensureMainThread { [weak self] in
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By marking some closures as @Sendable, the call sites had errors because the compiler was able to detect that we were (potentially) hopping threads, so we add ensureMainThread here for safety. 👀

[weak self] gets moved a level lower so we don't pass the non-Sendable self from the closure thread to the main thread.

guard let self else { return }

if let site {
self.presentContextMenu(for: site, with: indexPath, completionHandler: {
return self.contextMenu(for: site, with: indexPath)
})
} else if let bookmarkNode = self.viewModel.bookmarkNodes[safe: indexPath.row],
bookmarkNode.type == .folder,
self.isCurrentFolderEditable(at: indexPath) {
self.presentContextMenu(for: bookmarkNode, indexPath: indexPath)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class BookmarksPanelViewModel {
return true
}

func reloadData(completion: @escaping () -> Void) {
func reloadData(completion: @escaping @Sendable () -> Void) {
// Can be called while app backgrounded and the db closed, don't try to reload the data source in this case
if profile.isShutdown {
completion()
Expand Down Expand Up @@ -97,7 +97,7 @@ final class BookmarksPanelViewModel {
bookmarkNodes.insert(bookmarkNode, at: destinationIndexPath.row)
}

func getSiteDetails(for indexPath: IndexPath, completion: @escaping (Site?) -> Void) {
func getSiteDetails(for indexPath: IndexPath, completion: @escaping @Sendable (Site?) -> Void) {
guard let bookmarkNode = bookmarkNodes[safe: indexPath.row],
let bookmarkItem = bookmarkNode as? BookmarkItemData
else {
Expand All @@ -117,7 +117,7 @@ final class BookmarksPanelViewModel {
func createPinUnpinAction(
for site: Site,
isPinned: Bool,
successHandler: @escaping (String) -> Void
successHandler: @MainActor @escaping @Sendable (String) -> Void
) -> PhotonRowActions {
return SingleActionViewModel(
title: isPinned ? .Bookmarks.Menu.RemoveFromShortcutsTitle : .AddToShortcutsActionTitle,
Expand All @@ -129,14 +129,16 @@ final class BookmarksPanelViewModel {
: profile.pinnedSites.addPinnedTopSite(site)

action.uponQueue(.main) { result in
if result.isSuccess {
let message: String = isPinned
? .LegacyAppMenu.RemovePinFromShortcutsConfirmMessage
: .LegacyAppMenu.AddPinToShortcutsConfirmMessage
successHandler(message)
} else {
let logMessage = isPinned ? "Could not remove pinned site" : "Could not add pinne site"
logger.log(logMessage, level: .debug, category: .library)
MainActor.assumeIsolated {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Safe here since this is executed upon the .main queue above.

if result.isSuccess {
let message: String = isPinned
? .LegacyAppMenu.RemovePinFromShortcutsConfirmMessage
: .LegacyAppMenu.AddPinToShortcutsConfirmMessage
successHandler(message)
} else {
let logMessage = isPinned ? "Could not remove pinned site" : "Could not add pinne site"
logger.log(logMessage, level: .debug, category: .library)
}
}
}
}
Expand All @@ -157,7 +159,7 @@ final class BookmarksPanelViewModel {
return max(index - LocalDesktopFolder.numberOfRowsTaken, 0)
}

private func setupMobileFolderData(completion: @escaping () -> Void) {
private func setupMobileFolderData(completion: @escaping @Sendable () -> Void) {
bookmarksHandler
.getBookmarksTree(rootGUID: BookmarkRoots.MobileFolderGUID, recursive: false)
.uponQueue(.main) { result in
Expand Down Expand Up @@ -190,7 +192,7 @@ final class BookmarksPanelViewModel {
}

/// Subfolder data case happens when we select a folder created by a user
private func setupSubfolderData(completion: @escaping () -> Void) {
private func setupSubfolderData(completion: @escaping @Sendable () -> Void) {
bookmarksHandler.getBookmarksTree(rootGUID: bookmarkFolderGUID,
recursive: false).uponQueue(.main) { result in
guard let folder = result.successValue as? BookmarkFolderData else {
Expand All @@ -217,7 +219,7 @@ final class BookmarksPanelViewModel {

// Create a local "Desktop bookmarks" folder only if there exists a bookmark in one of it's nested
// subfolders
private func createDesktopBookmarksFolder(completion: @escaping () -> Void) {
private func createDesktopBookmarksFolder(completion: @escaping @Sendable () -> Void) {
bookmarksHandler.countBookmarksInTrees(folderGuids: BookmarkRoots.DesktopRoots.map { $0 }) { result in
switch result {
case .success(let bookmarkCount):
Expand All @@ -237,7 +239,11 @@ final class BookmarksPanelViewModel {
}
}

private func checkIfPinnedURL(_ url: String, queue: DispatchQueue = .main, completion: @escaping (Bool) -> Void ) {
private func checkIfPinnedURL(
_ url: String,
queue: DispatchQueue = .main,
completion: @escaping @Sendable (Bool) -> Void
) {
profile.pinnedSites.isPinnedTopSite(url)
.uponQueue(queue) { result in
completion(result.successValue ?? false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class UpdateViewModel: OnboardingViewModelProtocol,

// Function added to wait for AccountManager initialization to get
// if the user is Sign in with Sync Account to decide which cards to show
func hasSyncableAccount(completion: @escaping () -> Void) {
func hasSyncableAccount(completion: @MainActor @escaping @Sendable () -> Void) {
hasSyncableAccount = profile.hasAccount()
ensureMainThread {
completion()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ protocol BreachAlertsClientProtocol {
func fetchEtag(
endpoint: BreachAlertsClient.Endpoint,
profile: Profile,
completion: @escaping (_ etag: String?) -> Void
completion: @escaping @Sendable (_ etag: String?) -> Void
)
func fetchData(
endpoint: BreachAlertsClient.Endpoint,
profile: Profile,
completion: @escaping (_ result: Maybe<Data>) -> Void
completion: @escaping @Sendable (_ result: Maybe<Data>) -> Void
)
}

Expand All @@ -33,7 +33,7 @@ class BreachAlertsClient: BreachAlertsClientProtocol {
static let etagDateKey = "BreachAlertsDataDate"

/// Makes a header-only request to an endpoint and hands off the endpoint's etag to a completion handler.
func fetchEtag(endpoint: Endpoint, profile: Profile, completion: @escaping (_ etag: String?) -> Void) {
func fetchEtag(endpoint: Endpoint, profile: Profile, completion: @escaping @Sendable (_ etag: String?) -> Void) {
guard let url = URL(string: endpoint.rawValue) else { return }
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
Expand All @@ -58,7 +58,7 @@ class BreachAlertsClient: BreachAlertsClientProtocol {
}

/// Makes a network request to an endpoint and hands off the result to a completion handler.
func fetchData(endpoint: Endpoint, profile: Profile, completion: @escaping (_ result: Maybe<Data>) -> Void) {
func fetchData(endpoint: Endpoint, profile: Profile, completion: @escaping @Sendable (_ result: Maybe<Data>) -> Void) {
guard let url = URL(string: endpoint.rawValue) else { return }

dataTask?.cancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ final class PasswordManagerViewModel {

/// Searches SQLite database for logins that match query.
/// Wraps the SQLiteLogins method to allow us to cancel it from our end.
func queryLogins(_ query: String, completion: @escaping ([Login]) -> Void) {
func queryLogins(_ query: String, completion: @escaping @Sendable ([Login]) -> Void) {
loginProvider.searchLoginsWithQuery(query) { result in
ensureMainThread {
switch result {
Expand Down Expand Up @@ -155,7 +155,7 @@ final class PasswordManagerViewModel {
}
}

public func save(loginRecord: LoginEntry, completion: @escaping ((String?) -> Void)) {
public func save(loginRecord: LoginEntry, completion: @escaping @Sendable ((String?) -> Void)) {
loginProvider.addLogin(login: loginRecord, completionHandler: { result in
switch result {
case .success(let encryptedLogin):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ extension PasswordDetailViewController {
}
}

func onProfileDidFinishSyncing(completion: @escaping () -> Void) {
func onProfileDidFinishSyncing(completion: @escaping @Sendable () -> Void) {
// Reload details after syncing.
viewModel.profile.logins.getLogin(id: viewModel.login.id) { [weak self] result in
DispatchQueue.main.async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ final class DefaultTemporaryDocument: NSObject,
return tempFileURL
}

func download(_ completion: @escaping (URL?) -> Void) {
func download(_ completion: @escaping @Sendable (URL?) -> Void) {
if let tempFile = queryTempFile() {
ensureMainThread {
completion(tempFile)
Expand Down
7 changes: 4 additions & 3 deletions firefox-ios/Providers/RustSyncManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public class RustSyncManager: NSObject, SyncManager {
public let description = "Failed to get token server endpoint url."
}

func shouldSyncLogins(completion: @escaping (Bool) -> Void) {
func shouldSyncLogins(completion: @escaping @Sendable (Bool) -> Void) {
if !(self.prefs.boolForKey(PrefsKeys.LoginsHaveBeenVerified) ?? false) {
// We should only sync logins when the verification step has completed successfully.
// Otherwise logins could exist in the database that can't be decrypted and would
Expand All @@ -378,7 +378,8 @@ public class RustSyncManager: NSObject, SyncManager {
creditCardKey: String?,
completion: @escaping @Sendable (([String], [String: String])) -> Void) {
var localEncryptionKeys: [String: String] = [:]
var rustEngines: [String] = []
// FIXME: FXIOS-14052 Unprotected properties should not be mutated on background threads
nonisolated(unsafe) var rustEngines: [String] = []
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is suppressing an error for how this var gets (mis)used below.

var registeredPlaces = false
var registeredAutofill = false

Expand Down Expand Up @@ -468,7 +469,7 @@ public class RustSyncManager: NSObject, SyncManager {
}
}

private func doSync(params: SyncParams, completion: @escaping (SyncResult) -> Void) {
private func doSync(params: SyncParams, completion: @escaping @Sendable (SyncResult) -> Void) {
beginSyncing()
syncManagerAPI.sync(params: params) { syncResult in
// Save the persisted state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class FirefoxAccountSignInViewController: UIViewController, Themeable {
navigationController?.pushViewController(fxaWebVC, animated: true)
}

private func showFxAWebViewController(_ url: URL, completion: @escaping (URL) -> Void) {
private func showFxAWebViewController(_ url: URL, completion: @escaping @Sendable (URL) -> Void) {
if let accountManager = profile.rustFxA.accountManager {
let entrypoint = self.deepLinkParams.entrypoint.rawValue
accountManager.getManageAccountURL(entrypoint: "ios_settings_\(entrypoint)") { [weak self] result in
Expand Down