swift&JS交互 - JavaScriptCore
自從iOS7之后Apple退出JavaScriptCore,極大的方便了iOS與H5的聯(lián)系俘枫。
一疹瘦、JavaScriptCore主要類
JSContext
:JSContext是JS的執(zhí)行環(huán)境巡球,通過evaluateScript()方法可以執(zhí)行JS代碼
JSValue
: JSValue封裝了JS與ObjC中的對應的類型,以及調(diào)用JS的API等
JSExport
: JSExport是一個協(xié)議险胰,遵守此協(xié)議,就可以定義我們自己的協(xié)議棚贾,在協(xié)議中聲明的API都會在JS中暴露出來妙痹,這樣JS才能調(diào)用原生的API
二鼻疮、直接通過JSContext執(zhí)行JS代碼
import JavaScriptCore //記得導入JavaScriptCore
let context: JSContext = JSContext()
let result1: JSValue = context.evaluateScript("1 + 1")
print(result1) // 輸出2
// 定義js變量和函數(shù)
context.evaluateScript("var num1 = 2; var num2 = 3;")
context.evaluateScript("function multiply(param1, param2) { return param1 * param2; }")
// 通過js方法名調(diào)用方法
let result2 = context.evaluateScript("multiply(num1, num2)")
print(result2 ?? "result2 = nil") // 輸出6
// 通過下標來獲取js方法并調(diào)用方法
let squareFunc = context.objectForKeyedSubscript("multiply")
let result3 = squareFunc?.call(withArguments: [2, 3]).toString()
print(result3 ?? "result3 = nil") // 輸出6
三判沟、通過JSContext注入模型,然后調(diào)用模型的方法
1挪哄、首先定義一個協(xié)議SwiftJavaScriptDelegate 該協(xié)議必須遵守JSExport協(xié)議
這里必須使用@objc
,因為JavaScriptCore
庫是ObjectiveC
版本的砸彬。如果不加@objc
拿霉,則調(diào)用無效果咱扣。定義兩個函數(shù),有參和無參兩個沪铭,帶有參數(shù)的注意補全杀怠。
// 定義協(xié)議SwiftJavaScriptDelegate 該協(xié)議必須遵守JSExport協(xié)議
@objc protocol MallH5BridgeProtocol: JSExport {
/// 登錄
///
/// - Parameter urlString: 登錄成功后跳轉(zhuǎn)的url
func login(_ urlString: String)
/// 掃碼
func goToScanCode()
}
2赔退、然后定義一個模型 該模型實現(xiàn)SwiftJavaScriptDelegate協(xié)議
創(chuàng)建一個模型類遵從上面的協(xié)議,如果需要修改UI等相關操作窗骑,我們需要在主線程中操作漆枚。
// 定義一個模型 該模型實現(xiàn)SwiftJavaScriptDelegate協(xié)議
@objc class MallH5Bridge: NSObject, MallH5BridgeProtocol {
weak var controller: MallH5ViewController?
weak var jsContext: JSContext?
/// js調(diào)用APP登錄
func login(_ urlString: String) {
DispatchQueue.main.async {
[weak webController = self.controller] in
guard AppLoginUserManager.default.isLogin == false else {
AppShare.default.showMessage(message: "您已經(jīng)登錄了哦")
return
}
AppShare.goToLoginVC(sourceVC: webController) {
if urlString.count > 0 && urlString != "undefined" {
webController?.redirect(toUrl: urlString)
}
}
}
}
/// 掃碼
func goToScanCode() {
DispatchQueue.main.async {
AppShare.goToGoodsQRCode(source: self.controller)
}
}
}
3软族、將模型注入到網(wǎng)頁中残制,暴露給JS
注入操作在webViewDidFinishLoad
代理方法中。
func webViewDidFinishLoad(_ webView: UIWebView) {
if let jsContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext {
let model = MallH5Bridge()
model.controller = self
model.jsContext = jsContext
jsContext.setObject(model, forKeyedSubscript: "WebViewBridge" as NSCopying & NSObjectProtocol)
jsContext.exceptionHandler = { (context, exception) in
print("exception:", exception as Any)
}
self.jsContext = jsContext
}
stopWebLoading()
}
4仰禽、JS調(diào)用swift方法
在JS方法中如下調(diào)用即可吐葵。注意這里的WebViewBridge
是你在注入時定義的名稱温峭,可以自己設置字支。
//以下為JS中的方法
openScan() {
(window as any)["WebViewBridge"].goToScanCode()
}
login() {
(window as any)["WebViewBridge"].login("http://www.baidu.com")
}
5堕伪、swift調(diào)用JS方法
在JS中創(chuàng)建方法sayHello(),最重要的是要將此方法綁定到window下否則swift調(diào)用不到蹄梢。(這里坑了我一天多)
componentDidMount() {
(window as any).sayHello = this.sayHello
(window as any).sayGoodbye = this.sayGoodbye
}
sayHello() {
alert('hello')
}
sayGoodbye(argument) {
let name = argument['name']
alert('goodbye ${name}')
}
當在JS中創(chuàng)建完成后禁炒,swift中如下調(diào)用有參數(shù)和無參數(shù)的JS方法幕袱。
//這里是swift調(diào)用無參數(shù)的JS方法
func sayHello() {
let jsHandlerFunc = self.jsContext?.objectForKeyedSubscript("sayHello")
jsHandlerFunc?.callWithArguments([])
}
//這里是swift調(diào)用有參數(shù)的JS方法
func sayGoodbye() {
let jsHandlerFunc = self.jsContext?.objectForKeyedSubscript("sayGoodbye")
let dict = ["name": "joeal"]
jsHandlerFunc?.callWithArguments([dict])
}
如果你要在componentDidMount
方法中直接調(diào)用原生方法悠瞬,那么可能會發(fā)生找不到方法的錯誤涯捻。其實這是因為方法還未注入完成障癌。你可以延遲一點調(diào)用:
componentDidMount() {
setTimeout(() => {
(window as any)["WebViewBridge"].hello();
}, 2000);
}