spb/admin-ka-ios
Public
Swift 100%
1import SwiftUI2import WebKit3import UIKit45// État partagé entre le WKWebView et l'interface SwiftUI6final class WebModel: ObservableObject {7 @Published var progress: Double = 08 @Published var loaded = false9 @Published var failed = false10 var reloadRequested = false11 var webView: WKWebView?1213 func reload() {14 failed = false15 loaded = false16 progress = 017 if let wv = webView {18 if wv.url == nil { wv.load(URLRequest(url: Ka.url)) } else { wv.reload() }19 }20 }21}2223struct WebView: UIViewRepresentable {24 @ObservedObject var model: WebModel2526 func makeCoordinator() -> Coordinator { Coordinator(model: model) }2728 func makeUIView(context: Context) -> WKWebView {29 let cfg = WKWebViewConfiguration()30 cfg.websiteDataStore = .default() // persiste le cookie de session (reste connecté)31 cfg.allowsInlineMediaPlayback = true32 cfg.defaultWebpagePreferences.allowsContentJavaScript = true3334 let wv = WKWebView(frame: .zero, configuration: cfg)35 wv.navigationDelegate = context.coordinator36 wv.uiDelegate = context.coordinator37 wv.allowsBackForwardNavigationGestures = false38 wv.scrollView.bounces = true39 wv.scrollView.contentInsetAdjustmentBehavior = .never40 wv.isOpaque = false41 wv.backgroundColor = UIColor(Ka.bg)42 wv.scrollView.backgroundColor = UIColor(Ka.bg)43 wv.customUserAgent = "AdminKa-iOS/1.0 (Groupe KA console)"4445 // pull-to-refresh natif46 let rc = UIRefreshControl()47 rc.tintColor = UIColor(Ka.orange)48 rc.addTarget(context.coordinator, action: #selector(Coordinator.onRefresh(_:)), for: .valueChanged)49 wv.scrollView.refreshControl = rc5051 context.coordinator.observe(wv)52 model.webView = wv53 wv.load(URLRequest(url: Ka.url))54 return wv55 }5657 func updateUIView(_ uiView: WKWebView, context: Context) {}5859 final class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {60 let model: WebModel61 private var obs: NSKeyValueObservation?62 init(model: WebModel) { self.model = model }6364 func observe(_ wv: WKWebView) {65 obs = wv.observe(\.estimatedProgress, options: [.new]) { [weak self] wv, _ in66 DispatchQueue.main.async { self?.model.progress = wv.estimatedProgress }67 }68 }6970 @objc func onRefresh(_ sender: UIRefreshControl) {71 model.webView?.reload()72 }7374 func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {75 webView.scrollView.refreshControl?.endRefreshing()76 DispatchQueue.main.async {77 withAnimation(.easeOut(duration: 0.35)) { self.model.loaded = true }78 self.model.failed = false79 }80 }8182 func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { fail(error) }83 func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { fail(error) }84 private func fail(_ error: Error) {85 let ns = error as NSError86 if ns.code == NSURLErrorCancelled { return }87 DispatchQueue.main.async {88 self.model.webView?.scrollView.refreshControl?.endRefreshing()89 self.model.failed = true90 }91 }9293 // liens externes (target=_blank / http externes) → Safari94 func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration,95 for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {96 if let url = navigationAction.request.url { UIApplication.shared.open(url) }97 return nil98 }99100 // --- Panneaux JavaScript natifs (indispensables : sans eux confirm()101 // --- ne s'affiche pas → le mode BYPASS et les confirmations restent morts) ---102 private func topVC(_ webView: WKWebView) -> UIViewController? {103 var vc = webView.window?.rootViewController104 while let p = vc?.presentedViewController { vc = p }105 return vc106 }107 func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,108 initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {109 let a = UIAlertController(title: nil, message: message, preferredStyle: .alert)110 a.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler() })111 guard let vc = topVC(webView) else { completionHandler(); return }112 vc.present(a, animated: true)113 }114 func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String,115 initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {116 let a = UIAlertController(title: nil, message: message, preferredStyle: .alert)117 a.addAction(UIAlertAction(title: "Annuler", style: .cancel) { _ in completionHandler(false) })118 a.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) })119 guard let vc = topVC(webView) else { completionHandler(false); return }120 vc.present(a, animated: true)121 }122 func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String,123 defaultText: String?, initiatedByFrame frame: WKFrameInfo,124 completionHandler: @escaping (String?) -> Void) {125 let a = UIAlertController(title: nil, message: prompt, preferredStyle: .alert)126 a.addTextField { $0.text = defaultText }127 a.addAction(UIAlertAction(title: "Annuler", style: .cancel) { _ in completionHandler(nil) })128 a.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(a.textFields?.first?.text) })129 guard let vc = topVC(webView) else { completionHandler(nil); return }130 vc.present(a, animated: true)131 }132 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,133 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {134 if let url = navigationAction.request.url, let host = url.host,135 navigationAction.navigationType == .linkActivated,136 host != Ka.url.host {137 UIApplication.shared.open(url)138 decisionHandler(.cancel)139 return140 }141 decisionHandler(.allow)142 }143 }144}145