SkipWebUIDelegate is the popup/window-creation delegate API for SkipWeb.
Assign it through WebEngineConfiguration.uiDelegate when you need to control child-window behavior (target=_blank, window.open, multi-window flows).
public protocol SkipWebUIDelegate: AnyObject {
func webView(
_ webView: WebView,
createWebViewWith request: WebWindowRequest,
platformContext: PlatformCreateWindowContext
) -> WebEngine?
func webViewDidClose(_ webView: WebView, child: WebEngine)
}webView(_:createWebViewWith:platformContext:)- Return
nilto deny popup creation. - Return a child
WebEngineto allow popup creation. request.targetURLmay benilon Android at creation time.
- Return
webViewDidClose(_:child:)- Called when a previously-created child window is closed via javascript (
window.close()).
- Called when a previously-created child window is closed via javascript (
- iOS
- create callback is wired from
WKUIDelegate.webView(_:createWebViewWith:for:windowFeatures:) - close callback is wired from
WKUIDelegate.webViewDidClose(_:)
- create callback is wired from
- Android
- create callback is wired from
WebChromeClient.onCreateWindow - close callback is wired from
WebChromeClient.onCloseWindow
- create callback is wired from
The example below is inspired by the sandbox experiment and demonstrates:
- a direct
SkipWebUIDelegateimplementation - allowing popup creation by returning a child engine
- optional callback hooks for host UI/logging
- platform-safe child engine construction with
PlatformWebView, an alias forWkWebView(iOS) andWebView(Android)
import Foundation
// Need to use @preconcurrency here because Type 'WebEngine' does not conform to the 'Sendable' protocol
@preconcurrency import SkipWeb
/* SKIP @bridge */
public final class PopupDelegateProbe: NSObject, SkipWebUIDelegate {
// SKIP @nobridge
var onCreate: ((WebWindowRequest, WebEngine) -> Void)?
// SKIP @nobridge
var onClose: ((WebView, WebEngine) -> Void)?
/* SKIP @bridge */
public func webView(
_ webView: WebView,
createWebViewWith request: WebWindowRequest,
platformContext: PlatformCreateWindowContext
) -> WebEngine? {
let child = Self.makeChildEngine(platformContext: platformContext)
onCreate?(request, child)
return child
}
/* SKIP @bridge */
public func webViewDidClose(_ webView: WebView, child: WebEngine) {
onClose?(webView, child)
}
private static func makeChildEngine(platformContext: PlatformCreateWindowContext) -> WebEngine {
#if os(Android)
return MainActor.assumeIsolated {
WebEngine(configuration: WebEngineConfiguration())
}
#else
return MainActor.assumeIsolated {
let child = platformContext.makeChildWebEngine()
return child
}
#endif
}
}Attach the delegate to your configuration:
import SwiftUI
@preconcurrency import SkipWeb
struct PopupHostView: View {
private let delegate = PopupDelegateProbe()
@State internal var navigator = WebViewNavigator()
@State internal var state = WebViewState()
@State internal var configuration: WebEngineConfiguration
init() {
let config = WebEngineConfiguration(
javaScriptEnabled: true,
javaScriptCanOpenWindowsAutomatically: true
)
config.uiDelegate = delegate
_configuration = State(initialValue: config)
}
var body: some View {
WebView(
configuration: configuration,
navigator: navigator,
url: URL(string: "https://example.com")!,
state: $state
)
}
}- Keep delegate method signatures exactly aligned with the protocol.
- Keep the delegate class shape simple and direct.
- On iOS, WebKit requires the returned child to be initialized with the exact configuration supplied to
WKUIDelegate.createWebViewWith. - If this contract is violated, WebKit can raise
NSInternalInconsistencyExceptionwith:Returned WKWebView was not created with the given configuration. - For iOS popup creation, return a child created via
platformContext.makeChildWebEngine(...). makeChildWebEngine()mirrors the parentWebEngineConfigurationand inspectability by default. Pass an explicit configuration only when you intentionally want different child behavior.makeChildWebEngine()does not automatically copy platform delegate assignments (WKUIDelegate,WKNavigationDelegate) from the parent web view; assign those explicitly when needed.PlatformCreateWindowContextaliasesWebKitCreateWindowParamson iOS andAndroidCreateWindowParamson Android.