-
Notifications
You must be signed in to change notification settings - Fork 316
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
[SK2] Enable Observer Mode for StoreKit 2 #3580
Conversation
…hrow if observer mode is not enabled
purchaseResult: StoreKit.Product.PurchaseResult | ||
) async throws -> StoreTransaction? { | ||
guard !self.systemInfo.observerMode else { | ||
throw NewErrorUtils.observerModeNotEnabledError() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throw early, throw often. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prevent creating yet another error for this. That's yet another error that users need to handle not just on this API, but in all of our APIs, and maybe localize to different languages, etc.
I would instead throw unsupportedError
or configurationError
.
let (userCancelled, transaction) = try await self.purchasesOrchestrator.storeKit2TransactionListener.handle( | ||
purchaseResult: purchaseResult, fromTransactionUpdate: true) | ||
|
||
if userCancelled, self.systemInfo.dangerousSettings.customEntitlementComputation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure about the customEntitlementComputation
check here. I don't think this method should be used if it's enabled anyway? This snippet was taken from PurchasesOrchestrator
:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it for now
*/ | ||
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) | ||
func handleObserverModeTransaction( | ||
purchaseResult: StoreKit.Product.PurchaseResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the swifty way would be
purchaseResult: StoreKit.Product.PurchaseResult | |
_ purchaseResult: StoreKit.Product.PurchaseResult |
@@ -81,6 +81,33 @@ class StoreKit2ObserverModeIntegrationTests: StoreKit1ObserverModeIntegrationTes | |||
|
|||
} | |||
|
|||
class StoreKit2ObserverModeDisabledIntegrationTests: StoreKit1ObserverModeIntegrationTests { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to inherit and run every single test in the super class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh. This can be tested as unit in StoreKitTests
(because we need a Transaction
object) but we don't have any test that instantiates a Purchases object. Is it worth it creating one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but we don't have any test that instantiates a Purchases object. Is it worth it creating one?
Yeah I think it would be useful, but also mock all the dependencies. So I think we can extract the Purchases
setup from BasePurchasesTest
for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we can put this in the regular SK2 test class, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true! I moved it to StoreKit2IntegrationTests
and created StoreKit2NotEnabledObserverModeIntegrationTests
for the sk2 not enabled one.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) | ||
func handleObserverModeTransaction( | ||
purchaseResult: StoreKit.Product.PurchaseResult | ||
) async throws -> StoreTransaction? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this needs to call throw NewErrorUtils.error().asPublicError
.
@@ -263,6 +263,11 @@ private func checkAsyncMethods(purchases: Purchases) async { | |||
let _: CustomerInfo = try await purchases.restorePurchases() | |||
let _: CustomerInfo = try await purchases.syncPurchases() | |||
|
|||
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) { | |||
let result = try await StoreKit.Product.products(for: [""]).first!.purchase() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that you did it this way to ensure that whatever we pass to the next method is the result of this.
func handleObserverModeTransaction( | ||
purchaseResult: StoreKit.Product.PurchaseResult | ||
) async throws -> StoreTransaction? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is Observer Mode but does it matter what they have set for the StoreKit version? Like... should this also require .with(storeKitVersion: .storeKit2)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Indeed that's a requirement.
let (_, transaction) = try await self.purchasesOrchestrator.storeKit2TransactionListener.handle( | ||
purchaseResult: purchaseResult, fromTransactionUpdate: true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we linted this stuff 😇
I'd format it with symmetrical parenthesis:
let (_, transaction) = try await self.purchasesOrchestrator.storeKit2TransactionListener.handle( | |
purchaseResult: purchaseResult, fromTransactionUpdate: true) | |
let (_, transaction) = try await self.purchasesOrchestrator.storeKit2TransactionListener.handle( | |
purchaseResult: purchaseResult, fromTransactionUpdate: true | |
) |
* Purchases.shared.handleObserverModeTransaction(purchaseResult: result) | ||
* ``` | ||
* | ||
* - Note: You need to finish the transaction yourself after calling this method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe turn this into a Warning
since it's pretty important? Maybe put that in the code example above too?
* | ||
* - Note: You need to finish the transaction yourself after calling this method. | ||
* | ||
* - Parameter purchaseResult: The ``StoreKit.Product.PurchaseResult`` of the product that was just purchased. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can reference types outside the module with "``", does this not give a warning when building?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't noticed any warning, but replaced with single ticks since it cannot link anywhere ayway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(configuration.observerMode) == true | ||
expect(configuration.storeKitVersion) == .storeKit2 | ||
|
||
self.logger.verifyMessageWasLogged(Strings.configure.observer_mode_with_storekit2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe instead we could log a different message now pointing out that transactions need to be handled manually?
@@ -187,6 +185,8 @@ extension ConfigureStrings: LogMessage { | |||
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(storeKitVersion: .storeKit2)` " + | |||
"when configuring the SDK." | |||
case .sk2_required_observer_mode: | |||
return "Observer mode must be enabled. You must use `.with(observerMode: true)` when configuring the SDK." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
return "Observer mode must be enabled. You must use `.with(observerMode: true)` when configuring the SDK." | |
return "Attempted to manually handle transactions with observer mode not enabled. You must use `.with(observerMode: true)` when configuring the SDK." |
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## sk2-full-flow #3580 +/- ##
=================================================
- Coverage 85.87% 85.86% -0.02%
=================================================
Files 242 242
Lines 17613 17645 +32
=================================================
+ Hits 15125 15150 +25
- Misses 2488 2495 +7 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just one more question about the removed tests.
|
||
expect(configuration.observerMode) == true | ||
expect(configuration.storeKitVersion) == .storeKit2 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: extra space
@@ -31,50 +31,6 @@ class ConfigurationTests: TestCase { | |||
expect(Configuration.validate(apiKey: "swRTCezdEzjnJSxdexDNJfcfiFrMXwqZ")) == .legacy | |||
} | |||
|
|||
func testNoObserverModeWithStoreKit1() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You brought back observer_mode_with_storekit2
. Can you bring back these tests instead? I think you can just reset this file, right?
No description provided.