// // ScanView.swift // XuXuCheck // // Created by henseycai on 2025/6/25. // import SwiftUI @preconcurrency import WebKit struct WebViewRepresentable: UIViewRepresentable { var currentUrl: String @Environment(WebViewModel.self) var webViewModel // let viewModel: WebViewModel let webView = WKWebView() @Environment(LoginStatus.self) var loginStatus func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIView(context: Context) -> WKWebView { webView.navigationDelegate = context.coordinator // 让网页从 webView 自己的顶端开始渲染。默认的 .automatic 会在 webView 铺到状态栏 // 下面之后再加一份安全区 inset,内容被二次下推,顶部就多出一条空白带。 // 它同时决定底部:内容区不再被安全区收窄,所以底部也不会自动让出 34pt, // 滚动内容会一直用到 webView 的物理底边。 // 顶部到底留多少由 XuXuWebView 的 topSafeInset 统一控制;推导见 docs/webview-safe-area.md webView.scrollView.contentInsetAdjustmentBehavior = .never if(currentUrl.isEmpty == false){ webViewModel.hasNewCommand = false webViewModel.url = URL(string:currentUrl) } if let url = webViewModel.url { webView.load(URLRequest(url: url)) } webViewModel.assignWebView(webView: webView) return webView } func updateUIView(_ uiView: WKWebView, context: Context) { } class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler { var parent: WebViewRepresentable var webView: WKWebView { parent.webView } var webViewModel: WebViewModel { parent.webViewModel } init(_ parent: WebViewRepresentable) { self.parent = parent super.init() // Add addScriptMessageHandler in javascript: window.webkit.messageHandlers.MyObserver.postMessage() webView.configuration.userContentController.add(self, name: "receiveData") } func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if navigationAction.navigationType == .linkActivated { if let url = navigationAction.request.url { webView.load(URLRequest(url: url)) decisionHandler(.cancel) return } } decisionHandler(.allow) } func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { webViewModel.isLoading = true } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { webViewModel.isLoading = false } func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { webViewModel.isLoading = false webViewModel.error = error print("loading error: \(error)") } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let messageFromWebPage = message.body as! String let jsonData = messageFromWebPage.data(using: .utf8)! print(jsonData) do { let js2obj = try JSONDecoder().decode(JS2SwiftModel.self, from: jsonData) webViewModel.jsParameter = js2obj.parameter webViewModel.jsCommand = js2obj.command webViewModel.hasNewCommand = true print(js2obj) print(webViewModel.jsCommand?.lowercased()) switch webViewModel.jsCommand?.lowercased(){ case "print": break case "tel": break case "clearcache": break case "setdefaultmember": if let member_data = webViewModel.jsParameter?.member_data { Storage.shared.member_id = member_data.member_id ?? 0 Storage.shared.member_name = member_data.member_name ?? "" } break case "changeself": if let dataSelf = webViewModel.jsParameter?.jsSelf { Storage.shared.userid = dataSelf.user_id ?? 0 Storage.shared.mobile = dataSelf.user_mobile ?? "" Storage.shared.email = dataSelf.user_email ?? "" Storage.shared.username = dataSelf.username ?? "" } break case "goback": break case "scanpaper": break case "logout": Storage.shared.email = "" Storage.shared.username = "" Storage.shared.isLogin = false Storage.shared.member_id = 0 Storage.shared.mobile = "" Storage.shared.userid = 0 Storage.shared.usertoken = "" Storage.shared.member_name = "" //can do this parent.loginStatus.isLogin = false break case "login": Storage.shared.email = "" Storage.shared.username = "" Storage.shared.isLogin = false Storage.shared.member_id = 0 Storage.shared.mobile = "" Storage.shared.userid = 0 Storage.shared.usertoken = "" Storage.shared.member_name = "" //can do this parent.loginStatus.routeToLoginView = true break default: break } }catch{ print("error decoding json:\(error)") } // if messageFromWebPage == "index" { // viewModel.backButtonIsEnabled = false // viewModel.loadFile(name: messageFromWebPage) // return // } // if messageFromWebPage == "goPage" { // viewModel.backButtonIsEnabled = true // return // } // // let alertController = UIAlertController(title: "Javascript said:", message: messageFromWebPage, preferredStyle: .alert) // let okAction = UIAlertAction(title: "OK", style: .default) { (_) in // print("OK") // } // alertController.addAction(okAction) // guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, // let rootViewController = windowScene.windows.first?.rootViewController else { // return // } // rootViewController.present(alertController, animated: true) } /// Callback from javascript: window.webkit.messageHandlers.MyObserver.postMessage(message) // func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // let messageFromWebPage = message.body as! String // // if messageFromWebPage == "index" { // viewModel.backButtonIsEnabled = false // viewModel.loadFile(name: messageFromWebPage) // return // } // if messageFromWebPage == "goPage" { // viewModel.backButtonIsEnabled = true // return // } // // let alertController = UIAlertController(title: "Javascript said:", message: messageFromWebPage, preferredStyle: .alert) // let okAction = UIAlertAction(title: "OK", style: .default) { (_) in // print("OK") // } // alertController.addAction(okAction) // guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, // let rootViewController = windowScene.windows.first?.rootViewController else { // return // } // rootViewController.present(alertController, animated: true) // } } }