Skip to content
Open
Show file tree
Hide file tree
Changes from 95 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
5cf1dfd
feat(paywalls): public API surface for web_view messages (1/7)
cursoragent Jul 15, 2026
f7f5d16
feat(paywalls): web_view wire envelope + JSON hardening (2/7)
cursoragent Jul 15, 2026
d331b34
feat(paywalls): web_view navigation policy + content-rule isolation (…
cursoragent Jul 15, 2026
8d2452a
feat(paywalls): web_view schema component, not yet registered (4/7)
cursoragent Jul 15, 2026
7664d35
Merge webview/3-isolation-navigation into webview/5-session base
cursoragent Jul 15, 2026
b033efa
feat(paywalls): web_view bridge session with document-reset lifecycle…
cursoragent Jul 15, 2026
6a46822
Merge webview/4-schema into webview/6-view base
cursoragent Jul 15, 2026
37aa810
feat(paywalls): web_view component view + view model, not yet wired (…
cursoragent Jul 15, 2026
c132a31
fix(tests): open session via connect in coordinator lifecycle test
cursoragent Jul 15, 2026
24f352c
fix: remove extra vertical whitespace for SwiftLint
cursoragent Jul 15, 2026
07383d3
fix: SwiftLint whitespace, line length, and force_try
cursoragent Jul 15, 2026
96d24e1
fix: wrap web_view identity string under 120 columns
cursoragent Jul 15, 2026
ea83431
feat(paywalls): enable web_view component (7/7)
cursoragent Jul 15, 2026
e671378
refactor(paywalls): keep web view bridge internal
cursoragent Jul 16, 2026
41401ac
refactor(paywalls): keep web view bridge internal
cursoragent Jul 16, 2026
b5caeda
refactor(paywalls): keep web view bridge internal
cursoragent Jul 16, 2026
ad3541f
refactor(paywalls): keep web view bridge internal
cursoragent Jul 16, 2026
8bc119c
refactor(paywalls): keep web view bridge internal
cursoragent Jul 16, 2026
d941c01
Merge updated internal web view API base
cursoragent Jul 16, 2026
c9f7142
Merge updated internal web view API stack
cursoragent Jul 16, 2026
111d7ea
Merge updated internal web view API dependencies
cursoragent Jul 16, 2026
850fe90
Merge updated internal web view session stack
cursoragent Jul 16, 2026
cf8b833
Merge updated internal web view session dependencies
cursoragent Jul 16, 2026
dc39e52
Merge updated internal web view component stack
cursoragent Jul 16, 2026
8c8904b
Merge updated internal web view component dependencies
cursoragent Jul 16, 2026
ca80c8f
Merge branch 'main' into webview/1-public-api
ajpallares Jul 17, 2026
eba0f50
Merge remote-tracking branch 'origin/main' into webview/6-view
cursoragent Jul 17, 2026
bba700f
feat(paywalls): apply fit defaults to web views
cursoragent Jul 17, 2026
94982da
fix(paywalls): remove client-side web view payload limits
cursoragent Jul 17, 2026
3ac5561
Merge branch 'main' of https://github.com//RevenueCat/purchases-ios i…
JZDesign Jul 17, 2026
ea6d24b
test(paywalls): address web view envelope review
cursoragent Jul 17, 2026
0612595
refactor(paywalls): remove unused web view handler API
cursoragent Jul 17, 2026
d14d63c
fix(paywalls): retain internal bridge dependencies
cursoragent Jul 17, 2026
19cbfe6
refactor(paywalls): remove web view message handler concept
JZDesign Jul 17, 2026
c3d27a9
Merge branch 'webview/1-public-api' into webview/2-envelope
JZDesign Jul 17, 2026
e3ad994
Merge updated internal web view API stack (handler removal)
JZDesign Jul 17, 2026
4671130
Merge updated internal web view API dependencies (handler removal)
JZDesign Jul 17, 2026
226eb28
Merge updated internal web view session stack (handler removal)
JZDesign Jul 17, 2026
3278b60
Merge updated internal web view session dependencies (handler removal)
JZDesign Jul 17, 2026
0130c59
Merge updated internal web view component stack (handler removal)
JZDesign Jul 17, 2026
5b09a1b
Merge updated internal web view component dependencies (handler removal)
JZDesign Jul 17, 2026
92f61e3
feat(paywalls): fall back on unsupported web_view protocol_version
ajpallares Jul 20, 2026
eb53f30
Adapt web view bridge to trimmed envelope API
ajpallares Jul 20, 2026
66b3e33
Merge branch 'main' into webview/5-session
ajpallares Jul 21, 2026
f984e23
Merge branch 'main' into webview/5-session
ajpallares Jul 21, 2026
86d9c0b
refactor(paywalls): harden web_view session origin gating
ajpallares Jul 21, 2026
ab69065
Merge remote-tracking branch 'origin/webview/5-session' into webview/…
ajpallares Jul 21, 2026
a48f936
Merge branch 'main' into webview/5-session
ajpallares Jul 21, 2026
7940b96
refactor(paywalls): expose web_view origin as URL/WKSecurityOrigin ex…
ajpallares Jul 21, 2026
b266987
Merge webview/5-session into webview/6-view and reconcile the view layer
ajpallares Jul 21, 2026
7280b5d
refactor(paywalls): consolidate web_view imports under single platfor…
ajpallares Jul 21, 2026
0a866f8
refactor(paywalls): add copyright header to web_view files and fix im…
ajpallares Jul 21, 2026
8ceb7d4
Merge branch 'main' into webview/5-session
ajpallares Jul 21, 2026
9c12c2b
Merge branch 'webview/5-session' into webview/6-view
ajpallares Jul 21, 2026
b9078c4
fix(paywalls): adopt web_view origin extensions and add file headers
ajpallares Jul 21, 2026
d3047ce
Merge remote-tracking branch 'origin/webview/6-view' into webview/7-a…
ajpallares Jul 21, 2026
07ecb4c
fix(paywalls): treat unresolvable web_view origin as inert bridge
ajpallares Jul 21, 2026
8948589
Merge webview/5-session into webview/6-view (inert-bridge origin fix)
ajpallares Jul 21, 2026
1baead1
Merge branch 'webview/6-view' into webview/7-activate
ajpallares Jul 21, 2026
2eb1308
refactor(paywalls): tidy web_view session comments and narrow handleC…
ajpallares Jul 21, 2026
cd147f7
Merge branch 'webview/5-session' into webview/6-view
ajpallares Jul 21, 2026
ff30323
Merge branch 'webview/6-view' into webview/7-activate
ajpallares Jul 21, 2026
53fde7e
test(paywalls): add missing web_view drop-branch coverage and harden …
ajpallares Jul 21, 2026
7761ba2
Merge branch 'webview/5-session' into webview/6-view
ajpallares Jul 21, 2026
75bfed0
Merge branch 'webview/6-view' into webview/7-activate
ajpallares Jul 21, 2026
07c6a31
refactor(paywalls): address web_view view review feedback
ajpallares Jul 21, 2026
d7d0c25
Merge branch 'webview/6-view' of https://github.com/RevenueCat/purcha…
ajpallares Jul 21, 2026
a8f4cfb
Merge remote-tracking branch 'origin/webview/6-view' into webview/7-a…
ajpallares Jul 21, 2026
52b61dd
refactor(paywalls): derive web_view protocol version from a single so…
ajpallares Jul 21, 2026
bc2ad31
Merge remote-tracking branch 'origin/main' into webview/6-view
ajpallares Jul 21, 2026
2f1d7ca
fix(paywalls): drop WebViewComponentTests from xcodeproj UnitTests ta…
ajpallares Jul 21, 2026
a32d717
refactor(paywalls): split web_view schema and view tests by target
ajpallares Jul 21, 2026
cedc048
refactor(paywalls): introduce WebViewOrigin value type for origin gating
ajpallares Jul 21, 2026
bc74f30
Merge branch 'main' into webview/6-view
ajpallares Jul 21, 2026
77a5d69
Merge branch 'webview/6-view' into pallares/webview-origin-wrapper
ajpallares Jul 21, 2026
89c8ce7
refactor(paywalls): trim WebViewSession expectedOrigin doc comment
ajpallares Jul 21, 2026
6657c55
Merge branch 'pallares/webview-origin-wrapper' of github.com:RevenueC…
ajpallares Jul 21, 2026
f9bb0a8
Merge remote-tracking branch 'origin/webview/6-view' into webview/7-a…
ajpallares Jul 21, 2026
dc00d8b
Merge remote-tracking branch 'origin/pallares/webview-origin-wrapper'…
ajpallares Jul 21, 2026
408192b
Fix main-actor isolation error in WebViewRepresentable on Xcode 15
ajpallares Jul 21, 2026
1290ab6
refactor(paywalls): tighten web_view platform typealias access
ajpallares Jul 22, 2026
1374572
Merge remote-tracking branch 'origin/webview/7-activate' into webview…
ajpallares Jul 22, 2026
1c52e67
refactor(paywalls): tighten web_view platform typealias access
ajpallares Jul 22, 2026
fb5e589
refactor(paywalls): drop redundant macOS web_view data-store cleanup
ajpallares Jul 22, 2026
92d405c
Merge branch 'main' into webview/6-view
ajpallares Jul 22, 2026
adb9b52
Merge branch 'webview/6-view' into pallares/webview-origin-wrapper
ajpallares Jul 22, 2026
e1463a9
Merge branch 'pallares/webview-origin-wrapper' into webview/7-activate
ajpallares Jul 22, 2026
13810ec
Merge remote-tracking branch 'origin/main' into webview/7-activate
ajpallares Jul 22, 2026
6b64f1f
Merge branch 'main' into webview/7-activate
ajpallares Jul 22, 2026
c4d1eaa
fix(paywalls): limit web_view fallback to unsupported capabilities (#…
ajpallares Jul 22, 2026
be37f9b
fix(paywalls): make web_view fallback decode errors component-specific
ajpallares Jul 22, 2026
42e5f4b
refactor(paywalls): simplify web_view fallback decode error handling
ajpallares Jul 22, 2026
80ef6c3
Allow media playback without a user gesture in paywall web views
ajpallares Jul 23, 2026
9637971
Extract web view configuration builder and add media autoplay test
ajpallares Jul 23, 2026
1c4a04f
Merge branch 'pallares/web-view-allow-media-autoplay' into webview/7-…
ajpallares Jul 23, 2026
5e2decb
Harden paywall web view diagnostics and failure handling
ajpallares Jul 23, 2026
4ae20ab
Merge branch 'pallares/web-view-hardening' into webview/7-activate
ajpallares Jul 23, 2026
cd102a6
fix(paywalls): don't misreport present-but-invalid fallback as missing
ajpallares Jul 23, 2026
e33c775
Gate web view inspector behind DEBUG in addition to log level
ajpallares Jul 23, 2026
5add1ad
Trim web view inspector comment and link WebKit reference
ajpallares Jul 23, 2026
a0252d6
Merge branch 'pallares/web-view-hardening' into webview/7-activate
ajpallares Jul 23, 2026
65278eb
Fix duplicated punctuation in web view load failure log
ajpallares Jul 23, 2026
b9d9e12
Gate web view render on non-empty component id
ajpallares Jul 23, 2026
ee99f59
Handle HTTP 4xx/5xx errors in web view via decidePolicyForNavigationR…
ajpallares Jul 23, 2026
50f426e
Merge branch 'main' into pallares/web-view-hardening
ajpallares Jul 23, 2026
573c62f
Merge branch 'pallares/web-view-hardening' into webview/7-activate
ajpallares Jul 23, 2026
701da3e
Merge branch 'main' into webview/7-activate
ajpallares Jul 23, 2026
fe62e47
Merge branch 'pallares/webview-enable-scroll' into webview/7-activate
ajpallares Jul 23, 2026
c1b3d46
Merge branch 'pallares/webview-gesture-arbitration' into webview/7-ac…
ajpallares Jul 24, 2026
6420ecc
Merge branch 'pallares/webview-gesture-arbitration' into webview/7-ac…
ajpallares Jul 24, 2026
0bd007d
Merge branch 'pallares/webview-gesture-arbitration' into webview/7-ac…
ajpallares Jul 24, 2026
7a0956d
Merge branch 'pallares/webview-gesture-arbitration' into webview/7-ac…
ajpallares Jul 24, 2026
6827f72
Merge branch 'pallares/webview-gesture-arbitration' into webview/7-ac…
ajpallares Jul 24, 2026
7475ee6
Merge branch 'pallares/webview-gesture-arbitration' into webview/7-ac…
ajpallares Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RevenueCatUI/Templates/V2/Components/ComponentsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ struct ComponentsView: View {
VideoComponentView(viewModel: viewModel)
case .countdown(let viewModel):
CountdownComponentView(viewModel: viewModel, onDismiss: onDismiss)
case .webView(let viewModel):
WebViewComponentView(viewModel: viewModel)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ struct WebViewRepresentable: PlatformViewRepresentable {
#endif

@MainActor
private func makeWebView(context: Context) -> PlatformWebView {
static func makeConfiguration(session: WebViewSession?) -> WKWebViewConfiguration {
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = .nonPersistent()
configuration.userContentController = WKUserContentController()
Expand All @@ -209,6 +209,9 @@ struct WebViewRepresentable: PlatformViewRepresentable {
)
}

// This is required to allow media to begin playing without a user gesture
configuration.mediaTypesRequiringUserActionForPlayback = []

#if os(iOS)
configuration.allowsInlineMediaPlayback = true
let disableZoomScript = """
Expand All @@ -221,6 +224,12 @@ struct WebViewRepresentable: PlatformViewRepresentable {
)
#endif

return configuration
}

@MainActor
private func makeWebView(context: Context) -> PlatformWebView {
let configuration = Self.makeConfiguration(session: session)
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = context.coordinator

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class WebViewComponentViewModel: Hashable {
let visible: Bool
let componentID: String

lazy var url: URL? = Self.validatedHTTPSURL(from: self.urlString)
lazy var url: URL? = PaywallComponent.WebViewComponent.validatedHTTPSURL(from: self.urlString)

#if canImport(WebKit)
/// Canonical origin derived from ``url``. Because ``url`` is already validated as HTTPS with a
Expand All @@ -39,16 +39,6 @@ final class WebViewComponentViewModel: Hashable {
self.componentID = component.id
}

private static func validatedHTTPSURL(from urlString: String) -> URL? {
guard !urlString.contains("{{"),
let url = URL(string: urlString),
url.scheme?.lowercased() == "https",
url.host?.isEmpty == false else {
return nil
}
return url
}

func hash(into hasher: inout Hasher) {
hasher.combine(self.urlString)
hasher.combine(self.componentID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
@_spi(Internal) import RevenueCat

#if !os(tvOS) // For Paywalls V2

Expand All @@ -13,7 +14,7 @@ enum WebViewEnvelope {
static let messageHandlerName = "rcWebComponents"
/// Name of the JS function injected into the web view that receives host-to-content frames.
static let receiveFunction = "__rcWebComponentsReceive"
static let defaultProtocolVersion = 1
static let defaultProtocolVersion = PaywallComponent.WebViewComponent.supportedProtocolVersion

static let messageTypeResize = "resize"
static let messageTypeFit = "fit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum PaywallComponentViewModel {
case carousel(CarouselComponentViewModel)
case video(VideoComponentViewModel)
case countdown(CountdownComponentViewModel)
case webView(WebViewComponentViewModel)
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ extension PaywallComponent {
return component.containsUnsupportedConditions()
case .countdown(let component):
return component.containsUnsupportedConditions()
case .webView:
return false
case .fallbackHeader:
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,10 @@ struct ViewModelFactory {
fallbackStackViewModel: fallbackStackViewModel
)
)
case .webView(let component):
return .webView(
WebViewComponentViewModel(component: component)
)
case .fallbackHeader:
// fallbackHeader is filtered out in toStackViewModel and should never reach here.
assertionFailure("fallbackHeader should have been filtered before view model creation")
Expand Down
131 changes: 100 additions & 31 deletions Sources/Paywalls/Components/Common/PaywallComponentBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import Foundation

case countdown(CountdownComponent)

case webView(WebViewComponent)

case fallbackHeader

public enum ComponentType: String, Codable, Sendable {
Expand All @@ -54,6 +56,7 @@ import Foundation
case carousel
case video
case countdown
case webView = "web_view"
case fallbackHeader = "fallback_header"

}
Expand All @@ -73,6 +76,7 @@ import Foundation

case type
case fallback
case protocolVersion

}

Expand Down Expand Up @@ -129,6 +133,9 @@ import Foundation
case .countdown(let component):
try container.encode(ComponentType.countdown, forKey: .type)
try component.encode(to: encoder)
case .webView(let component):
try container.encode(ComponentType.webView, forKey: .type)
try component.encode(to: encoder)
case .fallbackHeader:
try container.encode(ComponentType.fallbackHeader, forKey: .type)
}
Expand All @@ -144,41 +151,66 @@ import Foundation
if let type = ComponentType(rawValue: typeString) {
self = try Self.decodeType(from: decoder, type: type)
} else {
if !container.contains(.fallback) {
let context = DecodingError.Context(
codingPath: container.codingPath,
debugDescription:
"""
Failed to decode unknown type "\(typeString)" without a fallback.
"""
)
throw DecodingError.dataCorrupted(context)
// If `typeString` is unknown, render the author-provided fallback.
self = try Self.decodeFallback(from: decoder, reason: .unknownType(typeString))
}
}

/// Explains why a component is being decoded through its author-provided `fallback`, so the
/// resulting `DecodingError` points at the real cause when no usable fallback is present.
private enum FallbackReason {

/// The component `type` string is not recognized by this SDK.
case unknownType(String)

/// A `web_view` component this SDK cannot service (e.g. unsupported platform or
/// `protocol_version`). The associated value describes the specific cause.
case unserviceableWebView(String)

/// Error description used when no `fallback` is present.
var missingFallbackDescription: String {
switch self {
case let .unknownType(typeString):
return "Failed to decode unknown type \"\(typeString)\" without a fallback."
case let .unserviceableWebView(detail):
return "Failed to decode web_view component without a fallback: \(detail)."
}
}

do {
// If `typeString` is unknown, try to decode the fallback
self = try container.decode(PaywallComponent.self, forKey: .fallback)
} catch DecodingError.valueNotFound {
let context = DecodingError.Context(
codingPath: container.codingPath,
debugDescription:
"""
Failed to decode unknown type "\(typeString)" without a fallback.
"""
)
throw DecodingError.dataCorrupted(context)
} catch {
let context = DecodingError.Context(
codingPath: container.codingPath,
debugDescription:
"""
Failed to decode fallback for unknown type "\(typeString)".
""",
underlyingError: error
)
throw DecodingError.dataCorrupted(context)
/// Error description used when a `fallback` is present but cannot be decoded.
var failedFallbackDescription: String {
switch self {
case let .unknownType(typeString):
return "Failed to decode fallback for unknown type \"\(typeString)\"."
case let .unserviceableWebView(detail):
return "Failed to decode fallback for web_view component: \(detail)."
}
}

}

/// Decodes the author-provided `fallback` component, used both for unknown component types and
/// for known components this SDK version cannot service (e.g. an unsupported `web_view`
/// `protocol_version`). Throws a `reason`-specific error if no usable fallback is present.
private static func decodeFallback(from decoder: Decoder, reason: FallbackReason) throws -> PaywallComponent {
let container = try decoder.container(keyedBy: CodingKeys.self)

do {
return try container.decode(PaywallComponent.self, forKey: .fallback)
} catch DecodingError.keyNotFound, DecodingError.valueNotFound {
let context = DecodingError.Context(
codingPath: container.codingPath,
debugDescription: reason.missingFallbackDescription
)
throw DecodingError.dataCorrupted(context)
Comment thread
cursor[bot] marked this conversation as resolved.
} catch {
let context = DecodingError.Context(
codingPath: container.codingPath,
debugDescription: reason.failedFallbackDescription,
underlyingError: error
)
throw DecodingError.dataCorrupted(context)
}
}

// swiftlint:disable:next cyclomatic_complexity
Expand Down Expand Up @@ -216,9 +248,46 @@ import Foundation
return .video(try VideoComponent(from: decoder))
case .countdown:
return .countdown(try CountdownComponent(from: decoder))
case .webView:
return try Self.decodeWebView(from: decoder)
case .fallbackHeader:
return .fallbackHeader
}
}

private static func decodeWebView(from decoder: Decoder) throws -> PaywallComponent {
#if os(watchOS) || os(tvOS) || !canImport(WebKit)
return try Self.decodeFallback(
from: decoder,
reason: .unserviceableWebView("web views are not supported on this platform")
)
#else
let container = try decoder.container(keyedBy: CodingKeys.self)

// Gate a valid raw version before decoding the full model. This lets a newer protocol use
// its fallback without requiring this SDK to understand that protocol's remaining fields.
let protocolVersion = try container.decode(Int.self, forKey: .protocolVersion)
guard protocolVersion == WebViewComponent.supportedProtocolVersion else {
return try Self.decodeFallback(
from: decoder,
reason: .unserviceableWebView(
"unsupported protocol_version \(protocolVersion) " +
"(this SDK supports \(WebViewComponent.supportedProtocolVersion))"
)
)
}

let component = try WebViewComponent(from: decoder)
if let validationError = component.configurationValidationError {
let context = DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: validationError
)
throw DecodingError.dataCorrupted(context)
}

return .webView(component)
#endif
}

}
4 changes: 2 additions & 2 deletions Sources/Paywalls/Components/PaywallV2CacheWarming.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ extension PaywallComponentsData.PaywallComponentsConfig {
includeHighResInComponentHeirarchy: includeHighResInComponentHeirarchy
)
}
case .fallbackHeader:
case .webView, .fallbackHeader:
break
}
}
Expand Down Expand Up @@ -265,7 +265,7 @@ extension PaywallComponentsData.PaywallComponentsConfig {
if let fallback = countdown.fallback {
urls += self.collectAllVideoURLs(in: fallback)
}
case .fallbackHeader:
case .webView, .fallbackHeader:
break
}
}
Expand Down
34 changes: 31 additions & 3 deletions Sources/Paywalls/Components/PaywallWebViewComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import Foundation

final class WebViewComponent: PaywallComponentBase {

/// Wire `type` value, kept as a `String` because `web_view` is not yet a `ComponentType` case.
let type: String
/// The host<->component bridge protocol version this SDK implements. A config declaring any
/// other version is treated as an unrecognized component and rendered via its `fallback`.
public static let supportedProtocolVersion: Int = 1

let type: ComponentType
public let id: String
public let name: String?
public let visible: Bool?
Expand All @@ -30,6 +33,31 @@ import Foundation

public let size: Size

/// Resolves a URL that is safe to use as a web view component entry point.
public static func validatedHTTPSURL(from urlString: String) -> URL? {
guard !urlString.contains("{{"),
let url = URL(string: urlString),
url.scheme?.lowercased() == "https",
url.host?.isEmpty == false else {
return nil
}

return url
}

/// Describes why the decoded static configuration is invalid, if applicable.
var configurationValidationError: String? {
if self.id.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return "Web view component ID must not be blank."
}

guard Self.validatedHTTPSURL(from: self.url) != nil else {
return "Web view component URL must be a resolved HTTPS URL with a host."
}

return nil
}

public init(
id: String,
name: String? = nil,
Expand All @@ -38,7 +66,7 @@ import Foundation
url: String,
size: Size = .init(width: .fill, height: .fit(nil))
) {
self.type = "web_view"
self.type = .webView
self.id = id
self.name = name
self.visible = visible
Expand Down
Loading