diff --git a/Sources/Identity/CustomerInfoManager.swift b/Sources/Identity/CustomerInfoManager.swift index bea9ed8c64..c0d3f5881c 100644 --- a/Sources/Identity/CustomerInfoManager.swift +++ b/Sources/Identity/CustomerInfoManager.swift @@ -17,7 +17,8 @@ import Foundation class CustomerInfoManager { - typealias CustomerInfoCompletion = @MainActor @Sendable (Result) -> Void + // todo: confirm removal of mainActor here + typealias CustomerInfoCompletion = @Sendable (Result) -> Void private let offlineEntitlementsManager: OfflineEntitlementsManager private let operationDispatcher: OperationDispatcher @@ -89,7 +90,7 @@ class CustomerInfoManager { } if let completion = completion { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchAsyncOnMainThread { completion(result) } } @@ -114,7 +115,7 @@ class CustomerInfoManager { } if let completion = completion { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchAsyncOnMainThread { completion(.success(customerInfo)) } } @@ -128,7 +129,7 @@ class CustomerInfoManager { ) { switch fetchPolicy { case .fromCacheOnly: - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchAsyncOnMainThread { completion?( Result(self.cachedCustomerInfo(appUserID: appUserID), .missingCachedCustomerInfo()) ) @@ -149,7 +150,7 @@ class CustomerInfoManager { Logger.debug(Strings.customerInfo.vending_cache) if let completion = completion { completionCalled = true - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchAsyncOnMainThread { completion(.success(infoFromCache)) } } @@ -175,7 +176,7 @@ class CustomerInfoManager { if let infoFromCache = infoFromCache, !isCacheStale { Logger.debug(Strings.customerInfo.vending_cache) if let completion = completion { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchAsyncOnMainThread { completion(.success(infoFromCache)) } } diff --git a/Sources/Misc/Concurrency/OperationDispatcher.swift b/Sources/Misc/Concurrency/OperationDispatcher.swift index 8f5f5d9f7b..3bae8948e6 100644 --- a/Sources/Misc/Concurrency/OperationDispatcher.swift +++ b/Sources/Misc/Concurrency/OperationDispatcher.swift @@ -52,6 +52,7 @@ class OperationDispatcher { self.mainQueue.async(execute: block) } + // todo: confirm all references to this have OS checks func dispatchOnMainActor(_ block: @MainActor @escaping @Sendable () -> Void) { Self.dispatchOnMainActor(block) } @@ -79,6 +80,7 @@ class OperationDispatcher { extension OperationDispatcher { + // todo: main actor needs OS check static func dispatchOnMainActor(_ block: @MainActor @escaping @Sendable () -> Void) { if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { Task { @MainActor in diff --git a/Sources/Misc/SystemInfo.swift b/Sources/Misc/SystemInfo.swift index 16b54ba38f..9fb23fce29 100644 --- a/Sources/Misc/SystemInfo.swift +++ b/Sources/Misc/SystemInfo.swift @@ -149,15 +149,15 @@ class SystemInfo { /// Asynchronous API if caller can't ensure that it's invoked in the `@MainActor` /// - Seealso: `isApplicationBackgrounded` func isApplicationBackgrounded(completion: @escaping @Sendable (Bool) -> Void) { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchOnMainThread { completion(self.isApplicationBackgrounded) } } /// Synchronous API for callers in `@MainActor`. /// - Seealso: `isApplicationBackgrounded(completion:)` - @MainActor - var isApplicationBackgrounded: Bool { + // todo: main actor here + private var isApplicationBackgrounded: Bool { #if os(iOS) || os(tvOS) || VISION_OS return self.isApplicationBackgroundedIOSAndTVOS #elseif os(macOS) @@ -274,7 +274,7 @@ private extension SystemInfo { // iOS/tvOS App extensions can't access UIApplication.sharedApplication, and will fail to compile if any calls to // it are made. There are no pre-processor macros available to check if the code is running in an app extension, // so we check if we're running in an app extension at runtime, and if not, we use KVC to call sharedApplication. - @MainActor + // todo: main actor here var isApplicationBackgroundedIOSAndTVOS: Bool { if self.isAppExtension { return true @@ -286,7 +286,7 @@ private extension SystemInfo { #elseif os(watchOS) - @MainActor + // todo: main actor here var isApplicationBackgroundedWatchOS: Bool { var isSingleTargetApplication: Bool { return Bundle.main.infoDictionary?.keys.contains("WKApplication") == true diff --git a/Sources/OfflineEntitlements/OfflineEntitlementsManager.swift b/Sources/OfflineEntitlements/OfflineEntitlementsManager.swift index 22c1802ec7..ca7de5f3e9 100644 --- a/Sources/OfflineEntitlements/OfflineEntitlementsManager.swift +++ b/Sources/OfflineEntitlements/OfflineEntitlementsManager.swift @@ -30,9 +30,10 @@ class OfflineEntitlementsManager { self.systemInfo = systemInfo } + // todo: main actor here func updateProductsEntitlementsCacheIfStale( isAppBackgrounded: Bool, - completion: (@MainActor @Sendable (Result<(), Error>) -> Void)? + completion: (@Sendable (Result<(), Error>) -> Void)? ) { guard #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *), !self.systemInfo.observerMode, @@ -103,12 +104,13 @@ private extension OfflineEntitlementsManager { private extension OfflineEntitlementsManager { + // todo: main actor here func dispatchCompletionOnMainThreadIfPossible( - _ completion: (@MainActor @Sendable (Result) -> Void)?, + _ completion: (@Sendable (Result) -> Void)?, result: Result ) { if let completion = completion { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchOnMainThread { completion(result) } } diff --git a/Sources/Purchasing/OfferingsManager.swift b/Sources/Purchasing/OfferingsManager.swift index c2f57f7f95..5d68550aea 100644 --- a/Sources/Purchasing/OfferingsManager.swift +++ b/Sources/Purchasing/OfferingsManager.swift @@ -39,11 +39,12 @@ class OfferingsManager { self.productsManager = productsManager } + // todo: main actor here func offerings( appUserID: String, fetchPolicy: FetchPolicy = .default, fetchCurrent: Bool = false, - completion: (@MainActor @Sendable (Result) -> Void)? + completion: (@Sendable (Result) -> Void)? ) { guard !fetchCurrent else { self.fetchFromNetwork(appUserID: appUserID, fetchPolicy: fetchPolicy, completion: completion) @@ -72,11 +73,12 @@ class OfferingsManager { return self.deviceCache.cachedOfferings } + // todo: main actor here func updateOfferingsCache( appUserID: String, isAppBackgrounded: Bool, fetchPolicy: FetchPolicy = .default, - completion: (@MainActor @Sendable (Result) -> Void)? + completion: (@Sendable (Result) -> Void)? ) { self.backend.offerings.getOfferings(appUserID: appUserID, isAppBackgrounded: isAppBackgrounded) { result in switch result { @@ -132,10 +134,11 @@ class OfferingsManager { private extension OfferingsManager { + // todo: main actor here func fetchFromNetwork( appUserID: String, fetchPolicy: FetchPolicy = .default, - completion: (@MainActor @Sendable (Result) -> Void)? + completion: (@Sendable (Result) -> Void)? ) { Logger.debug(Strings.offering.no_cached_offerings_fetching_from_network) @@ -225,11 +228,12 @@ private extension OfferingsManager { } } + // todo: main actor here func handleOfferingsBackendResult( with response: OfferingsResponse, appUserID: String, fetchPolicy: FetchPolicy, - completion: (@MainActor @Sendable (Result) -> Void)? + completion: (@Sendable (Result) -> Void)? ) { self.createOfferings(from: response, fetchPolicy: fetchPolicy) { result in switch result { @@ -255,21 +259,23 @@ private extension OfferingsManager { } } + // todo: main actor here func handleOfferingsUpdateError( _ error: Error, - completion: (@MainActor @Sendable (Result) -> Void)? + completion: (@Sendable (Result) -> Void)? ) { Logger.appleError(Strings.offering.fetching_offerings_error(error: error, underlyingError: error.underlyingError)) self.dispatchCompletionOnMainThreadIfPossible(completion, value: .failure(error)) } + // todo: verify main actor replacement here func dispatchCompletionOnMainThreadIfPossible( - _ completion: (@MainActor @Sendable (T) -> Void)?, + _ completion: (@Sendable (T) -> Void)?, value: T ) { if let completion = completion { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchOnMainThread { completion(value) } } diff --git a/Sources/Purchasing/Purchases/Purchases.swift b/Sources/Purchasing/Purchases/Purchases.swift index a04fd3e022..d2afe3d6e8 100644 --- a/Sources/Purchasing/Purchases/Purchases.swift +++ b/Sources/Purchasing/Purchases/Purchases.swift @@ -33,10 +33,11 @@ public typealias PurchaseResultData = (transaction: StoreTransaction?, /** Completion block for ``Purchases/purchase(product:completion:)`` */ -public typealias PurchaseCompletedBlock = @MainActor @Sendable (StoreTransaction?, - CustomerInfo?, - PublicError?, - Bool) -> Void +// todo: main actor here +public typealias PurchaseCompletedBlock = @Sendable (StoreTransaction?, + CustomerInfo?, + PublicError?, + Bool) -> Void /** Block for starting purchases in ``PurchasesDelegate/purchases(_:readyForPromotedProduct:purchase:)`` diff --git a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift index d7f7e4e433..a608f9c3b9 100644 --- a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift +++ b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift @@ -374,8 +374,9 @@ final class PurchasesOrchestrator { * we wouldn't be able to notify of the purchase result. */ + // todo: verify actor replacement here guard let productIdentifier = payment.extractProductIdentifier() else { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchOnMainThread { completion( nil, nil, @@ -1291,8 +1292,9 @@ private extension PurchasesOrchestrator { } } + // todo: verify actor replacement here func handleTestProduct(_ completion: @escaping PurchaseCompletedBlock) { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchOnMainThread { completion( nil, nil, diff --git a/Sources/Purchasing/Purchases/TransactionPoster.swift b/Sources/Purchasing/Purchases/TransactionPoster.swift index 0ace188224..0330a3616d 100644 --- a/Sources/Purchasing/Purchases/TransactionPoster.swift +++ b/Sources/Purchasing/Purchases/TransactionPoster.swift @@ -47,9 +47,10 @@ protocol TransactionPosterType: AnyObject, Sendable { /// Finishes the transaction if not in observer mode. /// - Note: `handlePurchasedTransaction` calls this automatically, /// this is only required for failed transactions. + // todo: main actor here func finishTransactionIfNeeded( _ transaction: StoreTransactionType, - completion: @escaping @Sendable @MainActor () -> Void + completion: @escaping @Sendable () -> Void ) } @@ -121,13 +122,14 @@ final class TransactionPoster: TransactionPosterType { } } + // todo: main actor here func finishTransactionIfNeeded( _ transaction: StoreTransactionType, - completion: @escaping @Sendable @MainActor () -> Void + completion: @escaping @Sendable () -> Void ) { @Sendable func complete() { - self.operationDispatcher.dispatchOnMainActor(completion) + self.operationDispatcher.dispatchOnMainThread(completion) } guard self.finishTransactions else { @@ -330,6 +332,7 @@ extension TransactionPoster { private extension TransactionPoster { + // todo: verify actor replacement ehre func product(with identifier: String, completion: @escaping (StoreProduct?) -> Void) { self.productsManager.products(withIdentifiers: [identifier]) { products in self.operationDispatcher.dispatchOnMainThread { diff --git a/Sources/Purchasing/StoreKit1/StoreKitRequestFetcher.swift b/Sources/Purchasing/StoreKit1/StoreKitRequestFetcher.swift index 8f98ca9332..11dff4eecc 100644 --- a/Sources/Purchasing/StoreKit1/StoreKitRequestFetcher.swift +++ b/Sources/Purchasing/StoreKit1/StoreKitRequestFetcher.swift @@ -27,7 +27,9 @@ class StoreKitRequestFetcher: NSObject { private let requestFactory: ReceiptRefreshRequestFactory private var receiptRefreshRequest: SKRequest? - private var receiptRefreshCompletionHandlers: [@MainActor @Sendable () -> Void] + // todo: main actor here + private var receiptRefreshCompletionHandlers: [@Sendable () -> Void] + private let operationDispatcher: OperationDispatcher init(requestFactory: ReceiptRefreshRequestFactory = ReceiptRefreshRequestFactory(), @@ -38,7 +40,8 @@ class StoreKitRequestFetcher: NSObject { self.receiptRefreshCompletionHandlers = [] } - func fetchReceiptData(_ completion: @MainActor @Sendable @escaping () -> Void) { + // todo: main actor here + func fetchReceiptData(_ completion: @Sendable @escaping () -> Void) { self.operationDispatcher.dispatchOnWorkerThread { self.receiptRefreshCompletionHandlers.append(completion) @@ -93,8 +96,9 @@ private extension StoreKitRequestFetcher { let completionHandlers = self.receiptRefreshCompletionHandlers self.receiptRefreshCompletionHandlers = [] + // todo: verify main actor replacement here for handler in completionHandlers { - self.operationDispatcher.dispatchOnMainActor { + self.operationDispatcher.dispatchOnMainThread { handler() } }