Skip to content

feat(paywalls): web_view bridge session with document-reset lifecycle (5/7)#7231

Merged
ajpallares merged 34 commits into
mainfrom
webview/5-session
Jul 21, 2026
Merged

feat(paywalls): web_view bridge session with document-reset lifecycle (5/7)#7231
ajpallares merged 34 commits into
mainfrom
webview/5-session

Conversation

@JZDesign

@JZDesign JZDesign commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Checklist

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

Motivation

Part 5 of 7 splitting #7183 (PWENG-99); inert until Part 7. Lands the security-sensitive WKWebView message bridge, retargeted onto main now that Parts 1–3 (#7227, #7228, #7229) are merged.

Description

  • Adds WebViewSession, the WKScriptMessageHandler handshake bridge (resize/fit auto-reply) plus a document-reset lifecycle (resetForNewDocument / onDocumentReset) so a committed main-frame navigation can re-handshake instead of staying stuck behind channelOpen.
  • Inbound frames are gated on the sender frame's securityOrigin (parity with Android's sourceOrigin in Make PaywallViewController open #3798); the WebView's top-level URL is only an outbound defense-in-depth check.

Remaining stack: #7230 (4/7), #7232 (6/7), #7233 (7/7).

cursoragent and others added 5 commits July 15, 2026 20:18
Add PaywallWebViewMessage/Value/Controller/Action types and API baselines.
Controller post APIs are no-ops until the session PR wires WebKit.

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Add WebViewEnvelope encode/decode and input hardening tests.
Inert until Part 7; builds on the public API from Part 1.

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
…3/7)

Extract origin normalization, main-frame navigation policy, and WK content
rule isolation as inert helpers used by later session/view PRs.

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
… (5/7)

Add WebViewSession (using WebViewOrigin), restore controller session wiring,
and document-reset so reloads can re-handshake. Includes session tests plus
reconnect/resize-threshold/onDocumentReset coverage.

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
@emerge-tools

emerge-tools Bot commented Jul 15, 2026

Copy link
Copy Markdown

4 builds increased size

