Add presented offering context to custom paywall events#6707
Conversation
40e2204 to
e2163fe
Compare
|
@RCGitBot please test |
|
@RCGitBot please test |
ca04e15 to
eb46543
Compare
|
@RCGitBot please test |
tonidero
left a comment
There was a problem hiding this comment.
I think this looks great!! Thank you!!
| @objc public init(paywallId: String? = nil, offeringId: String?) { | ||
| self.paywallId = paywallId | ||
| self.offeringId = offeringId | ||
| self.offering = nil |
There was a problem hiding this comment.
Should this init be marked as deprecated? And/or spi(Internal)
There was a problem hiding this comment.
I think you reviewed this just before I pushed the changes taken from your Android feedback :) Updated now!
| /// When provided, the SDK will derive the presented offering context (placement and targeting | ||
| /// information) from this offering. If neither `offering` nor `offeringId` is provided, the SDK | ||
| /// will use the current offering from the cache. | ||
| @objc public let offering: Offering? |
There was a problem hiding this comment.
I wonder if we should store the PresentedOfferingContext directly, like we do in Android... Not sure if we need the whole offering for something else?
There was a problem hiding this comment.
Same for this one, this is updated now :)
9e90383 to
8ed2bf8
Compare
|
@RCGitBot please test |
tonidero
left a comment
There was a problem hiding this comment.
Nice! Thanks for fixing those hehe
|
@RCGitBot please test |
…ing-context-to-custom-paywall-events` (#6722) * Add presented offering context to custom paywall events * Update API testers for offering-based custom paywall impression init * Add tests for placement and targeting in custom paywall events * Accept Offering for custom paywall impression and derive context * Decouple custom paywall wire request from PaywallEvent's nested context type * Look up offeringId in cached offerings to derive presented offering context * Update baseline swiftinterface files --------- Co-authored-by: Rick van der Linden <rick.vanderlinden@revenuecat.com>
…onMetadataSyncHelper The syncIfNeeded call in Purchases.init races with the call from applicationWillEnterForeground: if the init task acquires isSyncing first (even with empty metadata), it blocks the foreground notification task from running. Removing the init call is safe because applicationWillEnterForeground fires reliably on startup and covers the sync need.
…ransactionMetadataSyncHelper" This reverts commit d977926.
…ad of Offering Matches the Android PR #3424 changes: - Replace `offering: Offering?` property with `@_spi(Internal) presentedOfferingContext` so the params object stores the derived context rather than the raw Offering - Add `@_spi(Internal)` designated init for hybrid SDK (PHC) passthrough of pre-resolved presentedOfferingContext without requiring an Offering object - Deprecate `init(paywallId:offeringId:)` in favour of `init(paywallId:offering:)` - Update `trackCustomPaywallImpression` resolution order to match Android: params.presentedOfferingContext → cached[offeringId] → cached.current - Update Swift and ObjC API testers accordingly
bb9525f to
fe395a8
Compare
|
@RCGitBot please test |
|
@RCGitBot please test |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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 c89d357. Configure here.
…ession events (#1665) Builds on native PRs RevenueCat/purchases-ios#6707 and RevenueCat/purchases-android#3424 Adds the `presentedOfferingContext` (optional) parameter support to the custom paywall impression event API. When present the value will be read from the passed map as `[string: any]` and parses it using existing helpers like in other places.
) ### Description Adds `placement_identifier`, `targeting_revision`, and `targeting_rule_id` to the custom paywall impression event. Mirrors the event payload work in iOS PR RevenueCat/purchases-ios#6707 for Android. The internal paywall events already received this context via RevenueCat#3253; this PR brings the same placement/targeting attribution to custom paywall events. ### What's new - `CustomPaywallImpressionParams` gains an `Offering`-based constructor. Passing an `Offering` lets the SDK derive the offering identifier and `PresentedOfferingContext` from the offering's first available package. - The params object stores `paywallId`, `offeringId`, and `presentedOfferingContext`; it does not store or expose the `Offering` object. This keeps the public params surface focused on the values needed for event tracking. - The string `offeringId` constructor is deprecated in favor of passing an `Offering`, because an offering identifier alone cannot reliably carry placement and targeting context. - An `@InternalRevenueCatAPI` constructor accepts `paywallId`, `offeringId`, and `presentedOfferingContext` directly, so hybrid SDKs can pass the context through PHC to native without exposing `Offering` on the public params API. - `Purchases.trackCustomPaywallImpression(params)` resolves event data with this fallback chain: | Developer passes | `offering_id` sent | context sent | | --- | --- | --- | | Nothing (or only `paywallId`) | from `cachedOfferings.current` | from current offering | | Deprecated `offeringId` matching cache | passed string | from cached match | | Deprecated `offeringId` not in cache | passed string | nil | | `Offering` object | from offering | derived from offering | | Internal `presentedOfferingContext` | passed `offeringId` | passed context | - `CustomPaywallEvent.Impression.Data` carries flat `placementIdentifier` / `targetingRevision` / `targetingRuleId` fields, mirroring the existing internal `PaywallEvent` Android shape. - The wire payload nests these under `presented_offering_context`. The custom paywall event has its own `BackendEvent.CustomPaywallPresentedOfferingContextData` rather than reusing the internal paywall event's struct, matching the iOS approach where each event owns its own. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Public API surface changes (new constructors, deprecation) and analytics event schema updates; behavior is localized to custom paywall tracking rather than purchases or auth. > > **Overview** > Custom paywall impression tracking now sends **placement and targeting context** (placement identifier, targeting revision, rule id) on the backend event, nested as `presented_offering_context`, aligning custom paywalls with internal paywall analytics. > > **`CustomPaywallImpressionParams`** adds constructors that take an **`Offering`** (or offering-only) so the SDK can set `offeringId` and `presentedOfferingContext` from the offering’s first package. The **`offeringId` string constructor is deprecated** in favor of `Offering`. Params expose `presentedOfferingContext` but not the offering object itself. > > **`Purchases.trackCustomPaywallImpression`** resolves what gets tracked: explicit context from params, lookup from **cached offerings** when only an offering id or nothing is passed, or values taken directly from a passed **`Offering`**. > > Wire and storage paths extend **`CustomPaywallEvent.Impression.Data`** and **`BackendEvent.CustomPaywall`** with the new fields; **`cachedOfferings`** is exposed on the orchestrator/offerings manager to support resolution. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ea3369c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->

Description
Adds `placementIdentifier`, `targetingRevision`, and `targetingRuleId` to the custom paywall impression event.
Mirrors #6476, which added the same fields for the internal paywall events. Android parity: RevenueCat/purchases-android#3424.
What's new
Note
Medium Risk
Public API deprecation and new analytics fields affect integrators and backend payloads, but behavior is backward compatible for legacy stored events and is covered by broad unit tests.
Overview
Custom paywall impression analytics now carry placement and targeting (placement identifier, targeting revision, rule id), aligned with internal paywall events and Android.
Public API:
CustomPaywallImpressionParamsaddsinit(paywallId:offering:)so the SDK can readPresentedOfferingContextfrom the offering’s first package. TheofferingIdstring initializer is deprecated in favor of passing anOffering. An@_spi(Internal)path lets hybrid SDKs supply pre-resolved context without anOffering.Runtime:
Purchases.trackCustomPaywallImpressionresolves offering id and context from explicit SPI context, a cached offering for a passed id, or the current cached offering (context nil if the id isn’t in cache).Events & wire:
CustomPaywallEvent.Datastores the new fields; hybridtoMap()and backend JSON include them (nestedpresented_offering_contexton upload, omitted when empty). Stored events without those keys still decode.API surface snapshots and unit/API tests cover encoding, resolution, and legacy payloads.
Reviewed by Cursor Bugbot for commit c89d357. Bugbot is set up for automated code reviews on this repo. Configure here.