Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StoreKit 2: only listen to StoreKit.Transaction.updates when SK2 is enabled #3032

Merged
merged 1 commit into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Sources/Logging/Strings/ConfigureStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ enum ConfigureStrings {

case timeout_lower_than_minimum(timeout: TimeInterval, minimum: TimeInterval)

case sk2_required_for_swiftui_paywalls

}

extension ConfigureStrings: LogMessage {
Expand Down Expand Up @@ -176,6 +178,11 @@ extension ConfigureStrings: LogMessage {
Timeout value: \(timeout) is lower than the minimum, setting it
to the mimimum: (\(minimum))
"""

case .sk2_required_for_swiftui_paywalls:
return "Purchases is not configured with StoreKit 2 enabled. This is required in order to detect " +
"transactions coming from SwiftUI paywalls. You must use `.with(usesStoreKit2IfAvailable: true)` " +
"when configuring the SDK."
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/Purchasing/Purchases/PurchasesOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ final class PurchasesOrchestrator {

Task {
await storeKit2TransactionListener.set(delegate: self)
await storeKit2TransactionListener.listenForTransactions()
if systemInfo.storeKit2Setting == .enabledForCompatibleDevices {
await storeKit2TransactionListener.listenForTransactions()
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Purchasing/StoreKit1/PaymentQueueWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ extension PaymentQueueWrapper: SKPaymentQueueDelegate {
extension PaymentQueueWrapper: SKPaymentTransactionObserver {

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
// Ignored. Either `StoreKit1Wrapper` or `StoreKit2TransactionListener` will handle this.
// Ignored. Either `StoreKit1Wrapper` will handle this, or `StoreKit2TransactionListener` if `SK2` is enabled.
}

#if !os(watchOS)
Expand Down
4 changes: 4 additions & 0 deletions Sources/Support/PaywallExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ private extension View {
.onInAppPurchaseStart { product in
guard Purchases.isConfigured else { return }

if !Purchases.shared.storeKit2Setting.isEnabledAndAvailable {
Logger.appleWarning(Strings.configure.sk2_required_for_swiftui_paywalls)
}

Purchases.shared.cachePresentedOfferingIdentifier(
offering.identifier,
productIdentifier: product.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,28 +111,6 @@ class StoreKit1ObserverModeIntegrationTests: BaseStoreKitObserverModeIntegration
try await self.verifyEntitlementWentThrough(customerInfo)
}

@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
func testSK2RenewalsPostReceiptOnlyOnceWhenSK1IsEnabled() async throws {
try XCTSkipIf(Self.storeKit2Setting.isEnabledAndAvailable, "Test only for SK1")

// `StoreKit2TransactionListener` is always enabled even in SK1 mode.
// This test ensures that we don't end up posting receipts multiple times when renewals come through.

self.testSession.timeRate = .realTime

let productID = Self.monthlyNoIntroProductID

try await self.manager.purchaseProductFromStoreKit2(productIdentifier: productID)

try? self.testSession.forceRenewalOfSubscription(productIdentifier: productID)

try await self.logger.verifyMessageIsEventuallyLogged(
"Network operation 'PostReceiptDataOperation' found with the same cache key",
timeout: .seconds(4),
pollInterval: .milliseconds(100)
)
}

}

@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
Expand Down
13 changes: 8 additions & 5 deletions Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase {
}

@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
func testListensForSK2TransactionsWithSK2Disabled() throws {
func testDoesNotListenForSK2TransactionsWithSK2Disabled() throws {
try AvailabilityChecks.iOS15APIAvailableOrSkipTest()

let transactionListener = MockStoreKit2TransactionListener()
Expand All @@ -1071,11 +1071,12 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase {
self.setUpOrchestrator(storeKit2TransactionListener: transactionListener,
storeKit2StorefrontListener: StoreKit2StorefrontListener(delegate: nil))

expect(transactionListener.invokedListenForTransactions).toEventually(beTrue())
expect(transactionListener.invokedDelegateSetter).toEventually(beTrue())
expect(transactionListener.invokedListenForTransactions) == false
}

@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
func testListensForSK2TransactionsWithSK2EnabledOnlyForOptimizations() throws {
func testDoesNotListenForSK2TransactionsWithSK2EnabledOnlyForOptimizations() throws {
try AvailabilityChecks.iOS15APIAvailableOrSkipTest()

let transactionListener = MockStoreKit2TransactionListener()
Expand All @@ -1085,7 +1086,8 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase {
self.setUpOrchestrator(storeKit2TransactionListener: transactionListener,
storeKit2StorefrontListener: StoreKit2StorefrontListener(delegate: nil))

expect(transactionListener.invokedListenForTransactions).toEventually(beTrue())
expect(transactionListener.invokedDelegateSetter).toEventually(beTrue())
expect(transactionListener.invokedListenForTransactions) == false
}

@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
Expand All @@ -1099,7 +1101,8 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase {
self.setUpOrchestrator(storeKit2TransactionListener: transactionListener,
storeKit2StorefrontListener: StoreKit2StorefrontListener(delegate: nil))

expect(transactionListener.invokedListenForTransactions).toEventually(beTrue())
expect(transactionListener.invokedDelegateSetter).toEventually(beTrue())
expect(transactionListener.invokedListenForTransactions) == true
expect(transactionListener.invokedListenForTransactionsCount) == 1
}

Expand Down