-
Notifications
You must be signed in to change notification settings - Fork 12
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 的;如果不是的话,请注意,你的方法应当快速执行、立即返回,否则将会阻塞主线程。