-
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
Purchases
: don't clear intro eligibility / purchased products cache on first launch
#3067
Conversation
f3fecaa
to
c92bf68
Compare
@@ -100,14 +98,6 @@ class CustomerInfoManager { | |||
} | |||
} | |||
|
|||
func sendCachedCustomerInfoIfAvailable(appUserID: String) { |
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 wasn't being used by anything other than in tests.
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.
interesting! I guess it's a holder from this: #1828
@@ -19,8 +19,6 @@ class CustomerInfoManager { | |||
|
|||
typealias CustomerInfoCompletion = @MainActor @Sendable (Result<CustomerInfo, BackendError>) -> Void | |||
|
|||
var lastSentCustomerInfo: CustomerInfo? { return self.data.value.lastSentCustomerInfo } |
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 was only visible for tests so I moved it below.
f5705fa
to
5ebd497
Compare
self.purchasedProductsFetcher?.clearCache() | ||
self.delegate?.purchases?(self, receivedUpdated: customerInfo) | ||
func handleCustomerInfoChanged(from old: CustomerInfo?, to new: CustomerInfo) { | ||
if old != nil { |
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 the core of this change. We no longer clear the caches for the first time we receive a customer info change.
@@ -196,51 +207,10 @@ class CustomerInfoManagerTests: BaseCustomerInfoManagerTests { | |||
expect(self.mockBackend.invokedGetSubscriberDataCount) == 1 | |||
} | |||
|
|||
func testSendCachedCustomerInfoIfAvailableForAppUserIDSendsIfNeverSent() throws { |
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 all these tests for the unused method.
expect(self.customerInfoManagerLastCustomerInfoChange) == (old: nil, new: self.mockCustomerInfo) | ||
} | ||
|
||
func testCacheCustomerInfoSendsMultipleUpdatesIfChange() throws { |
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.
We didn't have this test.
expect(self.customerInfoManagerLastCustomerInfoChange) == (old: nil, new: info) | ||
} | ||
|
||
func testCacheCustomerInfoSendsToDelegateAfterCachingComputedOnDevice() { |
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.
The first implementation broke integration tests in a way that this test now captures.
@@ -215,7 +205,12 @@ class CustomerInfoManager { | |||
func clearCustomerInfoCache(forAppUserID appUserID: String) { | |||
self.modifyData { | |||
$0.deviceCache.clearCustomerInfoCache(appUserID: appUserID) | |||
$0.lastSentCustomerInfo = nil |
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 didn't make sense. It didn't matter because the property wasn't used by anything, but now this is used for sending the "old" value. Resetting this broke an integration test, which is what I captured in the new testCacheCustomerInfoSendsToDelegateAfterCachingComputedOnDevice
test.
func testFirstInitializationDoesNotClearIntroEligibilityCache() { | ||
self.setupPurchases() | ||
expect(self.purchasesDelegate.customerInfoReceivedCount).toEventually(equal(1)) | ||
|
||
expect(self.cachingTrialOrIntroPriceEligibilityChecker.invokedClearCache) == false | ||
} | ||
|
||
func testFirstInitializationDoesNotClearPurchasedProductsCache() { | ||
self.setupPurchases() | ||
expect(self.purchasesDelegate.customerInfoReceivedCount).toEventually(equal(1)) | ||
|
||
expect(self.mockPurchasedProductsFetcher.invokedClearCache) == false | ||
} | ||
|
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.
Tests to capture the new behavior.
… on first launch These caches are important, especially for `RevenueCatUI`. Without this fix, launching the app was doing this: - Pre-warming cache - Pre-warming cache a second time (to be fixed by a separate PR) - Clearing cache Which meant that the cache wasn't really warm when launching paywalls. To fix that, this only clears the cache after receiving an actual change. I've also removed `CustomerInfoManager.sendCachedCustomerInfoIfAvailable` because it wasn't used.
5ebd497
to
5392d52
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3067 +/- ##
==========================================
+ Coverage 86.58% 86.64% +0.05%
==========================================
Files 219 219
Lines 15674 15676 +2
==========================================
+ Hits 13572 13582 +10
+ Misses 2102 2094 -8 ☔ 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.
this reminds me of this old idea: https://linear.app/revenuecat/issue/SDK-2871/new-customerinfo-listener-with-diff, feels like this change would make that pretty much trivial?
Oh that's right 😇 this paves the way for that. |
… on first launch (#3067) These caches are important, especially for `RevenueCatUI`. Without this fix, launching the app was doing this: - Pre-warming cache - Pre-warming cache a second time (to be fixed by a separate PR) - Clearing cache Which meant that the cache wasn't really warm when launching paywalls. To fix that, this only clears the cache after receiving an actual change. I've also removed `CustomerInfoManager.sendCachedCustomerInfoIfAvailable` because it wasn't used.
**This is an automatic release.** ### Bugfixes * `DebugViewModel`: fixed runtime crash on iOS < 16 (#3139) via NachoSoto (@NachoSoto) ### Performance Improvements * `PurchasesOrchestrator`: return early if receipt has no transactions when checking for promo offers (#3123) via Mark Villacampa (@MarkVillacampa) * `Purchases`: don't clear intro eligibility / purchased products cache on first launch (#3067) via NachoSoto (@NachoSoto) ### Dependency Updates * `SPM`: update `Package.resolved` (#3130) via NachoSoto (@NachoSoto) ### Other Changes * `ReceiptParser`: fixed SPM build (#3144) via NachoSoto (@NachoSoto) * `carthage_installation_tests`: optimize SPM package loading (#3129) via NachoSoto (@NachoSoto) * `CI`: add workaround for `Carthage` timing out (#3119) via NachoSoto (@NachoSoto) * `Integration Tests`: workaround to not lose debug logs (#3108) via NachoSoto (@NachoSoto)
… on first launch (#3067) These caches are important, especially for `RevenueCatUI`. Without this fix, launching the app was doing this: - Pre-warming cache - Pre-warming cache a second time (to be fixed by a separate PR) - Clearing cache Which meant that the cache wasn't really warm when launching paywalls. To fix that, this only clears the cache after receiving an actual change. I've also removed `CustomerInfoManager.sendCachedCustomerInfoIfAvailable` because it wasn't used.
**This is an automatic release.** ### Bugfixes * `DebugViewModel`: fixed runtime crash on iOS < 16 (#3139) via NachoSoto (@NachoSoto) ### Performance Improvements * `PurchasesOrchestrator`: return early if receipt has no transactions when checking for promo offers (#3123) via Mark Villacampa (@MarkVillacampa) * `Purchases`: don't clear intro eligibility / purchased products cache on first launch (#3067) via NachoSoto (@NachoSoto) ### Dependency Updates * `SPM`: update `Package.resolved` (#3130) via NachoSoto (@NachoSoto) ### Other Changes * `ReceiptParser`: fixed SPM build (#3144) via NachoSoto (@NachoSoto) * `carthage_installation_tests`: optimize SPM package loading (#3129) via NachoSoto (@NachoSoto) * `CI`: add workaround for `Carthage` timing out (#3119) via NachoSoto (@NachoSoto) * `Integration Tests`: workaround to not lose debug logs (#3108) via NachoSoto (@NachoSoto)
Hey @NachoSoto , is there even a way to clear these caches from the user side at all ? I'm having stale cache issues that persist even across a cold app start edit: for eligibility I mean |
There is not currently. What are you observing? Could you share some logs so we can take a look? |
We're hoping to add some trial introductory offers a few weeks from now, and that would go along with some UI updates on the paywall to showcase it to the users. But it takes a long time for the SDK to catch up with any updates we do on App Store Connect, it comes back from disk cache it seems:
Could you share with me what is the cache window duration if any? I believe it would have cleared if I killed the app, but apparently it does not |
The test I'm doing is actually adding/removing a free trial on App Store Connect, so product caching might also play a factor (using the default offering for this) |
Intro eligibility is never cached on disk though, only in memory (so re-computed when launching the app). The cache is cleared whenever the
Interesting. What types of changes are you making and what do you observe? Also can confirm if you're using SK1 or SK2? |
Cool, thanks for the assurance. I did notice the
I am removing an existing Introductory Offer from App Store Connect (or adding one if it did not exist). I assume it wouldn't be a very common scenario, but it was caught by our QA team and we're trying to understand exactly what is the criteria for this to update or nudge it in the right way to refresh. |
The SDK also doesn't have any on-disk product cache.
The default is SK1 for everything.
Our current guess is that there's some propagation lag by Apple when you update offers in ASC. |
When using a SK file to introduce / remove the trial offers it reflects as soon as I run the new code as expected. Simulating realtime updates won't be possible and those would be cached by RC's eligibility checker anyway, so I think that's good enough as Apple is the main bottleneck here! |
These caches are important, especially for
RevenueCatUI
.Without this fix, launching the app was doing this:
Which meant that the cache wasn't really warm when launching paywalls.
To fix that, this only clears the cache after receiving an actual change.
I've also removed
CustomerInfoManager.sendCachedCustomerInfoIfAvailable
because it wasn't used.