Skip to content

Expose trackCustomPaywallImpression as experimental public API#6427

Merged
rickvdl merged 13 commits into
mainfrom
rickvdl/track-custom-paywall-impression-experimental-api
Mar 12, 2026
Merged

Expose trackCustomPaywallImpression as experimental public API#6427
rickvdl merged 13 commits into
mainfrom
rickvdl/track-custom-paywall-impression-experimental-api

Conversation

@rickvdl

@rickvdl rickvdl commented Mar 9, 2026

Copy link
Copy Markdown
Member

Description

Exposes trackCustomPaywallImpression as an experimental public API, allowing hybrid SDKs and consumers to track impressions of custom (non-RevenueCat) paywalls.

Changes

  • New CustomPaywallImpressionParams class gated behind @_spi(Experimental), @objc-compatible
  • Public non-async trackCustomPaywallImpression method on Purchases (fire-and-forget, wraps in Task {})
  • Swift and ObjC API testers

Related PRs


Note

Medium Risk
Introduces a new experimental public/ObjC API on Purchases, which changes the SDK’s public surface and event-tracking call pattern (fire-and-forget Task) and could affect analytics behavior if misused.

Overview
Exposes experimental custom paywall impression tracking to SDK consumers by adding CustomPaywallImpressionParams (ObjC-compatible) and making Purchases.trackCustomPaywallImpression a public @objc fire-and-forget API, plus a no-params convenience overload.

Removes the previously internal CustomPaywallEvent.Params type, updates unit tests accordingly, and adds Swift/ObjC API tester coverage to validate the new API surface. Project configuration is updated to include the new source/test files.

Written by Cursor Bugbot for commit 5fdd54f. This will update automatically on new commits. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Missing Sendable conformance on CustomPaywallImpressionParams
    • Added explicit Sendable conformance to CustomPaywallImpressionParams so it can be safely captured in the Task used by trackCustomPaywallImpression.

Create PR

Or push these changes by commenting:

@cursor push fdeb3da1f3
Preview (fdeb3da1f3)
diff --git a/Sources/Paywalls/Events/CustomPaywallEvent.swift b/Sources/Paywalls/Events/CustomPaywallEvent.swift
--- a/Sources/Paywalls/Events/CustomPaywallEvent.swift
+++ b/Sources/Paywalls/Events/CustomPaywallEvent.swift
@@ -78,7 +78,7 @@
 
 /// Parameters for tracking a custom paywall impression event.
 @_spi(Experimental) @objc(RCCustomPaywallImpressionParams)
-public final class CustomPaywallImpressionParams: NSObject {
+public final class CustomPaywallImpressionParams: NSObject, Sendable {
 
     /// An optional identifier for the custom paywall being shown.
     @objc public let paywallId: String?
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Comment thread Sources/Paywalls/Events/CustomPaywallEvent.swift Outdated

@ajpallares ajpallares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I have some initial comments

Comment thread Sources/Paywalls/Events/CustomPaywallEvent.swift
Comment thread Sources/Paywalls/Events/CustomPaywallEvent.swift Outdated
Comment thread Sources/Paywalls/Events/CustomPaywallEvent.swift Outdated
Comment thread Sources/Purchasing/Purchases/Purchases.swift
Comment thread Sources/Purchasing/Purchases/Purchases.swift Outdated
Comment thread Sources/Paywalls/Events/CustomPaywallEvent.swift
@rickvdl rickvdl force-pushed the rickvdl/track-custom-paywall-impression-experimental-api branch from caf32cb to 65b09d4 Compare March 11, 2026 11:34
@rickvdl rickvdl added pr:feat A new feature and removed pr:other labels Mar 11, 2026
@rickvdl rickvdl force-pushed the rickvdl/track-custom-paywall-impression-experimental-api branch from daa0fb6 to 6c6c10a Compare March 11, 2026 15:39
@rickvdl rickvdl requested a review from ajpallares March 11, 2026 16:12

@ajpallares ajpallares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing job! I have some comments, the most important one being a file commited that I believe is not related. But looks great otherwise!

.init(paywallId: params.paywallId)
)
await self.eventsManager?.track(featureEvent: event)
@objc public func trackCustomPaywallImpression(_ params: CustomPaywallImpressionParams) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a public API that consumers will likely call from SwiftUI's onAppear (which can fire multiple times during tab switches, sheet re-presentations, etc.), each invocation creates a brand new impression event with a fresh UUID and triggers a priority flush. There's nothing preventing duplicate impressions for the same paywall display.

Should we add doc comment guidance about when to call this (e.g., "Call once per paywall presentation, not in onAppear")?

I'm not sure, but I'm afraid we could be getting duplicate impressions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep that's indeed true. I've added a comment about this. LMK what you think :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the callout. It should help a bit avoid multiple impressions. Thank you!

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@rickvdl rickvdl force-pushed the rickvdl/track-custom-paywall-impression-experimental-api branch from e3877c9 to c6adc8c Compare March 12, 2026 06:19

@ajpallares ajpallares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you again! Looks great! 🚀

.init(paywallId: params.paywallId)
)
await self.eventsManager?.track(featureEvent: event)
@objc public func trackCustomPaywallImpression(_ params: CustomPaywallImpressionParams) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the callout. It should help a bit avoid multiple impressions. Thank you!

