ProductsFetcherSK2: removed now redundant caching logic#1908
Merged
Conversation
abbc573 to
dd5caae
Compare
682dfee to
fde5fc4
Compare
b491ce5 to
6b164ce
Compare
tonidero
approved these changes
Sep 28, 2022
Contributor
There was a problem hiding this comment.
I guess we want to remove the caching from the ProductsFetcherSK1 in the future, since we are now caching it in the CachingProductsManager. Did we create a Jira task for it?
Contributor
Author
There was a problem hiding this comment.
Yeah that's why I wrote this:
I've decided to keep the logic in ProductsFetcherSK1 because it's intertwined with the retry mechanism, and it wasn't trivial to remove caching while leaving that logic.
We could file a Jira to do it in the future, but I wonder if it's worth spending the time on that if SK1 is going away eventually /cc @aboedo thoughts?
fde5fc4 to
6eef764
Compare
6b164ce to
4b26689
Compare
NachoSoto
added a commit
that referenced
this pull request
Sep 28, 2022
…when fetching products (#1907) Fixes [SDKONCALL-129]. ### Goal The main feature that this change introduces is providing a _request_ cache to `ProductsFetcherSK2` (like `ProductsFetcherSK1` has). It already implements a _product_ cache, but concurrent requests for the same products result in fetching those multiple times. It does not cache products until they're done being fetched. This was observed in the logs when doing something simple like purchasing a product. This is running `testCanPurchaseProduct` integration test: > DEBUG: ℹ️ No existing products cached, starting store products request for: ["com.revenuecat.monthly_4.99.1_week_intro", "com.revenuecat.annual_39.99.2_week_intro", "com.revenuecat.weekly_1.99.3_day_intro"] DEBUG: ℹ️ No existing products cached, starting store products request for: ["com.revenuecat.monthly_4.99.1_week_intro", "com.revenuecat.annual_39.99.2_week_intro", "com.revenuecat.weekly_1.99.3_day_intro"] DEBUG: 😻 Store products request request received response DEBUG: ℹ️ Store products request finished DEBUG: 😻 Store products request request received response DEBUG: ℹ️ Store products request finished With this PR, we avoid the double request due to that race condition: > DEBUG: ℹ️ No existing products cached, starting store products request for: ["com.revenuecat.annual_39.99.2_week_intro", "com.revenuecat.weekly_1.99.3_day_intro", "com.revenuecat.monthly_4.99.1_week_intro"] DEBUG: 😻 Store products request request received response DEBUG: ℹ️ Store products request finished ### How Instead of implementing that logic _only_ on `ProductsFetcherSK2` or in `ProductsManager`, this aims to keep the implementation simple in `ProductsManager` and each of the fetchers, and instead uses the [decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern) to provide that new functionality in a way that's simple to test and maintain, independent of the logic for actually fetching products. To accomplish that, this adds a new `protocol`: `ProductsManagerType`, with 2 main methods (more about why the need for an SK2 special method below): ```swift /// Fetches the ``StoreProduct``s with the given identifiers /// The returned products will be SK1 or SK2 backed depending on the implementation and configuration. func products(withIdentifiers identifiers: Set<String>, completion: @escaping Completion) /// Fetches the `SK2StoreProduct`s with the given identifiers. @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) func sk2Products(withIdentifiers identifiers: Set<String>, completion: @escaping SK2Completion) ``` Those are only completion-block APIs. To avoid repetition, an `extension` on the `protocol` itself provides an `async` API, wrapping on top of these 2 methods, regardless of which `ProductsManagerType` they're being used on. The hierarchy is now as follows: - `CachingProductsManager` - `ProductsManager` - `ProductsFetcherSK1` - `ProductsFetcherSK2` #1908 gets rid of the now duplicate caching logic in `ProductsManager` and the fetchers. ### Other notes What makes the implementation a bit more convoluted is the need for a special SK2 method. This is necessary specifically for `TrialOrIntroPriceEligibilityChecker` which requires fetching only SK2 products regardless of `StoreKit2Setting`. In order to guarantee this invariant at compile time through types, a separate cache is maintained with `SK2StoreProduct`s. However, the main caching logic is *not* duplicated and instead shared for both. The main tests for `CachingProductsManager` are simple unit tests that don't make use of `StoreKitConfigTestCase`. Because creating SK2 products does require that, I added a separate test class for those. ### Future changes The caching logic is very simple, just like what we had for the fetchers. One missing piece is that if products A and B are requested, and only A is in the cache, this is treated as a complete cache miss. The fetchers had the same problem, but now that this caching logic is isolated and much easier to test, we can evolve and solve that feature in a more maintainable way. [SDKONCALL-129]: https://revenuecats.atlassian.net/browse/SDKONCALL-129?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This is now implemented and shared in `CachingProductsManager`. I've decided to keep the logic in `ProductsFetcherSK1` because its intertwined with a retry mechanism, and it wasn't trivial to remove caching while leaving that logic.
4b26689 to
5a56d7c
Compare
NachoSoto
pushed a commit
that referenced
this pull request
Sep 28, 2022
**This is an automatic release.** ### Other Changes * `ProductsFetcherSK2`: removed now redundant caching logic (#1908) via NachoSoto (@NachoSoto) * Created `CachingProductsManager` to provide consistent caching logic when fetching products (#1907) via NachoSoto (@NachoSoto) * Refactored `ReceiptFetcher.receiptData` (#1941) via NachoSoto (@NachoSoto) * Abstracted conversion from `async` to completion-block APIs (#1943) via NachoSoto (@NachoSoto) * Moved `InAppPurchase` into `AppleReceipt` (#1942) via NachoSoto (@NachoSoto) * `Purchases+async`: combined `@available` statements into a single one (#1944) via NachoSoto (@NachoSoto) * `Integration Tests`: don't initialize `Purchases` until the `SKTestSession` has been re-created (#1946) via NachoSoto (@NachoSoto) * `PostReceiptDataOperation`: print receipt data if `debug` logs are enabled (#1940) via NachoSoto (@NachoSoto) Co-authored-by: RCGitBot <dev+RCGitBot@revenuecat.com>
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.
Follow up to #1907.
This logic is now implemented and shared in
CachingProductsManager.I've decided to keep the logic in
ProductsFetcherSK1because it's intertwined with the retry mechanism, and it wasn't trivial to remove caching while leaving that logic.