如何在UIWebView中调用Swift方法

在UIWebView中调用Swift方法

在UIWebView中和Swfit代码交互

App中内嵌网页,有时候需要调用Native功能。
为了支持iOS和Android双平台,网页中调用native函数的代码的大概是下面的样子

1
2
3
……
<button click="nativeObj.func1(str)" …… />
……

因为默认情况下,Android的时把一个java对象设置给javascript context的nativeObj对象。

Swift中使用JavaScriptCore和UIWebView交互

  • 使用block实现

实现方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
extension WebViewController: UIWebViewDelegate {
//实现UIWebView委托方法
func webViewDidFinishLoad(_ webView: UIWebView) {
//获取JSContext上下文
if let context = self.webView!.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext{

//这里假定函数直邮一个参数
//@convention(block) 表示这是一个block规范的函数
//这里用到了self对象,需要注意循环引用造成内存泄漏
let blockFunc: @convention(block) (String) -> Void = {
[weak self] (str) in

DispatchQueue.main.async {[weak self] in
……
}
}
//把block 转换为objc 对象
let convertObjc = unsafeBitCast(blockJump, to: AnyObject.self)

//在javascript上下文中创建一个新对象
let jsValueNote = JSValue.init(newObjectIn: context)
//把block函数设置为新创建对象的属性,这里注意属性名就是网页中的方法名
jsValueNote?.setObject(convertObjc, forKeyedSubscript: "func1" as (NSCopying & NSObjectProtocol)!)
//把新创建的对象设置在上下文赋值给nativeObj属性。
context.setObject(jsValueNote, forKeyedSubscript: "nativeObj" as (NSCopying & NSObjectProtocol)!)
}
}
}
  • 使用JSExport实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    @objc protocol BackProtocol: JSExport{
    //使用_生成和javascript的方法一样的签名
    func func1(_ str: String) -> Void
    }

    //不要忘记派生自NSObject类
    class JSExportObj: NSObject, BackProtocol{

    var funcAction: (()-> Void)?

    public func func1(_ link_key: String) {
    funcAction?()
    }

    }

    extension WebViewController: UIWebViewDelegate {
    //实现UIWebView委托方法
    func webViewDidFinishLoad(_ webView: UIWebView) {
    //获取JSContext上下文
    if let context = self.webView!.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext{

    //创建一个临时对象
    let obj = JSExportObj()
    //设置临时对象的回掉函数,这里使用weak修饰符来避免循环引用造成内存泄漏
    obj.funcAction = {
    [weak self] in
    DispatchQueue.main.async {[weak self] in
    ……
    }
    }

    context.setObject(jsValueNote, forKeyedSubscript: "nativeObj" as (NSCopying & NSObjectProtocol)!)
    }
    }
    }

内存泄漏

上下文如果持有self对象,那么self对象将无法释放。造成内存泄漏。
这是因为iOS采用引用计数来管理内存,而javascript采用gc来管理内存。
奇怪的是,即使在viewcontroller中把webview设置为nil,仍然无法释放。这估计是因为iOS中的 js VM 继续持有了JSContext,没有及时释放。