Download apps/ios/Sources/Screen/ScreenWebView.swift from Darochin/openskynet: direct link, hf CLI and curl.
- Browser
- Download file 6.73 kB
-
https://huggingface.co/Darochin/openskynet/resolve/main/apps/ios/Sources/Screen/ScreenWebView.swift
- Command line
-
hf download hf://Darochin/openskynet/apps/ios/Sources/Screen/ScreenWebView.swift
-
curl -L -o ScreenWebView.swift https://huggingface.co/Darochin/openskynet/resolve/main/apps/ios/Sources/Screen/ScreenWebView.swift
6.73 kB
| import OpenClawKit | |
| import SwiftUI | |
| import WebKit | |
| struct ScreenWebView: UIViewRepresentable { | |
| var controller: ScreenController | |
| func makeCoordinator() -> ScreenWebViewCoordinator { | |
| ScreenWebViewCoordinator(controller: self.controller) | |
| } | |
| func makeUIView(context: Context) -> UIView { | |
| context.coordinator.makeContainerView() | |
| } | |
| func updateUIView(_: UIView, context: Context) { | |
| context.coordinator.updateController(self.controller) | |
| } | |
| static func dismantleUIView(_: UIView, coordinator: ScreenWebViewCoordinator) { | |
| coordinator.teardown() | |
| } | |
| } | |
| final class ScreenWebViewCoordinator: NSObject { | |
| private weak var controller: ScreenController? | |
| private let navigationDelegate = ScreenNavigationDelegate() | |
| private let a2uiActionHandler = CanvasA2UIActionMessageHandler() | |
| private let userContentController = WKUserContentController() | |
| private(set) var managedWebView: WKWebView? | |
| private weak var containerView: UIView? | |
| init(controller: ScreenController) { | |
| self.controller = controller | |
| super.init() | |
| self.navigationDelegate.controller = controller | |
| self.a2uiActionHandler.controller = controller | |
| } | |
| func makeContainerView() -> UIView { | |
| if let containerView { | |
| return containerView | |
| } | |
| let container = UIView(frame: .zero) | |
| container.backgroundColor = .black | |
| let webView = Self.makeWebView(userContentController: self.userContentController) | |
| webView.navigationDelegate = self.navigationDelegate | |
| self.installA2UIHandlers() | |
| webView.translatesAutoresizingMaskIntoConstraints = false | |
| container.addSubview(webView) | |
| NSLayoutConstraint.activate([ | |
| webView.leadingAnchor.constraint(equalTo: container.leadingAnchor), | |
| webView.trailingAnchor.constraint(equalTo: container.trailingAnchor), | |
| webView.topAnchor.constraint(equalTo: container.topAnchor), | |
| webView.bottomAnchor.constraint(equalTo: container.bottomAnchor), | |
| ]) | |
| self.managedWebView = webView | |
| self.containerView = container | |
| self.controller?.attachWebView(webView) | |
| return container | |
| } | |
| func updateController(_ controller: ScreenController) { | |
| let previousController = self.controller | |
| let controllerChanged = self.controller !== controller | |
| self.controller = controller | |
| self.navigationDelegate.controller = controller | |
| self.a2uiActionHandler.controller = controller | |
| if controllerChanged, let managedWebView { | |
| previousController?.detachWebView(managedWebView) | |
| controller.attachWebView(managedWebView) | |
| } | |
| } | |
| func teardown() { | |
| if let managedWebView { | |
| self.controller?.detachWebView(managedWebView) | |
| managedWebView.navigationDelegate = nil | |
| } | |
| self.removeA2UIHandlers() | |
| self.navigationDelegate.controller = nil | |
| self.a2uiActionHandler.controller = nil | |
| self.managedWebView = nil | |
| self.containerView = nil | |
| } | |
| private static func makeWebView(userContentController: WKUserContentController) -> WKWebView { | |
| let config = WKWebViewConfiguration() | |
| config.websiteDataStore = .nonPersistent() | |
| config.userContentController = userContentController | |
| let webView = WKWebView(frame: .zero, configuration: config) | |
| // Canvas scaffold is a fully self-contained HTML page; avoid relying on transparency underlays. | |
| webView.isOpaque = true | |
| webView.backgroundColor = .black | |
| let scrollView = webView.scrollView | |
| scrollView.backgroundColor = .black | |
| scrollView.contentInsetAdjustmentBehavior = .never | |
| scrollView.contentInset = .zero | |
| scrollView.scrollIndicatorInsets = .zero | |
| scrollView.automaticallyAdjustsScrollIndicatorInsets = false | |
| return webView | |
| } | |
| private func installA2UIHandlers() { | |
| for name in CanvasA2UIActionMessageHandler.handlerNames { | |
| self.userContentController.add(self.a2uiActionHandler, name: name) | |
| } | |
| } | |
| private func removeA2UIHandlers() { | |
| for name in CanvasA2UIActionMessageHandler.handlerNames { | |
| self.userContentController.removeScriptMessageHandler(forName: name) | |
| } | |
| } | |
| } | |
| // MARK: - Navigation Delegate | |
| /// Handles navigation policy to intercept openclaw:// deep links from canvas | |
| private final class ScreenNavigationDelegate: NSObject, WKNavigationDelegate { | |
| weak var controller: ScreenController? | |
| func webView( | |
| _: WKWebView, | |
| decidePolicyFor navigationAction: WKNavigationAction, | |
| decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) | |
| { | |
| guard let url = navigationAction.request.url else { | |
| decisionHandler(.allow) | |
| return | |
| } | |
| // Intercept openclaw:// deep links. | |
| if url.scheme?.lowercased() == "openclaw" { | |
| decisionHandler(.cancel) | |
| self.controller?.onDeepLink?(url) | |
| return | |
| } | |
| decisionHandler(.allow) | |
| } | |
| func webView( | |
| _: WKWebView, | |
| didFailProvisionalNavigation _: WKNavigation?, | |
| withError error: any Error) | |
| { | |
| self.controller?.errorText = error.localizedDescription | |
| } | |
| func webView(_: WKWebView, didFinish _: WKNavigation?) { | |
| self.controller?.errorText = nil | |
| self.controller?.applyDebugStatusIfNeeded() | |
| self.controller?.applyHomeCanvasStateIfNeeded() | |
| } | |
| func webView(_: WKWebView, didFail _: WKNavigation?, withError error: any Error) { | |
| self.controller?.errorText = error.localizedDescription | |
| } | |
| } | |
| private final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler { | |
| static let messageName = "openclawCanvasA2UIAction" | |
| static let handlerNames = [messageName] | |
| weak var controller: ScreenController? | |
| func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { | |
| guard Self.handlerNames.contains(message.name) else { return } | |
| guard let controller else { return } | |
| guard let url = message.webView?.url else { return } | |
| if url.isFileURL { | |
| guard controller.isTrustedCanvasUIURL(url) else { return } | |
| } else { | |
| // For security, only accept actions from local-network pages (e.g. the canvas host). | |
| guard controller.isLocalNetworkCanvasURL(url) else { return } | |
| } | |
| guard let body = ScreenController.parseA2UIActionBody(message.body) else { return } | |
| controller.onA2UIAction?(body) | |
| } | |
| } | |