Files
PrivateChat/apple-client/Sources/App/EmbeddedWebAppView.swift

113 lines
3.1 KiB
Swift
Raw Normal View History

2026-03-09 19:35:08 +01:00
import SwiftUI
import WebKit
struct EmbeddedWebAppView: View {
@Bindable var settings: SettingsStore
var body: some View {
PlatformWebView(script: settings.injectionScript, reloadToken: settings.webStateVersion)
.ignoresSafeArea()
}
}
#if os(macOS)
struct PlatformWebView: NSViewRepresentable {
let script: String
let reloadToken: UUID
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeNSView(context: Context) -> WKWebView {
context.coordinator.makeWebView(script: script)
}
func updateNSView(_ webView: WKWebView, context: Context) {
context.coordinator.update(webView: webView, script: script, reloadToken: reloadToken)
}
}
#else
struct PlatformWebView: UIViewRepresentable {
let script: String
let reloadToken: UUID
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIView(context: Context) -> WKWebView {
context.coordinator.makeWebView(script: script)
}
func updateUIView(_ webView: WKWebView, context: Context) {
context.coordinator.update(webView: webView, script: script, reloadToken: reloadToken)
}
}
#endif
final class Coordinator: NSObject, WKNavigationDelegate {
private var lastReloadToken: UUID?
private var pendingRefreshTask: Task<Void, Never>?
func makeWebView(script: String) -> WKWebView {
let configuration = WKWebViewConfiguration()
let contentController = WKUserContentController()
contentController.addUserScript(
WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: true)
)
configuration.userContentController = contentController
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = self
#if os(macOS)
webView.setValue(false, forKey: "drawsBackground")
#endif
loadIndex(into: webView)
return webView
}
func update(webView: WKWebView, script: String, reloadToken: UUID) {
guard lastReloadToken != reloadToken else {
return
}
lastReloadToken = reloadToken
pendingRefreshTask?.cancel()
pendingRefreshTask = Task { @MainActor [weak webView] in
await Task.yield()
guard !Task.isCancelled, let webView else {
return
}
webView.evaluateJavaScript(script) { _, _ in
DispatchQueue.main.async {
webView.reload()
}
}
}
}
private func loadIndex(into webView: WKWebView) {
let indexURL =
Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "WebApp/browser")
?? Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "WebApp")
guard let indexURL else {
let fallbackHTML = """
<html>
<body style="font-family:-apple-system;padding:32px;background:#08111d;color:#eff3ff">
<h2>Embedded chat bundle not found</h2>
<p>The Apple app expects the Angular client to be built into the bundled <code>WebApp</code> folder.</p>
</body>
</html>
"""
webView.loadHTMLString(fallbackHTML, baseURL: nil)
return
}
webView.loadFileURL(indexURL, allowingReadAccessTo: indexURL.deletingLastPathComponent())
}
}