diff --git a/Account/Sources/Account/AccountSyncError.swift b/Account/Sources/Account/AccountSyncError.swift index efc88da1e..6b32b5f2d 100644 --- a/Account/Sources/Account/AccountSyncError.swift +++ b/Account/Sources/Account/AccountSyncError.swift @@ -22,8 +22,7 @@ public struct AccountSyncError { init(account: Account, error: Error) { self.account = account self.error = error - os_log(.error, log: AccountSyncError.log, "%@", error.localizedDescription) + os_log(.error, log: Self.log, "%@", error.localizedDescription) } - } diff --git a/Account/Sources/Account/CloudKit/CloudKitRemoteNotificationOperation.swift b/Account/Sources/Account/CloudKit/CloudKitRemoteNotificationOperation.swift index fc5458e01..3109f0e0b 100644 --- a/Account/Sources/Account/CloudKit/CloudKitRemoteNotificationOperation.swift +++ b/Account/Sources/Account/CloudKit/CloudKitRemoteNotificationOperation.swift @@ -36,16 +36,16 @@ class CloudKitRemoteNotificationOperation: MainThreadOperation { self.operationDelegate?.operationDidComplete(self) return } - + os_log(.debug, log: log, "Processing remote notification...") - - accountZone.receiveRemoteNotification(userInfo: userInfo) { - articlesZone.receiveRemoteNotification(userInfo: self.userInfo) { - os_log(.debug, log: self.log, "Done processing remote notification.") - self.operationDelegate?.operationDidComplete(self) - } + + Task { @MainActor in + + await accountZone.receiveRemoteNotification(userInfo: userInfo) + await articlesZone.receiveRemoteNotification(userInfo: self.userInfo) + + os_log(.debug, log: self.log, "Done processing remote notification.") + self.operationDelegate?.operationDidComplete(self) } - } - } diff --git a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift index e9b5cb2bf..a76948e64 100644 --- a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift +++ b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift @@ -804,7 +804,7 @@ extension FeedlyAPICaller: FeedlyMarkArticlesService { var entryIDs: [String] } - func mark(_ articleIDs: Set, as action: FeedlyMarkAction, completion: @escaping (Result) -> ()) { + func mark(_ articleIDs: Set, as action: FeedlyMarkAction, completion: @escaping @Sendable (Result) -> ()) { guard !isSuspended else { return DispatchQueue.main.async { completion(.failure(TransportError.suspended)) diff --git a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift index 1155999b8..ddfb88463 100644 --- a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift +++ b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift @@ -13,8 +13,9 @@ import Core import Feedly public protocol OAuthAccountAuthorizationOperationDelegate: AnyObject { - func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) - func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) + + @MainActor func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) + @MainActor func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) } public enum OAuthAccountAuthorizationOperationError: LocalizedError { diff --git a/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift b/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift index 36eaaebe2..6fdbff954 100644 --- a/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift +++ b/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift @@ -98,7 +98,7 @@ public struct OAuthAuthorizationErrorResponse: Error { /// Error values as enumerated in section 4.1.2.1 of the OAuth 2.0 Authorization Framework. /// https://tools.ietf.org/html/rfc6749#section-4.1.2.1 -public enum OAuthAuthorizationError: String { +public enum OAuthAuthorizationError: String, Sendable { case invalidRequest = "invalid_request" case unauthorizedClient = "unauthorized_client" case accessDenied = "access_denied" diff --git a/CloudKitExtras/Sources/CloudKitExtras/CloudKitZone.swift b/CloudKitExtras/Sources/CloudKitExtras/CloudKitZone.swift index eae42efb2..ee86d5827 100644 --- a/CloudKitExtras/Sources/CloudKitExtras/CloudKitZone.swift +++ b/CloudKitExtras/Sources/CloudKitExtras/CloudKitZone.swift @@ -119,6 +119,15 @@ public extension CloudKitZone { }) } + func receiveRemoteNotification(userInfo: [AnyHashable : Any]) async { + + await withCheckedContinuation { continuation in + self.receiveRemoteNotification(userInfo: userInfo) { + continuation.resume() + } + } + } + func receiveRemoteNotification(userInfo: [AnyHashable : Any], completion: @escaping () -> Void) { let note = CKRecordZoneNotification(fromRemoteNotificationDictionary: userInfo) guard note?.recordZoneID?.zoneName == zoneID.zoneName else { diff --git a/Core/Sources/Core/RSAppMovementMonitor.swift b/Core/Sources/Core/RSAppMovementMonitor.swift index 9062b5c9a..ddc8fc01f 100644 --- a/Core/Sources/Core/RSAppMovementMonitor.swift +++ b/Core/Sources/Core/RSAppMovementMonitor.swift @@ -128,8 +128,11 @@ import AppKit func relaunchFromURL(_ appURL: URL) { // Relaunching is best achieved by requesting that the system launch the app // at the given URL with the "new instance" option to prevent it simply reactivating us. - let _ = try? NSWorkspace.shared.launchApplication(at: appURL, options: .newInstance, configuration: [:]) - NSApp.terminate(self) + let configuration = NSWorkspace.OpenConfiguration() + configuration.createsNewApplicationInstance = true + NSWorkspace.shared.openApplication(at: appURL, configuration: configuration) { _,_ in + NSApp.terminate(self) + } } func defaultHandler() { diff --git a/Feedly/Sources/Feedly/Services/FeedlyMarkArticlesService.swift b/Feedly/Sources/Feedly/Services/FeedlyMarkArticlesService.swift index 4c7e07c89..d6528253c 100644 --- a/Feedly/Sources/Feedly/Services/FeedlyMarkArticlesService.swift +++ b/Feedly/Sources/Feedly/Services/FeedlyMarkArticlesService.swift @@ -32,5 +32,6 @@ public enum FeedlyMarkAction: String, Sendable { } public protocol FeedlyMarkArticlesService: AnyObject { - func mark(_ articleIDs: Set, as action: FeedlyMarkAction, completion: @escaping (Result) -> ()) + + func mark(_ articleIDs: Set, as action: FeedlyMarkAction, completion: @escaping @Sendable (Result) -> ()) } diff --git a/Mac/Browser.swift b/Mac/Browser.swift index bfdd2caaf..d70f81f29 100644 --- a/Mac/Browser.swift +++ b/Mac/Browser.swift @@ -54,10 +54,13 @@ struct Browser { NSWorkspace.shared.open(preparedURL, configuration: configuration) { (runningApplication, error) in guard error != nil else { return } - if let defaultBrowser = defaultBrowser { - defaultBrowser.openURL(url, inBackground: inBackground) - } else { - MacWebBrowser.openURL(url, inBackground: inBackground) + + Task { @MainActor in + if let defaultBrowser = defaultBrowser { + defaultBrowser.openURL(url, inBackground: inBackground) + } else { + MacWebBrowser.openURL(url, inBackground: inBackground) + } } } } diff --git a/Mac/MainWindow/Detail/DetailIconSchemeHandler.swift b/Mac/MainWindow/Detail/DetailIconSchemeHandler.swift index ac9ba1354..dcd1757c0 100644 --- a/Mac/MainWindow/Detail/DetailIconSchemeHandler.swift +++ b/Mac/MainWindow/Detail/DetailIconSchemeHandler.swift @@ -10,15 +10,15 @@ import Foundation import WebKit import Articles -class DetailIconSchemeHandler: NSObject, WKURLSchemeHandler { +final class DetailIconSchemeHandler: NSObject, WKURLSchemeHandler { var currentArticle: Article? - + func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { Task { @MainActor in - guard let responseURL = urlSchemeTask.request.url, let iconImage = self.currentArticle?.iconImage() else { + guard let responseURL = urlSchemeTask.request.url, let iconImage = self.currentArticle?.iconImage() else { urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist)) return } diff --git a/Mac/MainWindow/Sidebar/PasteboardFeed.swift b/Mac/MainWindow/Sidebar/PasteboardFeed.swift index e0745578b..e0fb33621 100644 --- a/Mac/MainWindow/Sidebar/PasteboardFeed.swift +++ b/Mac/MainWindow/Sidebar/PasteboardFeed.swift @@ -168,29 +168,33 @@ extension Feed: PasteboardWriterOwner { // MARK: - NSPasteboardWriting - func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { + nonisolated func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { - return [FeedPasteboardWriter.feedUTIType, .URL, .string, FeedPasteboardWriter.feedUTIInternalType] + MainActor.assumeIsolated { + return [FeedPasteboardWriter.feedUTIType, .URL, .string, FeedPasteboardWriter.feedUTIInternalType] + } } - func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { + nonisolated func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { - let plist: Any? - - switch type { - case .string: - plist = feed.nameForDisplay - case .URL: - plist = feed.url - case FeedPasteboardWriter.feedUTIType: - plist = exportDictionary - case FeedPasteboardWriter.feedUTIInternalType: - plist = internalDictionary - default: - plist = nil + MainActor.assumeIsolated { + let plist: Any? + + switch type { + case .string: + plist = feed.nameForDisplay + case .URL: + plist = feed.url + case FeedPasteboardWriter.feedUTIType: + plist = exportDictionary + case FeedPasteboardWriter.feedUTIInternalType: + plist = internalDictionary + default: + plist = nil + } + + return plist } - - return plist } } diff --git a/Mac/MainWindow/Sidebar/PasteboardFolder.swift b/Mac/MainWindow/Sidebar/PasteboardFolder.swift index 256cb4224..4b110b815 100644 --- a/Mac/MainWindow/Sidebar/PasteboardFolder.swift +++ b/Mac/MainWindow/Sidebar/PasteboardFolder.swift @@ -104,25 +104,29 @@ extension Folder: PasteboardWriterOwner { // MARK: - NSPasteboardWriting - func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { + nonisolated func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { - return [.string, FolderPasteboardWriter.folderUTIInternalType] + MainActor.assumeIsolated { + return [.string, FolderPasteboardWriter.folderUTIInternalType] + } } - func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { + nonisolated func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { - let plist: Any? - - switch type { - case .string: - plist = folder.nameForDisplay - case FolderPasteboardWriter.folderUTIInternalType: - plist = internalDictionary - default: - plist = nil + MainActor.assumeIsolated { + let plist: Any? + + switch type { + case .string: + plist = folder.nameForDisplay + case FolderPasteboardWriter.folderUTIInternalType: + plist = internalDictionary + default: + plist = nil + } + + return plist } - - return plist } } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 05d3bd787..d0731d89d 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -2350,8 +2350,8 @@ 849C64611ED37A5D003D8FC0 /* Products */, 51C452B22265141B00C03939 /* Frameworks */, 84A699132BC34E8500605AB8 /* ArticleExtractor */, - 51CD32C624D2DEF9009ABAEF /* Account */, 84FB9FAE2BC3494B00B7AFC3 /* FeedFinder */, + 51CD32C624D2DEF9009ABAEF /* Account */, 84A699182BC3524C00605AB8 /* LocalAccount */, 84FB9FAD2BC344F800B7AFC3 /* Feedbin */, 84A699192BC36EDB00605AB8 /* Feedly */, diff --git a/Shared/SmartFeeds/SmartFeedPasteboardWriter.swift b/Shared/SmartFeeds/SmartFeedPasteboardWriter.swift index 8512e8b5c..53c99646c 100644 --- a/Shared/SmartFeeds/SmartFeedPasteboardWriter.swift +++ b/Shared/SmartFeeds/SmartFeedPasteboardWriter.swift @@ -20,23 +20,25 @@ import Account // MARK: - NSPasteboardWriting - func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { + nonisolated func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { return [.string] } - func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { + nonisolated func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { - let plist: Any? - - switch type { - case .string: - plist = smartFeed.nameForDisplay - default: - plist = nil + MainActor.assumeIsolated { + let plist: Any? + + switch type { + case .string: + plist = smartFeed.nameForDisplay + default: + plist = nil + } + + return plist } - - return plist } } diff --git a/Shared/UserNotifications/UserNotificationManager.swift b/Shared/UserNotifications/UserNotificationManager.swift index d9d3e0988..eafcb41f9 100644 --- a/Shared/UserNotifications/UserNotificationManager.swift +++ b/Shared/UserNotifications/UserNotificationManager.swift @@ -11,10 +11,9 @@ import Account import Articles import UserNotifications -final class UserNotificationManager: NSObject { - - override init() { - super.init() +final class UserNotificationManager: Sendable { + + init() { NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil) registerCategoriesAndActions() @@ -103,5 +102,4 @@ private extension UserNotificationManager { UNUserNotificationCenter.current().setNotificationCategories([newArticleCategory]) } - }