Swift与JS的故事

背景:

最近帮公司VP孩子稍微指导了一下Swift及OS X程序,于是回忆之前自学过的Swift,回忆青春,回忆过去,觉的这些都不该被我们忘掉的东西就应该记录下来,多年后证明自己曾经无比热爱过。

契机:

我们现在项目都是JS编写内容,iOS只在学习的时候加载这一堆资源包,那么就产生了Swift(OC,暂时不列出来,用法基本一样)与JS通信的问题

1、JS调用Swift
2、Swift调用JS

知识点:

1、继承JSExport协议,增加和JS相关约定好的方法,如下:

import Foundation
import JavaScriptCore

@objc protocol ZBJSDelegate:JSExport {
    ///打印日志
    func printJSLog(_ log: String)
    ///显示弹窗
    func showAlert(_ msg: String)
    ///打开新页面
    func openNewPage(_ url: String)
    ///待返回值的调用
    func callAndReturn() -> Int
    //js让我回调他
    func callMe()
    
    func callMe1()
}

2、再创建一个专门用于通信的类,继承协议,实现代理方法,如下:

代码如下

import UIKit
import JavaScriptCore

class ZBJSCallSwiftClass: NSObject, ZBJSDelegate {
    var controller: UIViewController?
    var jsContext: JSContext?
    weak var delegate: ZBJSDelegate?
    override init() {
        super.init()
    }
    
    //实现jsDelegate的方法
    func printJSLog(_ log: String) {
        print(log)
        if self.delegate != nil {
            self.delegate!.printJSLog(log)
        }
    }
    
    func showAlert(_ msg: String) {
        print(msg)
    }
    
    func openNewPage(_ url: String) {
        if self.delegate != nil {
            self.delegate?.openNewPage(url)
        }
    }
    
    func callAndReturn() -> Int {
        return 666
    }
    
    func callMe() {
        if self.delegate != nil {
            self.delegate?.callMe()
        }
    }
    
    func callMe1() {
        self.jsContext?.evaluateScript("callBack('ZB真的好帅callMe1')")
    }
}

3、这里就是你们需要加载webView的地方(业务层)

(1)这样分开的好处是,JS需要获取一些配置信息等等与业务无关的,可以在ZBJSCallSwiftClass中完成,与业务层相关的就放在业务层中处理。

(2)在webView的代理方法中webViewDidFinishLoad,先获取JSContext对象。

(3)JSContext对象表示JavaScript执行环境。您创建并使用JavaScript上下文来评估来自Objective-C或Swift代码的JavaScript脚本,访问在JavaScript中定义或计算的值,以及使JavaScript可以访问本机对象、方法或函数。

(4)JSValue实例是对JavaScript值的引用。您可以使用JSValue类在JavaScript和Objective-C或Swift表示之间转换基本值(比如数字和字符串),以便在本机代码和JavaScript代码之间传递数据。您还可以使用这个类来创建JavaScript对象,这些对象包装自定义类或JavaScript函数的本机对象,其实现由本机方法或块提供。

(5)JSValue(object:self.swiftObj, in:self.jsContext) 转换原生对象会创建一个JavaScript对象,包括一个构造函数和原型链,它反映了对象在Objective-C或Swift类型层次结构中的继承。默认情况下,转换对象上的属性和方法不会暴露给JavaScript:要选择哪些属性和方法应该对JavaScript可见,请参阅JSExport。创建包装本机对象的JSValue实例将保留底层Objective-C或Swift对象。

(6)JSManagedValue对象封装JSValue对象,添加“条件保留”行为来提供值的自动内存管理。托管值的主要用例是将JavaScript值存储在Objective-C或Swift对象中,该对象本身被导出到JavaScript。

(7)self.jsContext?.setObject(self.swiftObj, forKeyedSubscript:"swift"asNSCopying&NSObjectProtocol) 该方法首先从关键参数构造JSValue对象,然后在JavaScript中使用该值设置上下文的全局对象中的属性。使用此方法(或Objective-C下标语法)来桥接JavaScript中使用的本机对象或函数

self.swiftObj:要为JavaScript属性设置的值。

swift:要在上下文的全局JavaScript对象中使用的JavaScript属性名。

意思就是说,JS中可以直接swift.printJSLog('ZB666!')这么访问Swift方法了。

(8) 那么关键的是调用Swift call JS self.webView.stringByEvaluatingJavaScript(from: "callBack('ZB真是帅帅的(callMe)!')") 、self.jsContext?.evaluateScript("callBack('ZB真的好帅callMe1')")

代码如下:

import UIKit
import JavaScriptCore

class ZBWebViewController: UIViewController, UIWebViewDelegate, ZBJSDelegate {
    
    func printJSLog(_ log: String) {
        print("别慌,爸爸正在打印")
    }
    
    func callAndReturn() -> Int {
        return 111
    }
    
    func callMe() {
        DispatchQueue.main.async {
            self.webView.stringByEvaluatingJavaScript(from: "callBack('ZB真是帅帅的(callMe)!')")
        }
    }
    
    func callMe1() {}
    
    let webView = UIWebView()
    let width = UIScreen.main.bounds.size.width
    let height = UIScreen.main.bounds.size.height
    var jsContext: JSContext?
    let swiftObj = ZBJSCallSwiftClass()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "webView"
        self.webView.frame = CGRect(x: 0, y: 0, width: width, height: height)
        self.webView.backgroundColor = UIColor.gray
        self.view.addSubview(self.webView)
        
//        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first! + "/test.html"
        
        let path: String = Bundle.main.path(forResource: "test", ofType: "html")!
        self.webView.delegate = self
        let pathURL = NSURL.fileURL(withPath: path)
        var content = ""
        do {
            content = try String.init(contentsOf: pathURL, encoding: String.Encoding.utf8)
        } catch  { }
        
        self.webView.loadHTMLString(content, baseURL: pathURL)
        self.swiftObj.delegate = self
        
    }

    func webViewDidStartLoad(_ webView: UIWebView) {
        
    }
    
    func webViewDidFinishLoad(_ webView: UIWebView) {
        self.jsContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext
        self.swiftObj.jsContext = self.jsContext
        //遵循遵循一个retain机制
        let jsObj = JSValue(object: self.swiftObj, in: self.jsContext)
        let managedValue = JSManagedValue(value: jsObj)
        self.jsContext?.virtualMachine.addManagedReference(managedValue, withOwner: self.swiftObj)
        self.jsContext?.setObject(self.swiftObj, forKeyedSubscript: "swift" as NSCopying & NSObjectProtocol)
    }
    
    func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {}
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func showAlert(_ msg: String) {}
    
    func openNewPage(_ url: String) {
        DispatchQueue.main.async {
            let webView = UIWebView()
            webView.frame = CGRect(x: 0, y: 64, width: self.width, height: self.height-64)
            self.view.addSubview(webView)
            webView.loadRequest(URLRequest(url: URL(string: url)!))
        }
    }
}

4、JS调用Swift代码

代码如下




    
    Document
    
    


    
    


js调用App的printJSLog
演示最基本的调用及参数传递
js调用App的打开新页面
演示打开新页面
js调用App的弹出对话框方法showAlert()
弹出框演示
js调用App的方法后 App再调用js函数执行回调
App调用js函数执行回调时 内容会改变
js调用App的方法后 App再调用js函数执行回调1
App调用js函数执行回调时 内容会改变1

你可能感兴趣的:(Swift与JS的故事)