diff --git a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift index fafa1cbd4c..6ddd4a36ed 100644 --- a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift +++ b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift @@ -269,12 +269,16 @@ final class PurchasesOrchestrator { product: StoreProductType, completion: @escaping @Sendable (Result) -> Void) { guard let discountIdentifier = productDiscount.offerIdentifier else { - completion(.failure(ErrorUtils.productDiscountMissingIdentifierError())) + self.operationDispatcher.dispatchOnMainActor { + completion(.failure(ErrorUtils.productDiscountMissingIdentifierError())) + } return } guard let subscriptionGroupIdentifier = product.subscriptionGroupIdentifier else { - completion(.failure(ErrorUtils.productDiscountMissingSubscriptionGroupIdentifierError())) + self.operationDispatcher.dispatchOnMainActor { + completion(.failure(ErrorUtils.productDiscountMissingSubscriptionGroupIdentifierError())) + } return } @@ -284,14 +288,18 @@ final class PurchasesOrchestrator { discountIdentifier: discountIdentifier, product: product, subscriptionGroupIdentifier: subscriptionGroupIdentifier) { result in - completion(result) + self.operationDispatcher.dispatchOnMainActor { + completion(result) + } } } else { self.sk1PromotionalOffer(forProductDiscount: productDiscount, discountIdentifier: discountIdentifier, product: product, subscriptionGroupIdentifier: subscriptionGroupIdentifier) { result in - completion(result) + self.operationDispatcher.dispatchOnMainActor { + completion(result) + } } } @@ -1125,7 +1133,9 @@ private extension PurchasesOrchestrator { if let cachedCustomerInfo, cachedCustomerInfo.originalPurchaseDate != nil, cachedCustomerInfo.originalApplicationVersion != nil { - completion?(.success(cachedCustomerInfo)) + self.operationDispatcher.dispatchOnMainActor { + completion?(.success(cachedCustomerInfo)) + } return } diff --git a/Sources/Purchasing/TrialOrIntroPriceEligibilityChecker.swift b/Sources/Purchasing/TrialOrIntroPriceEligibilityChecker.swift index bd536175f2..f4d04dc93a 100644 --- a/Sources/Purchasing/TrialOrIntroPriceEligibilityChecker.swift +++ b/Sources/Purchasing/TrialOrIntroPriceEligibilityChecker.swift @@ -60,9 +60,18 @@ class TrialOrIntroPriceEligibilityChecker: TrialOrIntroPriceEligibilityCheckerTy return } + // Extracting and wrapping the completion block from the async call + // to avoid having to mark ReceiveIntroEligibilityBlock as @Sendable + // up to the public API thus making a breaking change. + let completionBlock: ReceiveIntroEligibilityBlock = { result in + self.operationDispatcher.dispatchOnMainActor { + completion(result) + } + } + if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *), self.systemInfo.storeKitVersion.isStoreKit2EnabledAndAvailable { - Async.call(with: completion) { + Async.call(with: completionBlock) { do { return try await self.sk2CheckEligibility(productIdentifiers) } catch { @@ -74,7 +83,11 @@ class TrialOrIntroPriceEligibilityChecker: TrialOrIntroPriceEligibilityCheckerTy } } } else { - self.sk1CheckEligibility(productIdentifiers, completion: completion) + self.sk1CheckEligibility(productIdentifiers) { result in + self.operationDispatcher.dispatchOnMainActor { + completion(result) + } + } } } @@ -85,12 +98,18 @@ class TrialOrIntroPriceEligibilityChecker: TrialOrIntroPriceEligibilityCheckerTy self.receiptFetcher.receiptData(refreshPolicy: .never) { data, _ in if let data = data { self.sk1CheckEligibility(with: data, - productIdentifiers: productIdentifiers, - completion: completion) + productIdentifiers: productIdentifiers) { eligibility in + self.operationDispatcher.dispatchOnMainActor { + completion(eligibility) + } + } } else { self.getIntroEligibility(with: data ?? Data(), - productIdentifiers: productIdentifiers, - completion: completion) + productIdentifiers: productIdentifiers) { eligibility in + self.operationDispatcher.dispatchOnMainActor { + completion(eligibility) + } + } } } }