[Do not merge] PoC: Add new APIs to handle deep links and process RCBilling redemption links#1880
[Do not merge] PoC: Add new APIs to handle deep links and process RCBilling redemption links#1880tonidero wants to merge 16 commits into
Conversation
3cddca1 to
49ef1a8
Compare
There was a problem hiding this comment.
Had to do this to avoid the same activity from launching multiple times with the deep link... We might want to recommend developers to do the same.
There was a problem hiding this comment.
Currently hardcoded schema.
There was a problem hiding this comment.
Added this override since it might be needed if the activity is already there, like in this example
There was a problem hiding this comment.
We will create a new endpoint for this, instead of relying on the aliasing endpoint. For now, reusing that one and we need to use the corresponding parameter names.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1880 +/- ##
==========================================
- Coverage 82.25% 81.78% -0.48%
==========================================
Files 227 231 +4
Lines 7968 8101 +133
Branches 1121 1140 +19
==========================================
+ Hits 6554 6625 +71
- Misses 967 1027 +60
- Partials 447 449 +2 ☔ View full report in Codecov by Sentry. |
|
Hi @RevenueCat/coresdk! While this PR is not final (the endpoint is wrong among other things), I would appreciate a look, specially on the new public APIs. I left comments in the places where it's still WIP. Lmk what you think! Also happy to chat over Slack if you have questions. |
JayShortway
left a comment
There was a problem hiding this comment.
This is getting good! Had some thoughts, and a lot of questions to help me understand the design better haha, but we're well on our way!
There was a problem hiding this comment.
Are devs expected to implement this? If not, can we avoid this being an interface?
There was a problem hiding this comment.
Actually I originally started without this and having the handleRCBillingPurchaseRedemption receive a lambda like (ResultListener) -> Unit. However, using lambdas in Java is not great 😅. So I decided to switch it with an interface... I think it could potentially be a class, as long as we extract the logic, which is implemented within the SDK outside the current class, which also felt like separating the logic too much... So I feel this is better as an interface but not a strong opinion and could be convinced to move to a class by refactoring the RCBillingPurchaseRedemptionHelper
There was a problem hiding this comment.
Yea I do agree this shouldn't be a lambda for Java's sake! I think I'd prefer this being a final class with an internal constructor. That minimizes the API surface. The internal code organization can be tackled in a few ways, but I think the API surface is more important.
There was a problem hiding this comment.
For my understanding, why is this in DeepLinkHandler instead of in Purchases? We now have a second entry point for the SDK (DeepLinkHandler.handleDeepLink()), while this could also be Purchases.handleDeepLink().
(I'm referring to the public API. In terms of code organization, the implementation could still live here of course.)
There was a problem hiding this comment.
Hmm honestly this was a bit of my preference to try to not bloat the Purchases class even more. Specially considering this can actually work without configuring the SDK... But honestly not a strong preference, if you prefer it to be inside Purchases, I'm ok moving it. As you said, it keeps everything into a single entry point.
There was a problem hiding this comment.
Yea I think my preference would be to move the API into the Purchases class. Purchases can be (made into) a super thin facade if we care about bloating it, but I think it helps a developer if there's just 1 entry point into the RevenueCat SDK.
There was a problem hiding this comment.
For my understanding (again, sorry haha): I know we talked about this, but why do we want to support calling this before the SDK is configured again? It might be simpler to just make Purchases.sharedInstance.handleDeepLink() the public API for this, but I'm probably overlooking something.
There was a problem hiding this comment.
There are some cases where developers might not be configuring the SDK until later, for example, when a user signs up/signs in. In those cases, I think it's good we cache the deep link and process it once the SDK is actually configured (and the listener set for web redemptions)
There was a problem hiding this comment.
What do you think about requiring the SDK to be configured for now? We can relatively easily build the caching later, if people end up asking for it and the product is more fleshed out. Would allow us to remove some complexity from the PoC.
There was a problem hiding this comment.
Maybe this function could be dispatchResult()? Also, why does this need to be called on the main thread? (You guessed it: just for my understanding 😅)
There was a problem hiding this comment.
Well, the result from the Backend operation will always be in our background dispatcher which is used internally. Seemed like a bad option to use that, so thought to just return in the main thread by default for now... We could also try to return in the same thread as the startRedemption was called, but that seemed more work.
There was a problem hiding this comment.
Alright, makes sense!
Oh, sorry for being unclear. I see you renamed dispatch to dispatchResult, but I actually meant to rename handleResult to dispatchResult. 🙈
There was a problem hiding this comment.
I wondered about making this a parameter for the SDK configuration… but since this will need to be something that handles the UI for redeeming web purchases, it seemed more flexible to be able to set it later, but lmk what you think!
ca4b6fa to
810da26
Compare
JayShortway
left a comment
There was a problem hiding this comment.
Very nice! Basically 2 open points left for me:
- Some ideas to simplify the API.
- Maybe we can require the SDK to be configured, for now.
Happy to have a broader discussion about this if you prefer!
There was a problem hiding this comment.
Parameter naming suggestion:
- fun handleWebPurchaseRedemption(startRedemption: WebPurchaseRedeemer)
+ fun handleWebPurchaseRedemption(redeemer: WebPurchaseRedeemer)There was a problem hiding this comment.
Yea I do agree this shouldn't be a lambda for Java's sake! I think I'd prefer this being a final class with an internal constructor. That minimizes the API surface. The internal code organization can be tackled in a few ways, but I think the API surface is more important.
There was a problem hiding this comment.
| interface ResultListener { | |
| fun interface ResultListener { |
This allows for SAM conversion, meaning Kotlin consumers can call this as if it were a lambda, avoiding the need for an anonymous object.
E.g. in the sample MainApplication, we can then make this change:
- webPurchaseRedeemer.redeemWebPurchase(object : RedeemWebPurchaseListener.ResultListener {
- override fun handleResult(result: RedeemWebPurchaseListener.Result) {
- // ...
- }
- })
+ webPurchaseRedeemer.redeemWebPurchase { result ->
+ // ...
+ }There was a problem hiding this comment.
What do you think about requiring the SDK to be configured for now? We can relatively easily build the caching later, if people end up asking for it and the product is more fleshed out. Would allow us to remove some complexity from the PoC.
There was a problem hiding this comment.
Alright, makes sense!
Oh, sorry for being unclear. I see you renamed dispatch to dispatchResult, but I actually meant to rename handleResult to dispatchResult. 🙈
There was a problem hiding this comment.
Yea I think my preference would be to move the API into the Purchases class. Purchases can be (made into) a super thin facade if we care about bloating it, but I think it helps a developer if there's just 1 entry point into the RevenueCat SDK.
There was a problem hiding this comment.
I understand the scenarios we'd like to cover. I'm a bit concerned about the long chain of callbacks here. We have:
onCreate()/onNewIntent(), in whichhandleDeepLink()is called.handleWebPurchaseRedemption(), in whichredeemWebPurchase()is called.handleResult(), containing the result of the redemption.
It's hard to follow the flow of the code through all these callbacks, if a developer is trying to reason about their own web redemption code. It possibly involves going through multiple files to see what each of these callbacks is doing.
I have 2 alternatives in mind, mostly intended to challenge your assumptions haha.
Idea 1: no RedeemWebPurchaseListener
One idea I had is to remove RedeemWebPurchaseListener.handleWebPurchaseRedemption() from the equation. We could define an instance method, e.g. in Purchases, along the lines of:
fun getWebPurchaseRedeemerOrNull(intent: Intent): RedeemWebPurchaseListener.WebPurchaseRedeemer?This will return null if the intent is not a web purchase intent. This can then be used like so:
override fun onNewIntent(intent: Intent) {
Purchases.sharedInstance
.getWebPurchaseRedeemerOrNull(intent)
?.redeemWebPurchase { result -> }
}Devs can keep a hold of the WebPurchaseRedeemer, or the Intent, if they don't want to redeem the purchase right away.
Idea 2: no RedeemWebPurchaseListener & no WebPurchaseRedeemer
To simplify even further, we could remove the concept of a WebPurchaseRedeemer. We could define an instance method in Purchases along the lines of:
fun redeemWebPurchase(intent: Intent, resultListener: ResultListener)The ResultListener would return an error result if the intent is not a web purchase intent. (If devs want to check whether the intent is a web purchase intent before calling redeemWebPurchase(), we can add a method isWebPurchaseRedemptionIntent() that they can use.)
This can then be used like so:
override fun onNewIntent(intent: Intent) {
Purchases.sharedInstance.redeemWebPurchase(intent) { result -> }
}In this case, if devs don't want to redeem right away, they can keep a hold of the Intent, and call redeemWebPurchase() whenever they want.
Again, these are mostly intended to challenge your assumptions and see if there are opportunities to simplify. If we can get away with it, I like idea 2 best because it's by far the simplest and removes a lot of concepts for devs to understand.
…emory until configuration
… allow processing after setting the listener
0627571 to
b9d7d11
Compare
|
I'm closing this PR since looks like we will end up going with the approach in #1889 |
Description
This is a PoC of what it would take to listen to deep links to be able to redeem RCBilling purchases in the app using custom schema urls and using an old aliasing endpoint. Note that it has quite a few things that are not final and need to change before merging.
This PoC includes 2 new APIs:
DeepLinkHandler.handleDeepLink(intent)to input deep links by the appRedeemWebPurchaseListenerto accept and process web redemption links