swift&JS交互 - JavaScriptCore

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等相關(guān)操作,我們需要在主線程中操作。

// 定義一個模型 該模型實現(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)用不到。(這里坑了我一天多)

images.jpeg
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);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容