业务需求
:蓝牙外设<-->连接APP<-->打开WKWebView加载的Web小游戏。
业务描述
:希望蓝牙设备
设备通过连接APP
和Web网页
进行数据交互。Web中的游戏业务逻辑依赖蓝牙设备
传输的数据,同时Web中由蓝牙设备
传输的数据触发的某些业务状态
,回传给蓝牙设备。
所以,这里暂时先记录下App和Web通信部分的逻辑;蓝牙部分后期补上。
iOS(Native)部分
1.1、 iOS WKWebView的初始化部分
/// js 调用 原生 无参、无返回值的函数
private let jsCallNativeMethodName = "jsCallNativeMethod"
/// js 调用 原生 无参、有返回值的函数
private let jsCallNativeJsonStringMethodName = "jsCallNativeJsonStringMethod"
/// js 调用 原生 有参、无返回值或有返回值的函数
private let jsCallNativeJsonStringWithParamName = "jsCallNativeJsonStringWithParam"
/// 原生 调用 js 有参、无返回值或有返回值的函数
private let nativeCallScriptName = "nativeCallScript"
var webView: WKWebView!
var requestUrl:URL?
/// 初始化WebView
func initWebView() {
// 添加js对Native方法调用的监听
let jsCalliOSMethons = [jsCallNativeMethodName,
jsCallNativeJsonStringMethodName,
jsCallNativeJsonStringWithParamName,
nativeCallScriptName]
// native与JavaScript的交互管理
let config = WKWebViewConfiguration()
jsCalliOSMethons.forEach { name in
config.userContentController.add(self, name: name)
}
webView = WKWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width,
height: UIScreen.main.bounds.size.height), configuration: config)
webView.uiDelegate = self
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
webView.scrollView.contentInsetAdjustmentBehavior = .never
webView.scrollView.contentInset = .zero
webView.scrollView.insetsLayoutMarginsFromSafeArea = false
view.addSubview(webView)
if (requestUrl != nil) {
webView.load(URLRequest(url: requestUrl!))
} else {
let url:String = "http://192.168.1.4:7456/web-mobile/web-mobile/index.html"
requestUrl = NSURL(string: url)! as URL
webView.load(URLRequest(url: requestUrl!))
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 移除注册的js方法
webView.configuration.userContentController.removeAllScriptMessageHandlers()
}
1.2、WKWebView的代理
WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate
//postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void;
//postMessage(message: any, options?: WindowPostMessageOptions): void;
func userContentController(_ userContentController: WKUserContentController, didReceive messa
WKScriptMessage) {
// 通过接收JS传出消息的name,进行捕捉的回调方法
print("-- ⚠️native 收到 js 调用:\(message.name) body:\(message.body)")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("-- ⚠️native did finish web load --")
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error)
print("-- ⚠️native did fail \(error.localizedDescription) --")
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!,
withError error: Error) {
print("-- ⚠️native did fail provisional navigation \(error.localizedDescription) --")
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// 拦截到请求url
print("-- ⚠️native decide policy for \(String(describing: navigationAction.request.url))
")
if navigationAction.request.url?.scheme?.caseInsensitiveCompare("jsCall") == .orderedSame
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
//alert(message?: any): void;
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
print("-- ⚠️native alert message:\(message)")
let alertViewController = UIAlertController(title: "JS Alert Msg", message: message,
preferredStyle: .alert)
let sureAction = UIAlertAction(title: "Sure", style: .default) { action in
completionHandler()
}
alertViewController.addAction(sureAction)
self.present(alertViewController, animated: true)
}
// confirm(message?: string): boolean;
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
print("-- ⚠️native alert message:\(message)")
let alertViewController = UIAlertController(title: "JS Confirm Msg", message: message,
preferredStyle: .alert)
let sureAction = UIAlertAction(title: "Sure", style: .default) { action in
completionHandler(true)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { action in
completionHandler(false)
}
alertViewController.addAction(sureAction)
alertViewController.addAction(cancelAction)
self.present(alertViewController, animated: true)
}
// prompt(message?: string, _default?: string): string | null;
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String,
defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping
(String?) -> Void) {
print("-- ⚠️native run JS Text Input With Prompt:\(prompt)")
// let alertViewController = UIAlertController(title: "Text Input With Prompt", message: prompt, preferredStyle: .alert)
//
// alertViewController.addTextField { textField in
// textField.text = defaultText
// }
//
// let sureAction = UIAlertAction(title: "Sure", style: .default) { action in
// completionHandler(alertViewController.textFields?[0].text)
// }
// alertViewController.addAction(sureAction)
// self.present(alertViewController, animated: true)
switch prompt {
case jsCallNativeMethodName:// 无参、无返回值
self.jsCallNativeMethod()
completionHandler("")
return
case jsCallNativeJsonStringMethodName:// 无参、有返回值
completionHandler(self.jsCallNativeJsonStringMethod())
return
case jsCallNativeJsonStringWithParamName:
completionHandler(self.jsCallNativeJsonStringWithParam(param: defaultText ?? ""))
return
default:
// 不处理无效的prompt
completionHandler("error param")
return
}
}
1.3、Native和JS的通信方法
extension GGWebViewController {
//MARK: - 被js调用的iOS方法
/// js 调用 原生 无参、无返回值的函数
func jsCallNativeMethod() {
print("-- ⚠️ 这里是 原生 无参、无返回值的函数 --")
var paramToScript:String = "iOS 调用 js 方法传递的参数"
self.dispatchEventToScript(param: paramToScript)
}
/// js 调用 原生 无参、有返回值的函数
func jsCallNativeJsonStringMethod() -> String {
let jsonString = "⚠️ 这是 原生 无参、有返回值函数 返回给JS的值"
return jsonString
}
/// 调用 原生 有参、无返回值或有返回值的函数
/// 可通过param来判断是否需要返回值
func jsCallNativeJsonStringWithParam(param:String) -> String {
guard let funcJsonData = param.data(using: .utf8) else{
print("--- ⚠️解析出错1 ---")
return "param error"
}
do {
guard let funcInfoDic = try JSONSerialization.jsonObject(with: funcJsonData) as? Dictionary<String, Any> else{
print("---⚠️解析出错3 --")
return "json serialization error"
}
print("---⚠️web回调需要返回值的函数:\(funcInfoDic)")
let jsonString = "⚠️ Native接受JS参数,并回调给JS"
return jsonString
} catch let errore {
print("--- ⚠️解析出错2 ---:\(errore.localizedDescription)")
return errore.localizedDescription
}
}
//MARK: - iOS调用js的方法
func dispatchEventToScript(param: String) {
var callBack:String = nativeCallScriptName + "(\"\(param)\")"
webView.evaluateJavaScript(callBack) { response, error in
if (error != nil) {
print("-- ⚠️\(self.nativeCallScriptName) error:\(String(describing: error))")
} else {
print("-- ⚠️Native调用JS回调的参数:\(String(describing: response))")
}
}
}
}
Web部分(TypeScript)
2.1、Web访问Native的方法(访问的方法要在Native实现并注册)
// -- JS 主动调用 原生 方法的处理 (对象方法) --
// 调用 原生 无参、无返回值的函数
public requestMethod() {
console.log("-- requestMethod --");
window.prompt("jsCallNativeMethod");
}
// 调用 原生 无参、有返回值的函数
public requestMethodForValue() {
let jsonString = window.prompt("jsCallNativeJsonStringMethod");
console.log("-- requestMethodForValue" + jsonString + "--");
}
// 调用 原生 有参、无返回值的函数
public requestMethodWithParam() {
console.log("-- requestMethodWithParam --");
window.prompt("jsCallNativeJsonStringWithParam","这条message是传递给Native的参数");
}
// 调用 原生 有参、有返回值的函数
public requestMethodWithParamForValue() {
let jsonString = window.prompt("jsCallNativeJsonStringWithParam","{\"desc\":\"这条message是传递给Native的JSON字符串参数,同时该函数可接受Native返回给js的值\"}");
console.log("-- requestMethodWithParamForValue" + jsonString + "--");
}
2.3、Native访问Web的方法
/ -- 原生 主动调用 JS 方法的处理 (⚠️原生调用JS的方法一定要挂载到全局window下,否则Native会调用JS方法失败)--
// param:参数 可为空
// string:返回值 可为空
function nativeCallScript(param:string):string {
console.log("-- nativeCallScript --");
console.log(param);
// 原生驱动游戏角色运动
if (param != null) {
}
let jsonstring = "这条Message是 Native 主动调用 JS 方法,将Native传递过来的param处理后,回调给Native"+ "->param:(" + param + ")";
return jsonstring;
}
(<any>window).nativeCallScript = nativeCallScript;