expect(data.paywallId).to(beNil())
}

func testTrackMultipleImpressionsInQuickSuccession() async throws {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding this!

@rickvdl rickvdl force-pushed the rickvdl/track-custom-paywall-impression-experimental-api branch from 7e1746d to 5fdd54f Compare March 12, 2026 09:35
@rickvdl rickvdl merged commit e4d10a0 into main Mar 12, 2026
36 checks passed
@rickvdl rickvdl deleted the rickvdl/track-custom-paywall-impression-experimental-api branch March 12, 2026 10:29
github-merge-queue Bot pushed a commit to RevenueCat/purchases-android that referenced this pull request Mar 12, 2026
## Description

Exposes `trackCustomPaywallImpression` as an experimental public API,
allowing hybrid SDKs and consumers to track impressions of custom
(non-RevenueCat) paywalls.

### Changes
- New `CustomPaywallEventParams` class gated behind
`@ExperimentalPreviewRevenueCatPurchasesAPI`
- Public `trackCustomPaywallImpression` method on `Purchases`
- API tester and Metalava API surface updates

### Related PRs
- purchases-ios: RevenueCat/purchases-ios#6427
- purchases-hybrid-common:
RevenueCat/purchases-hybrid-common#1537
- react-native-purchases:
RevenueCat/react-native-purchases#1660

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Introduces new public (experimental) API surface on `Purchases`, which
can affect binary compatibility and downstream integrations if the
signature or opt-in semantics change.
> 
> **Overview**
> Exposes `Purchases.trackCustomPaywallImpression` as an *experimental
public API* (with overloads) to let apps track impressions for
custom/non-RevenueCat paywalls, accepting optional `paywallId` via the
new `CustomPaywallImpressionParams` type.
> 
> Replaces the prior internal params type, updates API tester coverage
(Java/Kotlin) to exercise the new calls, and refreshes Metalava API
surface files and unit tests accordingly.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3896706. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
rickvdl added a commit to RevenueCat/purchases-hybrid-common that referenced this pull request Mar 12, 2026
## Description

Adds bridge methods for `trackCustomPaywallImpression` on both iOS and
Android, enabling hybrid SDKs to call the new experimental native API.

### Changes
- iOS: `trackCustomPaywallImpression(_ data:)` in
`CommonFunctionality.swift`
- Android: `trackCustomPaywallImpression(data)` in `common.kt`

Both methods accept a dictionary with an optional `paywallId` key and
delegate to the native SDK.

### Related PRs
- purchases-ios: RevenueCat/purchases-ios#6427
- purchases-android:
RevenueCat/purchases-android#3199
- react-native-purchases:
RevenueCat/react-native-purchases#1660
rickvdl added a commit to RevenueCat/purchases-unity that referenced this pull request Mar 17, 2026
## Summary
- Add `trackCustomPaywallImpression` API to the Unity SDK, wiring
through the existing hybrid-common bridge on both iOS and Android
- Introduce `CustomPaywallImpressionParams` class with an optional
`PaywallId`
- Pass `CustomPaywallImpressionParams` through the wrapper interface,
splitting into separate arguments only at the native bridge boundary
- Add a testing UI in the Subtester app's Paywall screen with an
optional paywall ID field

## Changes
- **New file:** `CustomPaywallImpressionParams.cs` — params class with
optional `PaywallId`
- **Interface:** `IPurchasesWrapper` — added
`TrackCustomPaywallImpression(CustomPaywallImpressionParams)`
- **Public API:** `Purchases.cs` — two overloads (with params,
parameterless)
- **iOS bridge:** `PurchasesWrapperiOS.cs` + `PurchasesUnityHelper.m` —
DllImport and ObjC bridge calling `[RCCommonFunctionality
trackCustomPaywallImpression:]`
- **Android bridge:** `PurchasesWrapperAndroid.cs` +
`PurchasesWrapper.java` — CallPurchases and static method calling
`CommonKt.trackCustomPaywallImpression()`
- **No-op:** `PurchasesWrapperNoop.cs` — empty stub
- **API tests:** `PurchasesAPITests.cs` — compile-time verification of
both overloads
- **Subtester:** `PaywallScreen.cs` — "Custom Paywall Events" section
with paywall ID field and button

### Related PRs
- purchases-ios: RevenueCat/purchases-ios#6427
- purchases-android:
RevenueCat/purchases-android#3199
- purchases-hybrid-common:
RevenueCat/purchases-hybrid-common#1537
- react-native-purchases:
RevenueCat/react-native-purchases#1660
- purchases-flutter:
RevenueCat/purchases-flutter#1679
- purchases-capacitor:
RevenueCat/purchases-capacitor#725

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Rick van der Linden <rick.vanderlinden@revenuecat.com>
rickvdl added a commit to RevenueCat/react-native-purchases that referenced this pull request Mar 17, 2026
## Summary
- Add `trackCustomPaywallImpression` API to React Native, enabling
developers to track impressions of custom (non-RevenueCat) paywalls for
analytics
- Add purchase tester demo screen for testing custom paywall impressions

## Changes
- **iOS bridge:** `RCT_EXPORT_METHOD(trackCustomPaywallImpression:)` in
`RNPurchases.m`
- **Android bridge:** `@ReactMethod trackCustomPaywallImpression` in
`RNPurchasesModule.java`
- **TypeScript:** `Purchases.trackCustomPaywallImpression()` in
`purchases.ts` with optional `paywallId` parameter
- **Purchase tester:** Added a demo Custom Paywall screen for testing
impressions

### Related PRs
- purchases-ios: RevenueCat/purchases-ios#6427
- purchases-android:
RevenueCat/purchases-android#3199
- purchases-hybrid-common:
RevenueCat/purchases-hybrid-common#1537
- purchases-flutter:
RevenueCat/purchases-flutter#1679
- purchases-capacitor:
RevenueCat/purchases-capacitor#725
rickvdl added a commit to RevenueCat/purchases-hybrid-common that referenced this pull request Mar 18, 2026
…PI (#1565)

## Summary
- Read `offeringId` from the data dictionary in
`trackCustomPaywallImpression` on both iOS and Android bridge layers
- Pass it through to the native `CustomPaywallImpressionParams`,
allowing hybrid SDKs to override the default offering identifier

## Related PRs
- purchases-ios: RevenueCat/purchases-ios#6427
- purchases-android:
RevenueCat/purchases-android#3199
- react-native-purchases:
RevenueCat/react-native-purchases#1660
- purchases-unity:
RevenueCat/purchases-unity#863
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants