UI events for paywall component interactions#6523
Conversation
Generated by 🚫 Danger |
4 builds increased size
RevenueCat 1.0 (1)
|
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 214.0 kB |
| RevenueCatUI.PaywallViewController.PaywallViewController | ⬆️ 23.7 kB |
| Code Signature | ⬆️ 18.6 kB |
| RevenueCat.InternalAPI.InternalAPI | ⬆️ 18.4 kB |
| DYLD.Exports | ⬆️ 14.6 kB |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 177.1 kB (1.52%)
Total download size change: ⬆️ 59.8 kB (1.54%)
Largest size changes
| Item | Install Size Change |
|---|---|
| RevenueCat.PurchasedTransactionDataEncodedWrapper.value witness | ⬆️ 11.2 kB |
| RevenueCat.LocalTransactionMetadata.value witness | ⬆️ 11.1 kB |
| RevenueCat.PaywallEvent.value witness | ⬆️ 8.8 kB |
| RevenueCat.PurchasesOrchestrator.CachedPurchaseContext.value witn... | ⬆️ 7.0 kB |
| RevenueCat.PurchasedTransactionData.value witness | ⬆️ 7.0 kB |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 272.9 kB (1.05%)
Total download size change: ⬆️ 76.5 kB (1.29%)
Largest size changes
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 39.4 kB |
| DYLD.String Table | ⬆️ 33.8 kB |
| RevenueCat.PurchasedTransactionDataEncodedWrapper.value witness | ⬆️ 11.2 kB |
| RevenueCat.LocalTransactionMetadata.value witness | ⬆️ 11.1 kB |
| RevenueCat.PaywallEvent.value witness | ⬆️ 8.8 kB |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 172.1 kB (1.69%)
Total download size change: ⬆️ 63.7 kB (1.59%)
Largest size changes
| Item | Install Size Change |
|---|---|
| RevenueCat.PurchasedTransactionDataEncodedWrapper.value witness | ⬆️ 11.2 kB |
| RevenueCat.LocalTransactionMetadata.value witness | ⬆️ 11.1 kB |
| RevenueCat.PaywallEvent.value witness | ⬆️ 8.8 kB |
| RevenueCat.PurchasesOrchestrator.CachedPurchaseContext.value witn... | ⬆️ 7.0 kB |
| RevenueCat.PurchasedTransactionData.value witness | ⬆️ 7.0 kB |
🛸 Powered by Emerge Tools
Comment trigger: Size diff threshold of 100.00kB exceeded
JZDesign
left a comment
There was a problem hiding this comment.
Great start, Still working through it, but here are my initial thoughts
….com:RevenueCat/purchases-ios into monika/UI-events/paywall-control-interaction
…allModifiers (PWENG-15)
| .onChangeOf(tabControlContext.selectedTabId) { newSelectedTabId in | ||
| let newIsOn = computeIsOn( | ||
| selectedTabId: newSelectedTabId, | ||
| tabIds: tabControlContext.tabIds | ||
| ) | ||
| if self.isOn != newIsOn { | ||
| self.isOn = newIsOn | ||
| } |
There was a problem hiding this comment.
I think this is still needed
There was a problem hiding this comment.
We don’t need it anymore because the toggle no longer keeps a separate @State copy of “on vs off.” selectedTabId (via TabControlContext) is the only source of truth, and the control uses a Binding whose getter derives isOn from selectedTabId and tabIds. When something else updates selectedTabId, @published triggers a view update, SwiftUI re-reads that getter, and the toggle reflects the new state without us manually assigning into local state
There was a problem hiding this comment.
I think you were the author of this stuff for various reasons that I can't recall now
There was a problem hiding this comment.
Just confirming here as well that it does not regress the behavior, all the flows behave the same.
….com:RevenueCat/purchases-ios into monika/UI-events/paywall-control-interaction # Conflicts: # RevenueCatUI/Templates/V2/Components/Tabs/TabControlToggleComponentView.swift
JZDesign
left a comment
There was a problem hiding this comment.
Blocking until we:
- test that tab selection logic
- Discuss the open thread we have in slack about URLs
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f94fcb7. Configure here.
| ) { | ||
| self.componentInteractionLogger(.paywallPurchaseButtonAction( | ||
| componentName: self.viewModel.componentName, | ||
| componentValue: self.viewModel.method?.description ?? "", |
There was a problem hiding this comment.
Empty component_value for web purchase button interaction
Low Severity
In logPurchaseButtonInteractionForWeb, componentValue falls back to "" when self.viewModel.method is nil, producing an empty string in the analytics event. The parallel logPurchaseButtonInteractionForInApp has a meaningful fallback of PaywallComponent.PurchaseButtonComponent.Method.inAppCheckout.description. Since component_value is a non-optional field that's always serialized into the paywall_component_interacted payload, a "" value will silently appear in integration pipelines rather than being filtered or flagged, making it harder to diagnose misconfigured components.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit f94fcb7. Configure here.






Checklist
purchases-androidand hybridsMotivation
This PR is part of the “Posting UI Events to Integrations” initiative and focuses on enabling
paywall_component_interactionon iOS so UI behavior events can flow through the existing paywall event pipeline and be forwarded to integrations without app-side callback wiring.It addresses the current gap where UI events exist but are not consistently surfaced in integration-friendly payloads. This is especially important for the upcoming Campaigns/Workflows/Checkpoints direction and for high-integration customers (e.g. Leadtech).
Resolves: PWENG-15
Description
This PR adds iOS support for
paywall_control_interactedand wires control interaction metadata end-to-end through RevenueCat + RevenueCatUI.What was added
paywall_control_interactioncomponent_typecomponent_namecomponent_valueorigin_package_identifierdestination_package_identifierdefault_package_identifierorigin_product_identifierdestination_product_identifierdefault_product_identifiercurrent_package_identifierresulting_package_identifiercurrent_product_identifierresulting_product_identifiertab,switch(wire value),carousel,button,package,package_selection_sheetPaywallEventTracker+componentInteractionLoggerenvironment wiring.UI coverage included
on/off)open/close)Semantics used
component_namenamewhen available (no ID exposure)namefrom JSONall_plans_button,restore_button,terms_link,privacy_link, etc.)component_valuerestore_purchases,navigate_to_terms,navigate_to_privacy_policy,toggle_all_plans)on/offopen/closeorigin*/destination*/default*current*:open: root paywall selection at sheet presentationclose: sheet-context selection at dismissresulting*:close: root paywall selection after dismiss (captures reset/revert behavior)Note
Medium Risk
Touches paywall analytics/event emission and session lifecycle across UI and core SDK, which could affect event deduping/ordering or missing/duplicate close events if session state is mishandled.
Overview
Adds a new
paywall_component_interactedpaywall event and maps its payload into hybrid/integration feature event fields (component type/name/value/url plus index/context and package/product identifiers).Introduces
PaywallEventTrackerand acomponentInteractionLoggerenvironment value to manage per-paywall session state and emit interaction events from RevenueCatUI (V1 + V2 controls like package selection, tabs/toggles, carousel swipes, buttons/links, markdown links, and package-selection sheet open/close). Paywall session IDs are now stored per view/session and reused across impression/close/interactions, and close tracking/reset paths inPurchaseHandler/UIKit paywall dismissal are updated accordingly.Extends paywall component models to carry optional
namefields (e.g. stack/button/purchase button/package/carousel) and adjusts previews/tests/mocks to support the new tracker and event dispatching behavior.Reviewed by Cursor Bugbot for commit f94fcb7. Bugbot is set up for automated code reviews on this repo. Configure here.