From db86c7147dbcf2ac5dd3fcbe1b437af1697c2389 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Thu, 19 Dec 2024 15:29:00 -0600 Subject: [PATCH 1/3] always call readyForPromotedProduct on main thread/actor --- Sources/Purchasing/Purchases/Purchases.swift | 11 ++++++++++- .../Purchases/PurchasesDeferredPurchasesTests.swift | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Sources/Purchasing/Purchases/Purchases.swift b/Sources/Purchasing/Purchases/Purchases.swift index 2996ad19d3..0a86bcdb91 100644 --- a/Sources/Purchasing/Purchases/Purchases.swift +++ b/Sources/Purchasing/Purchases/Purchases.swift @@ -1597,7 +1597,16 @@ extension Purchases: PurchasesOrchestratorDelegate { */ func readyForPromotedProduct(_ product: StoreProduct, purchase startPurchase: @escaping StartPurchaseBlock) { - self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase) + + if self.systemInfo.storeKitVersion == .storeKit1 { + OperationDispatcher.default.dispatchOnMainThread { + self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase) + } + } else { + OperationDispatcher.default.dispatchOnMainActor { + self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase) + } + } } #if os(iOS) || targetEnvironment(macCatalyst) || VISION_OS diff --git a/Tests/UnitTests/Purchasing/Purchases/PurchasesDeferredPurchasesTests.swift b/Tests/UnitTests/Purchasing/Purchases/PurchasesDeferredPurchasesTests.swift index ff2fc73790..1357143b14 100644 --- a/Tests/UnitTests/Purchasing/Purchases/PurchasesDeferredPurchasesTests.swift +++ b/Tests/UnitTests/Purchasing/Purchases/PurchasesDeferredPurchasesTests.swift @@ -177,6 +177,12 @@ class PurchaseDeferredPurchasesSK2Tests: BasePurchasesTests { for: self.product ) + waitUntil { completed in + if self.purchasesDelegate.makeDeferredPurchase != nil { + completed() + } + } + expect(self.purchasesDelegate.makeDeferredPurchase).toNot(beNil()) expect(self.purchasesDelegate.promoProduct) == StoreProduct(sk1Product: self.product) From 6cea4c7fd6cc944993eac292bb2fdf86af822769 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Fri, 20 Dec 2024 06:41:36 -0600 Subject: [PATCH 2/3] add comments --- Sources/Purchasing/Purchases/Purchases.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/Purchasing/Purchases/Purchases.swift b/Sources/Purchasing/Purchases/Purchases.swift index 0a86bcdb91..1edafad6a0 100644 --- a/Sources/Purchasing/Purchases/Purchases.swift +++ b/Sources/Purchasing/Purchases/Purchases.swift @@ -1599,10 +1599,13 @@ extension Purchases: PurchasesOrchestratorDelegate { purchase startPurchase: @escaping StartPurchaseBlock) { if self.systemInfo.storeKitVersion == .storeKit1 { + // Calling the delegate method on the main actor causes test failures on iOS 14-16, so instead + // we dispatch to the main thread, which doesn't cause the failures. OperationDispatcher.default.dispatchOnMainThread { self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase) } } else { + // Ensure that the delegate method is called on the main actor for StoreKit 2. OperationDispatcher.default.dispatchOnMainActor { self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase) } From 8bf9a765b25374ac5248e86c69c51d0781f4aa29 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Fri, 20 Dec 2024 11:48:12 -0600 Subject: [PATCH 3/3] switch to a switch --- Sources/Purchasing/Purchases/Purchases.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/Purchasing/Purchases/Purchases.swift b/Sources/Purchasing/Purchases/Purchases.swift index 1edafad6a0..62eb15d8b2 100644 --- a/Sources/Purchasing/Purchases/Purchases.swift +++ b/Sources/Purchasing/Purchases/Purchases.swift @@ -1598,13 +1598,14 @@ extension Purchases: PurchasesOrchestratorDelegate { func readyForPromotedProduct(_ product: StoreProduct, purchase startPurchase: @escaping StartPurchaseBlock) { - if self.systemInfo.storeKitVersion == .storeKit1 { + switch systemInfo.storeKitVersion { + case .storeKit1: // Calling the delegate method on the main actor causes test failures on iOS 14-16, so instead // we dispatch to the main thread, which doesn't cause the failures. OperationDispatcher.default.dispatchOnMainThread { self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase) } - } else { + case .storeKit2: // Ensure that the delegate method is called on the main actor for StoreKit 2. OperationDispatcher.default.dispatchOnMainActor { self.delegate?.purchases?(self, readyForPromotedProduct: product, purchase: startPurchase)