Skip to content

feat(paywalls): web_view component view + view model, not yet wired (6/7)#7232

Merged
ajpallares merged 64 commits into
mainfrom
webview/6-view
Jul 22, 2026
Merged

feat(paywalls): web_view component view + view model, not yet wired (6/7)#7232
ajpallares merged 64 commits into
mainfrom
webview/6-view

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

Adds the web_view paywall component's SwiftUI view and view model. It renders a sandboxed WKWebView but is not yet wired into component rendering, so it has no runtime effect yet.

Description

  • Reserves the configured fit size while loading, then adopts the size reported by web content (measured wins; a fit axis without a default keeps the 300×100 fallback).
  • Navigation and the message bridge are gated to the URL's origin; a hidden component or an unresolvable/invalid URL renders nothing, matching purchases-android.

cursoragent and others added 9 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>
Add PaywallComponent.WebViewComponent as dead code without registering
ComponentType.webView or PaywallComponent.webView, so paywall JSON is
unchanged until the activation PR.

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>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
…6/7)

Add WebViewComponentView/ViewModel with document-reset and content-process
termination lifecycle, fit-axes identity, and coordinator tests. Still
unreferenced by the render pipeline until Part 7.

Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Jul 15, 2026

Copy link
Copy Markdown

⚠️ Warnings

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

