Visual_Novel_iOS/crush/Crush/Src/Components/Base/H5BaseViewController.swift

490 lines
18 KiB
Swift
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// H5BaseViewController.swift
// E-Wow
//
// Created by dong on 2021/1/5.
//
// import Alamofire
// import CryptoSwift
import UIKit
import URLNavigator
import WebKit
class H5BaseViewController: CLBaseViewController {
public var navTitleShow: Bool = true
var targetUrl: URL! = URL(string: "")
var lastProgress: Double! = 0
private var observeRegister: Bool = false
private let schemes = ["route", "closeWebview", "getUserInfo", "setLoading", "request", "modal", "share", "init", "openBrowser", "getAppVersion", "sendTrack"]
lazy var webView: WKWebView! = {
let config = WKWebViewConfiguration()
let preferences = WKPreferences()
preferences.javaScriptCanOpenWindowsAutomatically = true
config.preferences = preferences
config.allowsInlineMediaPlayback = true
config.mediaTypesRequiringUserActionForPlayback = .video
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 320, height: 480), configuration: config)
// 3webView
webView.backgroundColor = .c.cbd
webView.scrollView.backgroundColor = .c.cbd
webView.isOpaque = false
let jsFilePath = Bundle.main.path(forResource: "webview", ofType: "js")
if let jshtml = try? String(contentsOfFile: jsFilePath!, encoding: .utf8) {
let script = WKUserScript(source: jshtml, injectionTime: .atDocumentStart, forMainFrameOnly: true)
webView.configuration.userContentController.addUserScript(script)
}
return webView
}()
lazy var progressView: UIProgressView = {
let progressView = UIProgressView(frame: .zero)
progressView.progress = 0
progressView.setProgress(0, animated: false)
navigationView.addSubview(progressView)
progressView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(1)
}
progressView.progressTintColor = .c.cpn//.purple
progressView.trackTintColor = .clear
return progressView
}()
lazy var closeButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(R.image.icon_close_20(), for: .normal)
button.addTarget(self, action: #selector(tapNaviCloseBtn), for: .touchUpInside)
button.isHidden = true
navigationView.leftStackH.addArrangedSubview(button)
button.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 44, height: 44))
}
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
// view.backgroundColor = .white
// webView.backgroundColor = .white
webView.backgroundColor = .c.cbd
webView.navigationDelegate = self
view.addSubview(webView)
webView.snp.makeConstraints { make in
make.bottom.right.left.equalTo(self.view)
make.top.equalTo(navigationView.snp.bottom)
}
navigationView.backButton.removeTarget(navigationView, action: .none, for: .touchUpInside)
navigationView.backButton.addTarget(self, action: #selector(tapNaviBackBtn), for: .touchUpInside)
setupBaseEvents()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for (_, per) in schemes.enumerated() {
webView.configuration.userContentController.add(self, name: per)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
for (_, per) in schemes.enumerated() {
webView.configuration.userContentController.removeScriptMessageHandler(forName: per)
}
}
public func loadURL(url: URL) {
/*
if let lan = Languages.preferedLans.first {
let queryItem = URLQueryItem(name: "lang", value: lan.rawValue)
if var compoments = URLComponents(url: url, resolvingAgainstBaseURL: true){
if let items = compoments.queryItems, items.count > 0{
compoments.queryItems?.append(queryItem)
}else{
compoments.queryItems = [queryItem]
}
let afterUrl = compoments.url
targetUrl = afterUrl
}
dlog("h5 path with language: \(String(describing: targetUrl))")
} else {
targetUrl = url
}
*/
targetUrl = url
}
func setupBaseEvents() {
weak var wself = self
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
guard let url = self.targetUrl else {
assert(false)
return
}
let request = NSMutableURLRequest(url: url)
wself?.webView.load(request as URLRequest)
}
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
observeRegister = true
progressView.setProgress(0, animated: false)
}
deinit {
webView.configuration.userContentController.removeAllUserScripts()
if observeRegister {
webView.removeObserver(self, forKeyPath: "title")
webView.removeObserver(self, forKeyPath: "estimatedProgress")
}
}
func handleInit(msg: JSSDKMessage) {
// wait to override the func
}
}
extension H5BaseViewController {
@objc func tapNaviBackBtn() {
if webView.canGoBack {
webView.goBack()
} else {
close()
}
}
@objc func tapNaviCloseBtn() {
close()
}
}
// MARK: - Helper
extension H5BaseViewController {
func stopHtmlVoice() {
// ...
let jsaudio = "var vids = document.getElementsByTagName('audio'); for( var i = 0; i < vids.length; i++ ){vids.item(i).pause()}"
webView.evaluateJavaScript(jsaudio, completionHandler: nil)
}
static func clearCache() {
// let dataStore = WKWebsiteDataStore.default()
// dataStore.fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), completionHandler: { records in
// for record in records {
// WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {
// print(" Webview cache clear successfully\(record)")
// })
// }
// })
let websiteDataTypes: Set<String> = [
WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeMemoryCache,
WKWebsiteDataTypeLocalStorage,
WKWebsiteDataTypeWebSQLDatabases,
WKWebsiteDataTypeIndexedDBDatabases
]
// 1970
let dateFrom = Date(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: dateFrom) {
print("WebView data cleared")
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "title" {
if navTitleShow {
navigationView.titleLabel.text = webView.title
}
} else if keyPath == "estimatedProgress" {
updateProgress(progress: webView.estimatedProgress)
} else {
}
}
func updateProgress(progress: Double) {
progressView.alpha = 1
// dlog("progress : \(progress)")
if progress > lastProgress {
progressView.setProgress(Float(progress), animated: true)
} else {
progressView.setProgress(Float(progress), animated: false)
}
lastProgress = progress
if progress >= 1 {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
self?.progressView.alpha = 0
self?.progressView.setProgress(0, animated: false)
self?.lastProgress = 0
}
}
}
}
// MARK: - Handle Event
extension H5BaseViewController {
func handleRoute(msg: JSSDKMessage) {
if let uri = msg.uri?.urlValue {
navigator.open(uri)
}
}
func handleRequest(msg: JSSDKMessage) {
// let requestUri = msg.uri
// let params = msg.params
// for h5...error
guard let uri = msg.uri else {
return
}
var requestAny: AnyCodable = AnyCodable(value: [:])
let token = UserCore.shared.token
if !token.isEmpty {
do {
// let body = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
// let str = String(data: body, encoding: .utf8)
var str = ""
if let dict = msg.params, let data2: Data = try? JSONSerialization.data(withJSONObject: dict, options: []) {
let objcAnyCodable = try! JSONDecoder().decode(AnyCodable.self, from: data2)
let backToJson = try! JSONEncoder().encode(objcAnyCodable)
let jsonString = String(bytes: backToJson, encoding: .utf8)!
str = jsonString
}
// let key = (token + "AHkt5aUUtO6HZPid").md5().uppercased()
// let aes = try AES(key: key, iv: "HBB4UO5kEmM4169Z")
// let encrypted = try aes.encrypt(str.bytes)
// let result = encrypted.toBase64()
// let dic = ["key": result]
// dlog(" \(str) \n \(dic)")
// if let data3: Data = try? JSONSerialization.data(withJSONObject: dic, options: []) {
// let objcAnyCodable = try! JSONDecoder().decode(AnyCodable.self, from: data3)
// requestAny = objcAnyCodable
// }
} catch {
}
} else {
if let dict = msg.params, let data2: Data = try? JSONSerialization.data(withJSONObject: dict, options: []), let objcAnyCodable = try? JSONDecoder().decode(AnyCodable.self, from: data2) {
requestAny = objcAnyCodable
}
}
// let headers = HTTPHeaders(APIConfig.apiHeaders()!)
Hud.showIndicator()
dlog("h5 request:\(uri) params: \(requestAny)")
// AF.request(uri, method: .post, parameters: requestAny, encoder: JSONParameterEncoder.default, headers: headers, interceptor: nil, requestModifier: nil).responseString { [weak self] response in
// // dlog("response: \(response)")
// self?.view.hideToastActivity()
// switch response.result {
// case let .success(model):
// guard let ltResponse: ResponseData = ResponseData<Dictionary<String, Any>>.deserialize(from: model) else {
// return
// }
//
// if ltResponse.status == "OK" {
// if let ltArrayResponse = ResponseData<Array<Dictionary<String, Any>>>.deserialize(from: model), let jsonDict = ltArrayResponse.content {
// if let data = try? JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted), let str = String(data: data, encoding: .utf8) {
// let jsonString = str // content2.toJSONString() ?? ""
// let js = "\(msg.success ?? "")((\(jsonString)))"
// dlog(" success call js:\(js)")
// self?.webView.evaluateJavaScript(js, completionHandler: { _, error in
// if error != nil {
// dlog(" exec js error: \(error?.localizedDescription ?? "")")
// }
// })
// return
// }
//
// } else if let content = ltResponse.content {
// let jsonString = content.toJSONString()
// let js = "\(msg.success ?? "")((\(jsonString ?? "")))"
// dlog(" success call js:\(js)")
// self?.webView.evaluateJavaScript(js, completionHandler: { _, error in
// if error != nil {
// dlog(" exec js error: \(error?.localizedDescription ?? "")")
// }
// })
// return
// }
//
// let js = "\(msg.success ?? "")()"
// dlog(" success call js no content:\(js)")
// self?.webView.evaluateJavaScript(js, completionHandler: { _, error in
// if error != nil {
// dlog(" exec js error: \(error?.localizedDescription ?? "")")
// }
// })
// } else {
// // ---
// let js = "\(msg.error ?? "")((\(model)))"
// dlog(" api error: \(js)")
// self?.webView.evaluateJavaScript(js, completionHandler: nil)
// }
//
// break
// default:
// // ---
// UIWindow.key?.makeToast(R.string.localizable.internet_connect_failed.localized())
// let js = "\(msg.error ?? "")()"
// dlog(" api network error: \(js)")
// self?.webView.evaluateJavaScript(js, completionHandler: nil)
// break
// }
// }
}
func handleLoading(msg: JSSDKMessage) {
if msg.status {
UIWindow.key?.makeToastActivity()
} else {
UIWindow.key?.hideToastActivity()
}
}
func handleGetUserInfo(msg: JSSDKMessage) {
}
func handleModal(msg: JSSDKMessage) {
// msg.type.
}
func handleOpenBrowser(msg: JSSDKMessage) {
if let uri = msg.uri, uri.count > 0, let url = URL(string: uri) {
UIApplication.shared.open(url, options: [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly: false], completionHandler: nil)
}
}
func handleGetAppversion(msg: JSSDKMessage) {
let version = Bundle.appVersion
let dict = ["version": version]
let data1 = try? JSONSerialization.data(withJSONObject: dict, options: [])
let dictJs = String(data: data1!, encoding: .utf8) ?? ""
// dict.toJSONString() ?? ""
let js = "\(msg.success ?? "")((\(dictJs)))"
dlog("✅ success call js:\(js)")
webView.evaluateJavaScript(js, completionHandler: { _, error in
if error != nil {
dlog("❌ exec js error: \(error?.localizedDescription ?? "")")
}
})
}
private func handleDealSendTrack(msg: JSSDKMessage) {
if let trackName = msg.name {
var params = msg.params ?? [:]
if let uid = UserCore.shared.user?.userId {
// params.updateValue(uid, forKey: "userId")
}
// AppAnalytics.commonRecord(trackName, parameters: params)
}
}
}
// MARK: - 🔥 WKScriptMessageHandler
extension H5BaseViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
dlog("☁️☁️ h5 message name: \(message.name) message body: \(message.body)")
let msgName = message.name
guard let body = message.body as? String else {
// elog("h5 message failed! : \(msgName)")
return
}
guard let msg = CodableHelper.decode(JSSDKMessage.self, from: body) else {
return
}
if msgName == "route" {
msg.msgName = msgName
handleRoute(msg: msg)
} else if msgName == "request" {
msg.msgName = msgName
handleRequest(msg: msg)
} else if msgName == "setLoading" {
msg.msgName = msgName
handleLoading(msg: msg)
} else if msgName == "closeWebview" {
close()
} else if msgName == "getUserInfo" {
msg.msgName = msgName
handleGetUserInfo(msg: msg)
} else if msgName == "modal" {
msg.msgName = msgName
handleModal(msg: msg)
} else if msgName == "share" {
// to do.
} else if msgName == "init" {
handleInit(msg: msg)
} else if msgName == "openBrowser" {
msg.msgName = msgName
handleOpenBrowser(msg: msg)
} else if msgName == "getAppVersion" {
msg.msgName = msgName
handleGetAppversion(msg: msg)
} else if msgName == "sendTrack" {
msg.msgName = msgName
handleDealSendTrack(msg: msg)
} else {
// Please upgrade to the latest version
dlog("🛎Please upgrade to the latest version,Unsupported protocol.")
}
}
}
// MARK: - WKNavigationDelegate`
extension H5BaseViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let urlRequest = navigationAction.request.url?.absoluteString.removingPercentEncoding else {
decisionHandler(.cancel)
return
}
dlog("☁️ webview load \(urlRequest) ☁️")
if urlRequest.hasPrefix(AppConst.schemePrefix) {
navigator.open(urlRequest)
} else if urlRequest.hasPrefix("mailto:") { // open system to send email.
if let url = URL(string: urlRequest) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
dlog("didFinish 🔥🔥🔥\(String(describing: webView.url))")
closeButton.isHidden = !webView.canGoBack
}
}