[Paywalls V2] Extract ImageLoader to use a single one#2146
Conversation
0953e78 to
ab37428
Compare
📸 Snapshot TestSome snapshots failed to render
🛸 Powered by Emerge Tools |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2146 +/- ##
==========================================
- Coverage 80.54% 80.47% -0.07%
==========================================
Files 277 277
Lines 9456 9465 +9
Branches 1334 1335 +1
==========================================
+ Hits 7616 7617 +1
- Misses 1280 1288 +8
Partials 560 560 ☔ View full report in Codecov by Sentry. |
ajpallares
left a comment
There was a problem hiding this comment.
Looks good, but I'm not ready to approve these yet 🙏
A couple of comments though
|
|
||
| @Suppress("unused", "UNUSED_VARIABLE", "EmptyFunctionBlock", "DEPRECATION") | ||
| private class PurchasesAPI { | ||
| @OptIn(InternalRevenueCatAPI::class) |
There was a problem hiding this comment.
Is this the equivalent to what we have in iOS with _spi(Internal). Just curious :)
There was a problem hiding this comment.
Yup! You need to explicitly annotate usages of these APIs like this.
JayShortway
left a comment
There was a problem hiding this comment.
This is great! The garbage collector will thank us 😄 Just a suggestion on making this a static field instead.
| customerInfoUpdateHandler, | ||
| ), | ||
| val imageLoader: ImageLoader? = null, | ||
| val processLifecycleOwnerProvider: () -> LifecycleOwner = { ProcessLifecycleOwner.get() }, |
There was a problem hiding this comment.
Just curious, why was this change needed?
There was a problem hiding this comment.
Ahh good question 😅 So, it seems that adding the testImplementation with the code library, changed our dependency with androidx.lifecycle. Before this PR, it had an implementation in Java, where we could mockk the ProcessLifecycleOwner using mockkStatic. After the new version, it was in kotlin, and our previous implementation didn't work. I thought of modifying the mocks to work, but then I thought we should try to inject it instead of mocking it like we were until now.
As I was thinking about injecting it, in order to avoid any behavior change, I preferred not to get it at the time the PurchasesOrchestrator is built (normally during Application's onCreate), instead, this is basically lazy loading it when it was being called before. I think it might be fine to get it at the time of the PurchasesOrchestrator creation, but not sure. Wdyt? Happy to change it to just store the object instead of lazy loading it.
There was a problem hiding this comment.
Oh right, so we're using a different androidx.lifecycle version in tests vs production, because of the new Coil testing library we added? I think providing it like this is fine!
| var newImageLoader = if (Purchases.isConfigured) { | ||
| Purchases.sharedInstance.getImageLoader() | ||
| } else { | ||
| null | ||
| } |
There was a problem hiding this comment.
Instead of creating it at configure() time, it would be nicer to have a static field in Purchases that gets set the first time it is accessed, e.g. Purchases.imageLoader. We could take inspiration from newImageLoader() in Coil 2.7.0.
This would avoid this entire scenario, because we'd always be able to get an image loader. That also means we don't need to duplicate the creation logic in both SDKs. What do you think?
There was a problem hiding this comment.
Hmm I'm wondering... I think the accessor in Purchases should still be nullable in case the Coil dependency isn't added... Though TBH, you would need to add it in order to access the class of that property at all... So maybe I will just make it not nullable and say it will crash if Coil isn't present. It's meant for internal usage anyway.
There was a problem hiding this comment.
Another thing as I was starting this, we do need the Application context to create the ImageLoader, which is only given to us at configure time. I guess we could lazy load it, but considering we need to prefetch images anyway, I feel it's better the way it is. Wdyt?
There was a problem hiding this comment.
Hmm, yea my main concern is the duplication of the image loader configuration. I'd love to get rid of that with this effort. I see 2 options:
- We just use
Purchases.sharedInstance.imageLoadereverywhere, and get rid of theContext.getRevenueCatUIImageLoader()function. We won't be showing any UI (and thus images) if we're not configured anyway. - Probably cleaner: we create a static
Purchases.getImageLoader(Context)function, which only creates a new one the first time it is called, and otherwise just returns the same instance every time. (Good point on needing a context!)
What do you think about that second option in particular?
There was a problem hiding this comment.
Hmm yeah I think that can work! I will do the second option then 👍
|
|
||
| dokkaPlugin(project(":dokka-hide-internal")) | ||
|
|
||
| testImplementation libs.coil.compose |
There was a problem hiding this comment.
This is still needed because in a couple tests, due to some mockk internals, it needs to reference the actual coil.ImageLoader type, making us having to do this... We could rewrite those tests, but I think the new way in this PR probably makes more sense. Lmk what you think!
There was a problem hiding this comment.
It's not the end of the world imo!
JayShortway
left a comment
There was a problem hiding this comment.
Beautiful! 💪 Just the thread safe comment.
| customerInfoUpdateHandler, | ||
| ), | ||
| val imageLoader: ImageLoader? = null, | ||
| val processLifecycleOwnerProvider: () -> LifecycleOwner = { ProcessLifecycleOwner.get() }, |
There was a problem hiding this comment.
Oh right, so we're using a different androidx.lifecycle version in tests vs production, because of the new Coil testing library we added? I think providing it like this is fine!
| fun getImageLoader(context: Context): ImageLoader { | ||
| val currentImageLoader = cachedImageLoader | ||
| return if (currentImageLoader == null) { | ||
| val maxCacheSizeBytes = 25 * 1024 * 1024L // 25 MB | ||
| val cacheFolder = "revenuecatui_cache" | ||
| val imageLoader = ImageLoader.Builder(context) | ||
| .diskCache { | ||
| DiskCache.Builder() | ||
| .directory(context.cacheDir.resolve(cacheFolder)) | ||
| .maxSizeBytes(maxCacheSizeBytes) | ||
| .build() | ||
| } | ||
| .build() | ||
| cachedImageLoader = imageLoader | ||
| imageLoader | ||
| } else { | ||
| currentImageLoader | ||
| } | ||
| } |
There was a problem hiding this comment.
What do you think about making this thread safe, along the lines of this?
There was a problem hiding this comment.
Whoops good point, thanks!!
**This is an automatic release.** ## RevenueCat SDK ### ✨ New Features * Add `hasPaywall` property to `Offering` (#2212) via Antonio Pallares (@ajpallares) ### 🐞 Bugfixes * Fix empty options in NoActive subscriptions screen (#2168) via Cesar de la Vega (@vegaro) ## RevenueCatUI SDK ### Customer Center #### ✨ New Features * Create `CustomerCenterListener` (#2199) via Cesar de la Vega (@vegaro) #### 🐞 Bugfixes * Reload Customer Center after a successful restore (#2203) via Cesar de la Vega (@vegaro) * Fixes CustomerCenter state not refreshing when reopening (#2202) via Cesar de la Vega (@vegaro) ### 🔄 Other Changes * Improves PaywallsTester multi-API-key support (#2218) via JayShortway (@JayShortway) * [EXTERNAL] Bump Emerge Gradle Plugin and Snaphsots version (#2211) via @runningcode (#2217) via JayShortway (@JayShortway) * [AUTOMATIC][Paywalls V2] Updates Compose previews of all templates (#2207) via RevenueCat Git Bot (@RCGitBot) * [Paywalls V2] Enables template previews again (#2215) via JayShortway (@JayShortway) * Adds support for switching between 2 API keys to PaywallsTester (#2213) via JayShortway (@JayShortway) * Adds a `LocalPreviewImageLoader` `CompositionLocal`. (#2201) via JayShortway (@JayShortway) * Logs from RevenueCatUI are now tagged with `[Purchases]` too. (#2206) via JayShortway (@JayShortway) * [Paywalls V2] Ignores template previews for now. (#2209) via JayShortway (@JayShortway) * [Paywalls V2] Some more template previews optimizations (#2208) via JayShortway (@JayShortway) * chore: Delete key from customer center survey event (#2204) via Facundo Menzella (@facumenzella) * [Paywalls V2] Extract ImageLoader to use a single one (#2146) via Toni Rico (@tonidero) * [Paywalls V2] Adds progress indicator to buttons (#2198) via JayShortway (@JayShortway) * Avoids triggering "unscheduled" workflows when triggering workflows via the CircleCI API (#2200) via JayShortway (@JayShortway) * [Paywalls V2] Adds a note on publishing to the missing paywall error. (#2193) via JayShortway (@JayShortway) * Adds X-Kotlin-Version header. (#2197) via JayShortway (@JayShortway) * [Paywalls V2] Adds docs on ignored arguments for Paywalls V2 in more places. (#2195) via JayShortway (@JayShortway) * chore: Add backend integration test for events (#2189) via Facundo Menzella (@facumenzella) * [Paywalls V2] Adds CI job to update template previews (#2192) via JayShortway (@JayShortway) --------- Co-authored-by: revenuecat-ops <ops@revenuecat.com> Co-authored-by: Cesar de la Vega <cesarvegaro@gmail.com> Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com>
Description
We should be using a single
ImageLoaderbut we're actually creating a bunch. This refactors our code to create a single one and pass that one where needed.