cursoragent and others added 2 commits July 15, 2026 20:36
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
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 ⬆️ 49.6 kB (0.26%) 69.8 MB ⬆️ 182.6 kB (0.26%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.local-source
1.0 (1) 4.5 MB ⬆️ 3.7 kB (0.08%) 13.5 MB ⬆️ 8.9 kB (0.07%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.cocoapods
1.0 (1) 6.8 MB ⬆️ 4.8 kB (0.07%) 29.6 MB ⬆️ 21.3 kB (0.07%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.spm
1.0 (1) 4.6 MB ⬇️ 1.9 kB (-0.04%) 11.7 MB ⬆️ 8.6 kB (0.07%) N/A

RevenueCat 1.0 (1)
com.revenuecat.PaywallsTester

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 182.6 kB (0.26%)
Total download size change: ⬆️ 49.6 kB (0.26%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 39.5 kB
Code Signature ⬆️ 4.9 kB
RevenueCatUI.Coordinator.Objc Metadata ⬆️ 4.6 kB
DYLD.Exports ⬆️ 3.1 kB
External Methods ⬆️ 1.5 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: ⬆️ 8.9 kB (0.07%)
Total download size change: ⬆️ 3.7 kB (0.08%)

Largest size changes

Item Install Size Change
📝 WKNavigationDelegate.Objc Metadata ⬆️ 2.6 kB
DYLD.String Table ⬆️ 1.3 kB
📝 RevenueCatUI.WebViewNavigationPolicy.policy(for,isMainFrame,expec... ⬆️ 648 B
RevenueCatUI.Coordinator.Objc Metadata ⬆️ 642 B
📝 RevenueCatUI.WebViewComponentViewModel.Objc Metadata ⬆️ 548 B
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: ⬆️ 21.3 kB (0.07%)
Total download size change: ⬆️ 4.8 kB (0.07%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 6.1 kB
📝 WKNavigationDelegate.Objc Metadata ⬆️ 2.6 kB
📝 RevenueCatUI.WebViewNavigationPolicy.policy(for,isMainFrame,expec... ⬆️ 672 B
RevenueCatUI.Coordinator.Objc Metadata ⬆️ 642 B
📝 RevenueCatUI.WebViewComponentViewModel.Objc Metadata ⬆️ 548 B
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: ⬆️ 8.6 kB (0.07%)
Total download size change: ⬇️ 1.9 kB (-0.04%)

Largest size changes

Item Install Size Change
📝 WKNavigationDelegate.Objc Metadata ⬆️ 2.5 kB
📝 RevenueCatUI.WebViewNavigationPolicy.policy(for,isMainFrame,expec... ⬆️ 648 B
RevenueCatUI.Coordinator.Objc Metadata ⬆️ 619 B
📝 RevenueCatUI.WebViewComponentViewModel.Objc Metadata ⬆️ 548 B
📝 RevenueCatUI.WebViewRepresentable.Coordinator.webView(decidePolic... ⬆️ 536 B
View Treemap

Image of diff


🛸 Powered by Emerge Tools

Comment trigger: Size diff threshold of 100.00kB exceeded

Comment thread RevenueCatUI/Templates/V2/Components/WebView/WebViewComponentView.swift Outdated
Co-authored-by: Jacob Rakidzich <Jacob@JacobZivanDesign.com>
ajpallares and others added 5 commits July 21, 2026 12:14
…m guard

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

Co-authored-by: Cursor <cursoragent@cursor.com>
Follow-up to merging the refactored base into webview/6-view:
- Use the new `URL.webViewOrigin` extension instead of the now-private
  `WebViewOrigin.origin(of:)` in WebViewComponentView.
- Add the copyright header to WebViewComponentView/ViewModel to match
  the sibling web_view files.
@ajpallares ajpallares self-assigned this Jul 21, 2026
ajpallares and others added 8 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>
Brings in the updated base commit "treat unresolvable web_view origin as
inert bridge" (WebViewSession.expectedOrigin is now optional).

- Strings.swift: unioned the two sides — keep the new
  post_message_skipped(reason:) and invalid_expected_origin cases from the
  base, and retain content_process_terminated used by the Part 6 view.
- WebViewComponentView: pass session.expectedOrigin ?? "" to the
  representable so an unresolvable origin also cancels main-frame
  navigation, keeping the whole web view inert (not just the bridge).
…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>
…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>
- Drop unused `localizationProvider` from WebViewComponentViewModel.init
- Include `size` and `visible` in the view model's equality/hash so
  rendered-state differences aren't treated as equal
- Cache `url` validation via a lazy var
- Drop the redundant BridgedWebViewComponentView.componentID property
- Isolate the navigation-delegate Coordinator to @mainactor and drop the
  per-method MainActor.assumeIsolated wrappers
- Add view-model equality/visible tests and an empty-origin navigation
  policy case

Co-authored-by: Cursor <cursoragent@cursor.com>
Base automatically changed from webview/5-session to main July 21, 2026 14:17
ajpallares and others added 2 commits July 21, 2026 16:21
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	RevenueCat.xcodeproj/project.pbxproj
#	RevenueCatUI/Data/Strings.swift
…rget

The consolidated WebViewComponentTests now imports RevenueCatUI, but the
xcodeproj UnitTests target only links RevenueCat, causing "Unable to find
module dependency: 'RevenueCatUI'". Remove it from that target's Sources so
it runs only via the SPM RevenueCatUITests target, matching its
RevenueCatUI-importing siblings (WebViewSessionTests, WebViewEnvelopeTests).

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

@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 2f1d7ca. Configure here.

Comment thread RevenueCat.xcodeproj/project.pbxproj
ajpallares and others added 2 commits July 21, 2026 17:03
WebViewComponentTests mixed RevenueCat schema (Codable) tests with
RevenueCatUI view-model/coordinator tests in a single file. Split them to
match the Video component convention:

- Tests/UnitTests/Paywalls/Components/WebViewComponentTests.swift: schema
  decoding tests (RevenueCat only), wired into the xcodeproj UnitTests
  target so they run under `fastlane scan` again.
- Tests/RevenueCatUITests/PaywallsV2/WebViewComponentViewTests.swift:
  view-model/sizing/coordinator tests (RevenueCatUI), SPM only.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares
ajpallares requested a review from AlvaroBrey July 21, 2026 15:43
The private makeWebView/update/configureSession helpers were non-isolated,
so mutating the @mainactor WebViewSession properties from them failed to
compile under Xcode 15 (Swift 5.9) in the SPM RevenueCatUI release build.
Annotate the helpers @mainactor to keep the main-actor chain intact.

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

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

Nothing blocking, just a nit and a question

Comment thread RevenueCatUI/Templates/V2/Components/WebView/WebViewComponentView.swift Outdated
Comment on lines +277 to +282
#if os(macOS)
webView.configuration.websiteDataStore.removeData(
ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(),
modifiedSince: .distantPast
) {}
#endif

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.

q: why only on mac?

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.

Good catch!
Tbh, I'm not sure. This was pre-existent and I couldn't find any reason why this would be needed, since we have a per-view nonPersistent() store, it doesn't write data to disk, it writes it in-memory, and the data is released when the web view + its configuration deallocates anyway.

I removed this for now in fb5e589. We can re-add it if needed.

@JZDesign do you know why this was done and only for macOS?

ajpallares and others added 3 commits July 22, 2026 09:09
The web view uses a per-view .nonPersistent() data store, which keeps data
in memory and releases it when the web view deallocates, so the macOS-only
removeData call on teardown was redundant.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares
ajpallares merged commit 03ef642 into main Jul 22, 2026
19 of 20 checks passed
@ajpallares
ajpallares deleted the webview/6-view branch July 22, 2026 08:07
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