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代码

代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <meta name="viewport"
        content="width=device-width,initial-scale=1,minimum-scale=1, maximum-scale=1, user-scalable=no">
    <style>
        .btn-block {
            display: block;
            height: 45px;
            line-height: 45px;
            margin: 10px auto;
            color: #fff;
            background: #8bc53f;
            border-radius: 10px;
            text-align: center;
            text-decoration: none;
            font-size: 14px;
        }
    
        .content {
            text-align: left;
            font-size: 14px;
            margin-bottom: 30px;
        }
    </style>
</head>
<body>
    
    <br><br><br>

    <div class="btn-block" onclick="swift.printJSLog('鸡哥666!')">
        js调用App的printJSLog
    </div>
    <div class="content">演示最基本的调用及参数传递</div>
    
    
    <div class="btn-block" onclick="swift.openNewPage('www.baidu.com')">js调用App的打开新页面</div>
    <div class="content">演示打开新页面</div>
    
    
    <div class="btn-block" onclick="swift.showAlert('Hello World')">
        js调用App的弹出对话框方法showAlert()
    </div>
    <div class="content">弹出框演示</div>
    
    
    <div class="btn-block" onclick="swift.callMe()">
        js调用App的方法后 App再调用js函数执行回调
    </div>
    <div class="content" id="js-content">App调用js函数执行回调时 内容会改变</div>
    
    <div class="btn-block" onclick="swift.callMe1()">
        js调用App的方法后 App再调用js函数执行回调1
    </div>
    <div class="content" id="js-content">App调用js函数执行回调时 内容会改变1</div>
    
    <script type="text/javascript">
        
        function jsHandlerFunc(argument) {
            document.getElementById('js-content').innerHTML = "App调用js回调函数啦, 我是" + argument['name'];
        }
    
        function callBack(str) {
            swift.printJSLog(str)
        }
    
    </script>
</body>
</html>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354