PurchasesOrchestrator: simplified StoreKit2TransactionListenerDelegate transaction handling#2536
Closed
NachoSoto wants to merge 1 commit into
Closed
PurchasesOrchestrator: simplified StoreKit2TransactionListenerDelegate transaction handling#2536NachoSoto wants to merge 1 commit into
PurchasesOrchestrator: simplified StoreKit2TransactionListenerDelegate transaction handling#2536NachoSoto wants to merge 1 commit into
Conversation
…gate` transaction handling While working on #2533 I noticed that the implementation for `StoreKit2TransactionListenerDelegate` was unnecessarily complex: ```swift @available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *) extension PurchasesOrchestrator: StoreKit2TransactionListenerDelegate { func storeKit2TransactionListener( _ listener: StoreKit2TransactionListener, updatedTransaction transaction: StoreTransactionType ) async throws { let isRestore = self.systemInfo.observerMode _ = try await self.syncPurchases(receiptRefreshPolicy: .always, isRestore: isRestore, initiationSource: .queue) await Async.call { completion in self.finishTransactionIfNeeded(transaction) { @mainactor in completion(()) } } } } ``` Instead of only posting the one transaction, we were calling `syncPurchases`, which is inconsistent with how we handle SK1 transactions. The new logic in #2533 will also make use of this single-transaction handling code, so it makes sense to unify this as well. This only required an overload to specify the `InitiationSource`, which uncovered one incorrect `isRestore` parameter in one of our tests. I verified that this is expected based on other tests, and the implementation of `initiationSource(for productIdentifier:restored:)`, as well as checking with @antoniobg.
NachoSoto
commented
May 24, 2023
Comment on lines
-861
to
-871
| let isRestore = self.systemInfo.observerMode | ||
|
|
||
| _ = try await self.syncPurchases(receiptRefreshPolicy: .always, | ||
| isRestore: isRestore, | ||
| initiationSource: .queue) | ||
|
|
||
| await Async.call { completion in | ||
| self.finishTransactionIfNeeded(transaction) { @MainActor in | ||
| completion(()) | ||
| } | ||
| } |
Contributor
Author
There was a problem hiding this comment.
Goodbye manual code here.
NachoSoto
commented
May 24, 2023
| expect(transaction.finishInvoked) == false | ||
| expect(self.backend.invokedPostReceiptData) == true | ||
| expect(self.backend.invokedPostReceiptDataParameters?.isRestore) == true | ||
| expect(self.backend.invokedPostReceiptDataParameters?.isRestore) == false |
Contributor
Author
There was a problem hiding this comment.
I explained this in the PR description, this was wrong.
Contributor
Author
|
This breaks |
Contributor
Author
|
This will probably benefit from #2540. |
Contributor
Author
|
Done in #2540 instead. |
NachoSoto
added a commit
that referenced
this pull request
May 25, 2023
This extracts the logic from `PurchasedProductsFetcher` and adds tests. It will be used in #2536. Also exposed the duplicated `underlyingTransaction` implementations
NachoSoto
added a commit
that referenced
this pull request
May 25, 2023
NachoSoto
added a commit
that referenced
this pull request
May 26, 2023
Necessary refactor for #2533. This allows `CustomerInfoManager` to have an instance of `TransactionPosterType` instead of the whole `PurchasesOrchestrator`. Essentially this becomes the hierarchy now:  ### Changes: - Created stateless `TransactionPoster` - Extracted `handlePurchasedTransaction` into `TransactionPoster` - The new implementation there is _mostly_ the same, with the one difference that it's stateless. Instead of holding `purchaseCompleteCallbacksByProductID`, that's done by `PurchasesOrchestrator`. The methods there all take a callback, so they're easier to use and don't require keeping a callback around. - Created `PurchaseSource` to abstract the combo of `isRestore` and `InitiationSource`. This is still messy using the deprecated `allowSharingAppStoreAccount`, but at least that's abstracted out and the new `TransactionPoster` won't deal with that deprecated property. - I didn't mock `TransactionPoster`, which means that all existing tests that check `PurchasesOrchestrator` continue working the same way. This was on purpose to keep this refactor as simple as possible. - This supersedes #2536: the change made there was now possible here without any integration test failures, thanks to the fact that we can process transactions independently without messing with the `purchaseCompleteCallbacksByProductID` state now. ### Future improvements This is just a start, there's a lot more than can be done to simplify the `PurchasesOrchestrator` monster. The main thing would be to remove all the custom code for `restorePurchases`, which is largely the same as what's in `TransactionPoster` now. ### Notes: Just posting this here for posteriority. I had to trace the call hierarchy for both SK1 and SK2. This "block" is what's now in `TransactionPoster`: 
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
While working on #2533 I noticed that the implementation for
StoreKit2TransactionListenerDelegatewas unnecessarily complex:Instead of only posting the one transaction, we were calling
syncPurchases, which is inconsistent with how we handle SK1 transactions.The new logic in #2533 will also make use of this single-transaction handling code, so it makes sense to unify this as well.
Note that since #1704, this method is only invoked for transactions detected outside of a manual purchase flow.
This only required an overload to specify the
InitiationSource, which uncovered one incorrectisRestoreparameter in one of our tests.I verified that this is expected based on other tests, and the implementation of
initiationSource(for productIdentifier:restored:), as well as checking with @antoniobg.