Name Version Download Change Install Change Approval
RevenueCat
com.revenuecat.PaywallsTester
1.0 (1) 19.3 MB ⬆️ 27.6 kB (0.14%) 69.6 MB ⬆️ 71.0 kB (0.1%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.local-source
1.0 (1) 4.5 MB ⬆️ 18.3 kB (0.41%) 13.5 MB ⬆️ 44.8 kB (0.34%) ⏳ Needs approval
BinarySizeTest
com.revenuecat.binary-size-test.cocoapods
1.0 (1) 6.8 MB ⬆️ 27.5 kB (0.41%) 29.5 MB ⬆️ 82.3 kB (0.28%) ⏳ Needs approval
BinarySizeTest
com.revenuecat.binary-size-test.spm
1.0 (1) 4.6 MB ⬆️ 18.5 kB (0.4%) 11.7 MB ⬆️ 34.9 kB (0.3%) ⏳ Needs approval

RevenueCat 1.0 (1)
com.revenuecat.PaywallsTester

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 71.0 kB (0.1%)
Total download size change: ⬆️ 27.6 kB (0.14%)

Largest size changes

Item Install Size Change
📝 RevenueCatUI.WebViewSession.Objc Metadata ⬆️ 18.9 kB
DYLD.String Table ⬆️ 18.5 kB
🗑 RCPurchases.logOutWithCompletion: ⬇️ -13.3 kB
RCPurchases.Objc Metadata ⬆️ 13.3 kB
📝 RevenueCatUI.WebViewSession.WebViewSession ⬆️ 2.9 kB
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 44.8 kB (0.34%)
Total download size change: ⬆️ 18.3 kB (0.41%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 2.9 kB
📝 RevenueCatUI.WebViewEnvelope.Envelope.init(from) ⬆️ 1.7 kB
RevenueCatUI.TemplateViewConfiguration.filter(packages,with) ⬇️ -1.5 kB
DYLD.Lazy Bind ⬆️ 1.5 kB
RevenueCatUI.TemplateViewConfiguration.PackageConfiguration.__der... ⬆️ 1.4 kB
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 82.3 kB (0.28%)
Total download size change: ⬆️ 27.5 kB (0.41%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 26.0 kB
Code Signature ⬆️ 2.4 kB
📝 RevenueCatUI.WebViewEnvelope.Envelope.init(from) ⬆️ 1.8 kB
RevenueCatUI.TemplateViewConfiguration.filter(packages,with) ⬇️ -1.5 kB
DYLD.Lazy Bind ⬆️ 1.5 kB
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 34.9 kB (0.3%)
Total download size change: ⬆️ 18.5 kB (0.4%)

Largest size changes

Item Install Size Change
📝 RevenueCatUI.WebViewEnvelope.Envelope.init(from) ⬆️ 1.7 kB
RevenueCatUI.TemplateViewConfiguration.filter(packages,with) ⬇️ -1.5 kB
RevenueCatUI.TemplateViewConfiguration.PackageConfiguration.__der... ⬆️ 1.4 kB
📝 RevenueCatUI.WebViewEnvelope.Envelope.__derived_struct_equals ⬆️ 1.4 kB
📝 RevenueCatUI.WebViewSession.send(allowBeforeNavigation) ⬆️ 1.3 kB
View Treemap

Image of diff


🛸 Powered by Emerge Tools

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Comment thread RevenueCatUI/Templates/V2/Components/WebView/WebViewSession.swift
cursoragent and others added 8 commits July 16, 2026 20:01
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
# Conflicts:
#	RevenueCatUI/Templates/V2/Components/WebView/PaywallWebViewAPI.swift

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Removes the message-handler concept from the session: no app-facing
delivery, controller, or app-provided variable posting. The wire bridge
(handshake, resize, variables auto-reply, origin gating) is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
return
}
} else if let payload = envelope.payload {
guard WebViewEnvelope.reservedPayloadKeys.isDisjoint(with: payload.keys) else {

@ajpallares ajpallares Jul 20, 2026

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'm not sure we really need reservedPayloadKeys at all. I removed it from PR#2 (#7228) in 306e38d and we'll revisit here in this PR.

My initial intuition tells me that it shouldn't a problem to have the payload use the same key name as the Envelope itself, as they'd be used at different levels of the JSON anyway. But we'll see

@ajpallares ajpallares added pr:other and removed pr:feat A new feature labels Jul 20, 2026
Match Part 2: drop the unused `rc:*` message types (step-loaded, step-complete,
request-variables, variables, error) and their bridge handlers, keeping only
`resize`/`fit`. This also removes the now-dead SDK-variables/locale plumbing
and `reservedPayloadKeys`.

Move `receiveScript` (and its `receiveFunction` constant) from the envelope
data model into WebViewSession, where the sole consumer lives, with tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares self-assigned this Jul 21, 2026
Retarget Part 5 onto main now that Parts 1-3 (#7227, #7228, #7229) are merged.

Resolve conflicts by keeping main's canonical Part 1-3 files (PaywallWebViewValue,
WebViewEnvelope, WebViewOriginPolicy and their tests) and dropping stale
deps-123 artifacts (PaywallWebViewAPI.swift, WebViewIsolation.swift and its test,
plus the now-unused content_rules_failed log string). Part 5's own additions
(WebViewSession + tests, log strings) are preserved.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares
ajpallares changed the base branch from webview/deps-123 to main July 21, 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 f984e23. Configure here.

Comment thread RevenueCatUI/Templates/V2/Components/WebView/WebViewSession.swift Outdated
ajpallares and others added 3 commits July 21, 2026 11:57
Validate inbound frames against the sender frame's securityOrigin instead
of the WebView's top-level URL (parity with Android's sourceOrigin), keeping
the top-level URL only as an outbound defense-in-depth check. Normalize
expectedOrigin in init, drop the dead protocolVersion parameter in favor of
the fixed supported version, move receiveFunction onto WebViewEnvelope, and
log dropped frames at warning level. Adds origin-gating tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Jul 21, 2026

Copy link
Copy Markdown

⚠️ Warnings

This PR changes 370 lines of production Swift (limit 300); skipped via skip-pr-lines-changed-check label.

ajpallares and others added 4 commits July 21, 2026 12:12
…tensions

Replace WebViewOrigin.origin(of:) static helpers with a webViewOrigin
computed property on URL and WKSecurityOrigin, sharing a single private
canonicalization helper. Also drop the redundant nonisolated/assumeIsolated
on the script message handler since WebViewSession is already @mainactor.

Co-authored-by: Cursor <cursoragent@cursor.com>
…m guard

Co-authored-by: Cursor <cursoragent@cursor.com>
…port order

Co-authored-by: Cursor <cursoragent@cursor.com>
static let channel = "rc-web-components"
static let messageHandlerName = "rcWebComponents"
/// Name of the JS function injected into the web view that receives host-to-content frames.
static let receiveFunction = "__rcWebComponentsReceive"

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.

Moved it here to make it equal to Android (+ it makes more sense to me)

@ajpallares ajpallares Jul 21, 2026

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.

This file only contains a simple refactor to have getter webViewOrigin variables on URL and WKSecurityOrigin instead of two methods in WebViewOrigin. The WebViewOrigin enum is now a private namespace for the common canonicalOrigin method

ajpallares and others added 2 commits July 21, 2026 13:09
Normalize expectedOrigin to an optional canonical origin; when it cannot
be resolved, keep it nil and log a warning instead of silently trusting
the raw string, so the bridge rejects all inbound/outbound traffic and
the misconfiguration is diagnosable. Mirrors purchases-android.

Also splits the outbound skip log into distinct reasons (channel-not-open
vs untrusted-current-url), documents why handshake replies allow sending
before the top-level URL settles, and fixes a stale render-only test
comment.

Co-authored-by: Cursor <cursoragent@cursor.com>
…onnect

Pass only the protocol version into handleConnect (matching handleResize),
annotate the JS line-terminator escaping, and trim comment wording.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares
ajpallares requested a review from AlvaroBrey July 21, 2026 11:48
…inbound-drop assertions

Add coverage for component-id mismatch, unknown message type, missing
type, and the wrong-channel decode filter. Rework the existing
inbound-drop tests to assert against an onContentResize sink with a
real payload instead of captured outbound scripts, so a resize that
should be dropped is distinguishable from a no-op and the tests fail
if their gate is removed.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares

Copy link
Copy Markdown
Member

@RCGitBot please test

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

Nice! :shipit:

@ajpallares
ajpallares merged commit 6bddd1b into main Jul 21, 2026
45 checks passed
@ajpallares
ajpallares deleted the webview/5-session branch July 21, 2026 14:17
This was referenced Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants