-
Notifications
You must be signed in to change notification settings - Fork 6
JavaScript 调用 Native 接口
首先,在你的视图中使用 DSBridge.WebView
而非 WKWebView
:
import class DSBridge.WebView
class ViewController: UIViewController {
// ......
override func loadView() {
view = WebView()
}
// ......
}
声明一个类型并加上 @Exposed
注释,它便成了一个 Interface
,其下的方法将被暴露给 JavaScript:
import Foundation
import typealias DSBridge.Exposed
import protocol DSBridge.ExposedInterface
@Exposed
class MyInterface {
func addingOne(to input: Int) -> Int {
input + 1
}
}
对于不想暴露的方法,加上 @unexposed
注释:
@Exposed
class MyInterface {
@unexposed
func localMethod()
}
除了 class
,你也可以声明 struct
或者 enum
作为 Interface
:
@Exposed
enum EnumInterface {
case onStreet
case inSchool
func getName() -> String {
switch self {
case .onStreet:
"Heisenberg"
case .inSchool:
"Walter White"
}
}
}
最后,将接口添加到 WebView
中。
注意,第二个参数 by
传入的是命名空间,传入 nil
或空字符串,则该 Interface
没有命名空间。同时只能有一个没有命名空间的 Interface
,每个命名空间下同时也只能有一个 Interface
,如果重复则后来者居上:
webView.addInterface(MyInterface(), by: nil) // `nil` works the same as ""
webView.addInterface(EnumInterface.onStreet, by: "street")
webView.addInterface(EnumInterface.inSchool, by: "school")
之后,你就可以从 JavaScript 调用这些方法了,注意在方法名前加上命名空间:
bridge.call('addingOne', 5) // returns 6
bridge.call('street.getName') // returns Heisenberg
bridge.call('school.getName') // returns Walter White
你完全可以声明多层的命名空间,如
a.b.c
等。
声明异步方法略有不同,方法的最后一个参数必须是一个闭包,你将通过这个闭包来返回你的响应:
@Exposed
class MyInterface {
func asyncStyledFunction(callback: (String) -> Void) {
callback("Async response")
}
}
从 JavaScript 调用时,对应地,将回调函数传入:
bridge.call('asyncStyledFunction', function(v) { console.log(v) });
// ""
// Async response
可以看到,调用之后会立刻收到一个空字符串返回,这是符合期望的。而我们的异步返回值则是在传入的回调 function 中获得的。
DSBridge 提供了一次调用、多次返回的功能,你只需要将给闭包增加一个 Bool
类型的参数,这个参数意味着是否已完成。响应时,若传入 false
,表示未完成,以后你还可以再次调用这个闭包来发送响应;若传入 true
,JS 端将删除回调函数,即不再接收对于本次调用的响应:
@Exposed
class MyInterface {
func asyncFunction(
input: Int,
completion: @escaping (Int, Bool) -> Void
) {
// 传入 `false` 要求 JS 保留回调函数
completion(input + 1, false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
completion(input + 2, false)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// 传入 `true` 则 JS 将删除回调函数
completion(input + 3, true)
}
// 之后再调用也不会有效果了
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
completion(input + 4, true)
}
}
}
JavaScript 调用:
bridge.call('asyncFunction', 1, function(v) { console.log(v) });
// ""
// 2
// 3
// 4
你可以在 JavaScript 代码中查询 Native 是否存在某个接口:
bridge.hasNativeMethod('test') // true
也可以指定接口的同/异步:
bridge.hasNativeMethod('test', 'syn') // true
bridge.hasNativeMethod('test', 'asyn') // false
你可以将 Interface
声明为 class
、struct
或 enum
。暂未支持 actor
,欢迎大家的想法。
你可以发送或接收这些类型的数据:
- String
- Int, Double 等(与 NSNumber 无缝转换的类型)
- Bool
- 标准的 JSON 顶层对象:
- Dictionary,必须可编码为 JSON
- Array,必须可编码为 JSON
DSBridge-Swift 无视 Interface
中的方法的参数名,无论调用名还是内部名,因此你可以使用任意的参数名。
关于参数,同步方法只能:
-
有 1 个参数,类型符合上述”支持的数据类型“
-
没有参数
关于返回值,同步方法可以:
- 有返回值,类型符合上述”支持的数据类型“
- 没有返回值
为了简便,使用 Allowed
代指上面说的”支持的数据类型“:
func name()
func name(Allowed)
func name(Allowed) -> Allowed
异步方法可以有 1 个或 2 个参数,不允许有返回值。
如果有 2 个参数,第 1 个参数类型必须符合上述”支持的数据类型“。
方法的最后一个参数必须是闭包,返回 Void
。关于参数,闭包只能:
- 有 1 个参数,类型符合上述”支持的数据类型“
- 有 2 个参数,第 1 个类型符合上述”支持的数据类型“,第 2 个必须是
Bool
类型
typealias Completion = (Allowed) -> Void
typealias RepeatableCompletion = (Allowed, Bool) -> Void
func name(Completion)
func name(RepeatableCompletion)
func name(Allowed, Completion)
func name(Allowed, RepeatableCompletion)
闭包可以是 @escaping
的;如果不是的话,请注意,你的方法应当快速执行、立即返回,否则将会阻塞主线程。