Skip to content

feat(paywalls): decode top-level state declaration map#6989

Merged
MonikaMateska merged 2 commits into
mainfrom
pw-state/1-declaration
Jun 16, 2026
Merged

feat(paywalls): decode top-level state declaration map#6989
MonikaMateska merged 2 commits into
mainfrom
pw-state/1-declaration

Conversation

@MonikaMateska

@MonikaMateska MonikaMateska commented Jun 14, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-android and hybrids

Motivation

First PR in the state-driven paywalls Phase 0 stack (iOS, PWENG-56). Adds the top-level state map so the SDK can decode declared state keys (type + default) that seed the presentation-session store in later PRs. Strictly additive and decode-only.
Spec: https://github.com/RevenueCat/sdk-specs/tree/main/openspec/changes/add-paywall-component-state

Description

Decodes the optional top-level state declaration on PaywallComponentsData into StateDeclaration values. Decoding is resilient: a malformed entry is dropped individually and a malformed map is ignored, so it can never fail the whole paywall. All types are @_spi(Internal).

Stack: 1/5 → base main.


Note

Low Risk
Additive, internal SPI decoding with no runtime state-store or UI behavior yet; malformed state is dropped without failing paywall loads.

Overview
Adds decode-only support for the optional top-level state map on paywall component JSON, as the first step toward state-driven paywalls.

Introduces PaywallComponent.StateDeclaration (wire type + default as existing ConditionValue, plus normalizedDefaultValue for double keys with integral JSON literals). PaywallComponentsData gains stateDeclarations, mapped from JSON key state, with resilient decoding: malformed entries are skipped, a bad map is ignored, and empty/missing/all-failed maps become nil so paywall decode never fails on state. Encoding round-trips the map. New unit tests cover declarations and PaywallComponentsData edge cases. Package.resolved also picks up additional SPM pins (e.g. snapshot/preview tooling).

Reviewed by Cursor Bugbot for commit 6977334. Bugbot is set up for automated code reviews on this repo. Configure here.

@MonikaMateska MonikaMateska changed the title feat(paywalls): decode top-level state declaration map feat(paywalls): PR 1 decode top-level state declaration map Jun 14, 2026
@MonikaMateska MonikaMateska force-pushed the pw-state/1-declaration branch from 8f8b112 to 9fbd9f3 Compare June 14, 2026 15:55
@MonikaMateska MonikaMateska changed the title feat(paywalls): PR 1 decode top-level state declaration map feat(paywalls): decode top-level state declaration map Jun 15, 2026
@MonikaMateska MonikaMateska marked this pull request as ready for review June 15, 2026 17:19
@MonikaMateska MonikaMateska requested a review from a team as a code owner June 15, 2026 17:19
@MonikaMateska MonikaMateska force-pushed the pw-state/1-declaration branch from 0fe842c to fd6e0ad Compare June 16, 2026 08:09

@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.

Fix All in Cursor

❌ 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 b5a745b. Configure here.

Comment thread Sources/Networking/Responses/RevenueCatUI/PaywallComponentsData.swift Outdated
@MonikaMateska MonikaMateska force-pushed the pw-state/1-declaration branch from b5a745b to ca873a9 Compare June 16, 2026 08:41

@alexrepty alexrepty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One nitpick, and one idea: since we're decoding defensively and guarding for errors, missing values and such, should we also guard against type mismatch?

I think it's fine without these things though, so please feel free to disregard.

Comment thread Sources/Networking/Responses/RevenueCatUI/PaywallComponentsData.swift Outdated
facumenzella and others added 2 commits June 16, 2026 16:29
…low (#7002)

* fix(paywalls): skip workflow fetch for offerings with no mapped workflow

Under workflows-enabled, presenting a paywall for an offering with only a
legacy paywall (no workflow on the backend) fired a workflow fetch that 404'd
and surfaced an error state instead of rendering the legacy paywall. Gate the
workflow path on the offeringId -> workflowId map: no mapping renders the
legacy paywall with no network call. Offerings are fetched first so the map is
populated before the decision, avoiding a cold-cache downgrade of real workflow
offerings.

Port of RevenueCat/purchases-android#3598

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Gate legacy-vs-workflow on offering.paywall, not the workflows map

Aligns with the merged purchases-android #3598: route an offering to the
workflows endpoint only when `offering.paywall == nil` (the durable marker of a
non-legacy paywall), instead of checking the offeringId -> workflowId map. The
map check was an intermediate approach the Android PR abandoned: an offering not
yet migrated to workflows is missing from the map, which would wrongly fall back
to legacy for a real workflow offering. Reading offering.paywall is authoritative
and removes the timing dependency on the map.

Drops the cachedWorkflowId plumbing (no longer needed; Purchases.workflow already
resolves the workflow id with an offering-id fallback) and the now-unused warning
string. Applies the same gate to the synchronous cache seed for consistency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MonikaMateska MonikaMateska force-pushed the pw-state/1-declaration branch from 5c8032b to 6977334 Compare June 16, 2026 14:44
@MonikaMateska MonikaMateska merged commit 74473b8 into main Jun 16, 2026
44 of 45 checks passed
@MonikaMateska MonikaMateska deleted the pw-state/1-declaration branch June 16, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants