-
Notifications
You must be signed in to change notification settings - Fork 382
Add offer code redemption support to paywall buttons #5437
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
Conversation
- Add offerCode destination type to PaywallButtonComponent - Implement offer code redemption sheet handling in ButtonComponentView - Add offerCodeRedemptionSheet navigation destination to ButtonComponentViewModel - Invalidate customer info cache after offer code redemption 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
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.
Just a question but looking great!!
} | ||
} | ||
|
||
private func openCodeREDemptionSheet() { | ||
Purchases.shared.presentCodeRedemptionSheet() |
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.
Not part of this PR, since I see we were already calling Purchases.shared
when invalidating the customer info when opening a web paywall link... But I do think these methods should ideally belong to the view model, not to the view.
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.
Besides what Toni says, we should probably avoid calling Purchases.shared
directly and go through the existing PurchaseHandler
instance instead (the person working on the RC app will later thank us for this 😄).
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.
As talked with Josh privately, removing the Purchases.shared
here is more intricate than it looks. So it'll be tackled in a future PR (thank you Swift protocols for not allowing @Availability checks in your methods)
await self.updateCustomerInfo() | ||
} | ||
.onChange(of: scenePhase) { newPhase in | ||
// Used when Offer Code Redemption sheet dismisses |
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.
Correct me if I'm wrong but would this be called every time the app becomes active even if it didn't go through the offer code redemption sheet, but something else? I guess it's not a huge deal... but we should be careful about invalidating customer info too often IMO, since it could lead to a user temporarily losing entitlements momentarily. If you can confirm this would only happen on offer code redemption sheet dismissal, I'm ok with this :)
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.
Good point from Toni here. The code currently only checks when the scenePhase
passes to active
. Perhaps we could restrict this to make it closer to the "when Offer Code Redemption sheet dismisses" event by using https://developer.apple.com/documentation/swiftui/view/onchange(of:initial:_:)-4psgg and only do updateCustomerInfo()
if newPhase == .active && oldPhase == .inactive
(this way we avoid calling it when coming from the .background
)
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.
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.
GOT IT
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.
I have a couple of more comments, building on Toni's
} | ||
} | ||
|
||
private func openCodeREDemptionSheet() { | ||
Purchases.shared.presentCodeRedemptionSheet() |
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.
Besides what Toni says, we should probably avoid calling Purchases.shared
directly and go through the existing PurchaseHandler
instance instead (the person working on the RC app will later thank us for this 😄).
RevenueCatUI/Templates/V2/Components/Button/ButtonComponentView.swift
Outdated
Show resolved
Hide resolved
await self.updateCustomerInfo() | ||
} | ||
.onChange(of: scenePhase) { newPhase in | ||
// Used when Offer Code Redemption sheet dismisses |
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.
Good point from Toni here. The code currently only checks when the scenePhase
passes to active
. Perhaps we could restrict this to make it closer to the "when Offer Code Redemption sheet dismisses" event by using https://developer.apple.com/documentation/swiftui/view/onchange(of:initial:_:)-4psgg and only do updateCustomerInfo()
if newPhase == .active && oldPhase == .inactive
(this way we avoid calling it when coming from the .background
)
📸 Snapshot Test1 modified, 704 unchanged
🛸 Powered by Emerge Tools |
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.
Let's goo!
(just one small nit)
if old == new { | ||
action(.new(new)) | ||
} else { | ||
action(.changed(old: old, new: new)) | ||
} |
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.
Total nit, and I understand that it could be misleading to say changed
when old == new
, but that would be the fault of the system API calling onChange
when the value didn't change. This way, we don't lose the oldValue
information (and we wouldn't be getting the Customer Info below if both old
and new
were .active
if old == new { | |
action(.new(new)) | |
} else { | |
action(.changed(old: old, new: new)) | |
} | |
action(.changed(old: old, new: new)) |
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.
Oh yeah, good point! Will change this 👍
} | ||
} | ||
|
||
private func openCodeREDemptionSheet() { | ||
Purchases.shared.presentCodeRedemptionSheet() |
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.
As talked with Josh privately, removing the Purchases.shared
here is more intricate than it looks. So it'll be tackled in a future PR (thank you Swift protocols for not allowing @Availability checks in your methods)
Summary
offerCode
destination type toPaywallButtonComponent
with proper encoding/decodingButtonComponentView
to present the system offer code redemption sheetTest plan
offer_code
destination in JSON🤖 Generated with Claude Code