From a9ae7bdbb09a9a49a60660161fceaa2480e88732 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 20 Oct 2023 08:35:24 -0700 Subject: [PATCH] `StoreKit 1`: disabled `finishTransactions` log on observer mode (#3314) --- Sources/Logging/Strings/PurchaseStrings.swift | 23 +++++++++++++------ Sources/Purchasing/Purchases/Purchases.swift | 6 ++++- .../StoreKit1/StoreKit1Wrapper.swift | 10 ++++++-- .../PurchasesOrchestratorTests.swift | 2 +- .../Mocks/MockStoreKit1Wrapper.swift | 4 ++++ .../Purchases/BasePurchasesTests.swift | 3 ++- .../Purchasing/StoreKit1WrapperTests.swift | 1 + 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/Sources/Logging/Strings/PurchaseStrings.swift b/Sources/Logging/Strings/PurchaseStrings.swift index 10537e6ff2..66a1d353f6 100644 --- a/Sources/Logging/Strings/PurchaseStrings.swift +++ b/Sources/Logging/Strings/PurchaseStrings.swift @@ -39,7 +39,8 @@ enum PurchaseStrings { case paymentqueue_removed_transaction(SKPaymentTransactionObserver, SKPaymentTransaction) case paymentqueue_removed_transaction_no_callbacks_found(SKPaymentTransactionObserver, - SKPaymentTransaction) + SKPaymentTransaction, + observerMode: Bool) case paymentqueue_updated_transaction(SKPaymentTransactionObserver, SKPaymentTransaction) case presenting_code_redemption_sheet @@ -163,12 +164,20 @@ extension PurchaseStrings: LogMessage { .compactMap { $0 } .joined(separator: " ") - case let .paymentqueue_removed_transaction_no_callbacks_found(observer, transaction): - return "\(observer.debugName) removedTransaction for \(transaction.payment.productIdentifier) " + - "but no callbacks to notify.\n" + - "If the purchase completion block is not being invoked after this, it likely means that some other code " + - "outside of the RevenueCat SDK is calling `SKPaymentQueue.finishTransaction`, which is interfering with " + - "RevenueCat purchasing state handling." + case let .paymentqueue_removed_transaction_no_callbacks_found(observer, transaction, observerMode): + // Transactions finished with observer mode won't have a callback because they're being finished + // by the developer and not our SDK. + let shouldIncludeCompletionBlockMessage = !observerMode + + let prefix = "\(observer.debugName) removedTransaction for \(transaction.payment.productIdentifier) " + + "but no callbacks to notify." + let completionBlockMessage = "If the purchase completion block is not being invoked after this, " + + "it likely means that some other code outside of the RevenueCat SDK is calling " + + "`SKPaymentQueue.finishTransaction`, which is interfering with RevenueCat purchasing state handling." + + return shouldIncludeCompletionBlockMessage + ? prefix + "\n" + completionBlockMessage + : prefix case let .paymentqueue_updated_transaction(observer, transaction): return "\(observer.debugName) updatedTransaction: \(transaction.payment.productIdentifier) " + diff --git a/Sources/Purchasing/Purchases/Purchases.swift b/Sources/Purchasing/Purchases/Purchases.swift index 54f5159fcf..6076bd08ab 100644 --- a/Sources/Purchasing/Purchases/Purchases.swift +++ b/Sources/Purchasing/Purchases/Purchases.swift @@ -311,7 +311,11 @@ public typealias StartPurchaseBlock = (@escaping PurchaseCompletedBlock) -> Void let paymentQueueWrapper: EitherPaymentQueueWrapper = systemInfo.storeKit2Setting.shouldOnlyUseStoreKit2 ? .right(.init()) - : .left(.init(operationDispatcher: operationDispatcher, sandboxEnvironmentDetector: systemInfo)) + : .left(.init( + operationDispatcher: operationDispatcher, + observerMode: observerMode, + sandboxEnvironmentDetector: systemInfo + )) let offeringsFactory = OfferingsFactory() let receiptParser = PurchasesReceiptParser.default diff --git a/Sources/Purchasing/StoreKit1/StoreKit1Wrapper.swift b/Sources/Purchasing/StoreKit1/StoreKit1Wrapper.swift index 29b5877386..e5d11cd262 100644 --- a/Sources/Purchasing/StoreKit1/StoreKit1Wrapper.swift +++ b/Sources/Purchasing/StoreKit1/StoreKit1Wrapper.swift @@ -68,13 +68,16 @@ class StoreKit1Wrapper: NSObject { private let paymentQueue: SKPaymentQueue private let operationDispatcher: OperationDispatcher + private let observerMode: Bool private let sandboxEnvironmentDetector: SandboxEnvironmentDetector init(paymentQueue: SKPaymentQueue = .default(), operationDispatcher: OperationDispatcher = .default, + observerMode: Bool, sandboxEnvironmentDetector: SandboxEnvironmentDetector = BundleSandboxEnvironmentDetector.default) { self.paymentQueue = paymentQueue self.operationDispatcher = operationDispatcher + self.observerMode = observerMode self.sandboxEnvironmentDetector = sandboxEnvironmentDetector super.init() @@ -211,8 +214,11 @@ extension StoreKit1Wrapper: SKPaymentTransactionObserver { !callbacks.isEmpty { callbacks.forEach { $0() } } else { - Logger.debug(Strings.purchase.paymentqueue_removed_transaction_no_callbacks_found(self, - transaction)) + Logger.debug(Strings.purchase.paymentqueue_removed_transaction_no_callbacks_found( + self, + transaction, + observerMode: self.observerMode + )) } } } diff --git a/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift b/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift index 3c25ecf16e..a5a97ebf3d 100644 --- a/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift +++ b/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift @@ -129,7 +129,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { } fileprivate func setUpStoreKit1Wrapper() { - self.storeKit1Wrapper = MockStoreKit1Wrapper() + self.storeKit1Wrapper = MockStoreKit1Wrapper(observerMode: self.systemInfo.observerMode) self.storeKit1Wrapper.mockAddPaymentTransactionState = .purchased self.storeKit1Wrapper.mockCallUpdatedTransactionInstantly = true diff --git a/Tests/UnitTests/Mocks/MockStoreKit1Wrapper.swift b/Tests/UnitTests/Mocks/MockStoreKit1Wrapper.swift index e76700f8bc..12901d1981 100644 --- a/Tests/UnitTests/Mocks/MockStoreKit1Wrapper.swift +++ b/Tests/UnitTests/Mocks/MockStoreKit1Wrapper.swift @@ -7,6 +7,10 @@ import StoreKit class MockStoreKit1Wrapper: StoreKit1Wrapper { + init(observerMode: Bool = false) { + super.init(observerMode: observerMode) + } + var payment: SKPayment? var addPaymentCallCount = 0 diff --git a/Tests/UnitTests/Purchasing/Purchases/BasePurchasesTests.swift b/Tests/UnitTests/Purchasing/Purchases/BasePurchasesTests.swift index 24386a281b..829a1b1346 100644 --- a/Tests/UnitTests/Purchasing/Purchases/BasePurchasesTests.swift +++ b/Tests/UnitTests/Purchasing/Purchases/BasePurchasesTests.swift @@ -30,7 +30,6 @@ class BasePurchasesTests: TestCase { // this level it should be moved to `StoreKitUnitTests`, which runs serially. Purchases.logLevel = .verbose - self.storeKit1Wrapper = MockStoreKit1Wrapper() self.notificationCenter = MockNotificationCenter() self.purchasesDelegate = MockPurchasesDelegate() @@ -41,6 +40,7 @@ class BasePurchasesTests: TestCase { self.systemInfo = MockSystemInfo(finishTransactions: true, storeKit2Setting: self.storeKit2Setting, clock: self.clock) + self.storeKit1Wrapper = MockStoreKit1Wrapper(observerMode: self.systemInfo.observerMode) self.deviceCache = MockDeviceCache(sandboxEnvironmentDetector: self.systemInfo, userDefaults: self.userDefaults) self.paywallCache = .init() @@ -228,6 +228,7 @@ class BasePurchasesTests: TestCase { finishTransactions: false, storeKit2Setting: self.storeKit2Setting, clock: self.clock) + self.storeKit1Wrapper = MockStoreKit1Wrapper(observerMode: true) self.initializePurchasesInstance(appUserId: nil) } diff --git a/Tests/UnitTests/Purchasing/StoreKit1WrapperTests.swift b/Tests/UnitTests/Purchasing/StoreKit1WrapperTests.swift index 9f49310bcd..a0e2c6fbb7 100644 --- a/Tests/UnitTests/Purchasing/StoreKit1WrapperTests.swift +++ b/Tests/UnitTests/Purchasing/StoreKit1WrapperTests.swift @@ -29,6 +29,7 @@ class StoreKit1WrapperTests: TestCase, StoreKit1WrapperDelegate { self.wrapper = StoreKit1Wrapper(paymentQueue: self.paymentQueue, operationDispatcher: self.operationDispatcher, + observerMode: false, sandboxEnvironmentDetector: self.sandboxEnvironmentDetector) self.wrapper.delegate = self }