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

[SK2] Enable Observer Mode for StoreKit 2 #3580

Merged
merged 31 commits into from
Jan 18, 2024

Conversation

MarkVillacampa
Copy link
Member

No description provided.

@MarkVillacampa MarkVillacampa added the pr:feat A new feature label Jan 17, 2024
purchaseResult: StoreKit.Product.PurchaseResult
) async throws -> StoreTransaction? {
guard !self.systemInfo.observerMode else {
throw NewErrorUtils.observerModeNotEnabledError()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw early, throw often. 😄

Copy link
Contributor

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 {
Copy link
Member Author

@MarkVillacampa MarkVillacampa Jan 17, 2024

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:

https://github.com/RevenueCat/purchases-ios/blob/main/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift#L501-L502

Copy link
Member Author

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
Copy link
Contributor

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

Suggested change
purchaseResult: StoreKit.Product.PurchaseResult
_ purchaseResult: StoreKit.Product.PurchaseResult

@@ -81,6 +81,33 @@ class StoreKit2ObserverModeIntegrationTests: StoreKit1ObserverModeIntegrationTes

}

class StoreKit2ObserverModeDisabledIntegrationTests: StoreKit1ObserverModeIntegrationTests {
Copy link
Contributor

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.

Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Member Author

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? {
Copy link
Contributor

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()
Copy link
Contributor

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.

Comment on lines 1105 to 1107
func handleObserverModeTransaction(
purchaseResult: StoreKit.Product.PurchaseResult
) async throws -> StoreTransaction? {
Copy link
Member

@joshdholtz joshdholtz Jan 17, 2024

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)?

Copy link
Member Author

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.

Comment on lines 1111 to 1112
let (_, transaction) = try await self.purchasesOrchestrator.storeKit2TransactionListener.handle(
purchaseResult: purchaseResult, fromTransactionUpdate: true)
Copy link
Contributor

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:

Suggested change
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.
Copy link
Contributor

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.
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-01-17 at 12 18 25

expect(configuration.observerMode) == true
expect(configuration.storeKitVersion) == .storeKit2

self.logger.verifyMessageWasLogged(Strings.configure.observer_mode_with_storekit2,
Copy link
Contributor

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."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

Suggested change
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."

@NachoSoto NachoSoto changed the title [SK2] Enable Obserber Mode for StoreKit 2 [SK2] Enable Observer Mode for StoreKit 2 Jan 17, 2024
Copy link

codecov bot commented Jan 18, 2024

Codecov Report

Attention: 25 lines in your changes are missing coverage. Please review.

Comparison is base (ce9f13c) 85.87% compared to head (97bd115) 85.86%.

Files Patch % Lines
Sources/Purchasing/Purchases/Purchases.swift 0.00% 20 Missing ⚠️
Sources/Logging/Strings/ConfigureStrings.swift 66.66% 4 Missing ⚠️
Sources/Logging/Strings/PurchaseStrings.swift 50.00% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@NachoSoto NachoSoto left a 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

Copy link
Contributor

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() {
Copy link
Contributor

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?

@MarkVillacampa MarkVillacampa merged commit ce434bc into sk2-full-flow Jan 18, 2024
26 checks passed
@MarkVillacampa MarkVillacampa deleted the sk2-observer-mode branch January 18, 2024 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants