原文地址:edgardegas.github.io/dsbridge-sw…
大家好,今日介绍我新写的一个开源库:DSBridge-Swift,它是 DSBridge-iOS 的一个 Swift 翻新版。
DSBridge-iOS 是一个深受大家喜爱的 JavaScript Bridge,尽管现已尘封 6 年,但仍然广为人所用,也有不少新的 Issue。现在它面对一个比较大的问题,那便是 iOS 体系迭代。比方 iOS 16.4 推出的新 API:
@available(iOS 16.4, *)
func webView(
_ webView: WKWebView,
willPresentEditMenuWithAnimator animator: any UIEditMenuInteractionAnimating
) {
}
即使你设置了 dsuiDelegate
并且完成了这个办法,在网页选中文本、弹出修正栏的时分,这个办法仍然不会被调用。原因是按照 DSBridge-iOS 的规划,WKUIDelegate
中任何一个办法都有必要先在 DWKWebView
中完成一遍,它才可能转发给你的 dsuiDelgate
。
由于上述原因,这个库有必要要经过修正本身的源码才干匹配 iOS 体系的更新。这不契合[开闭准则]({% post_url 2024-02-04-再谈SOLID准则 %})。
DSBridge-Swift 选择不站在开发者和 WKWebView
之间。DSBridge-Swift 的 DSBridge.UIDelegate
只做了一件事,便是捕获来自 JS 的调用,而将其他的署理办法悉数转发给开发者自己设置的 WebView.uiDelegate
,由开发者自己决定是否完成、怎么完成。
因而也就没有 dsuiDelegate
了,直接设置 uiDelegate
就能够了。
DSBridge-iOS 默认会为你完成 alert
、confirm
和 prompt
的弹窗,但现在的局面便是,它所运用的 UIAlertView
现已被 iOS 弃用了。出于和上面相同的原因,DSBridge-Swift 选择由开发者自己完成这些呼应,比方经过设置 uiDelegate
,并完成 runJavaScriptConfirmPanelWithMessage
、runJavaScriptAlertPanelWithMessage
和 runJavaScriptTextInputPanelWithPrompt
来弹出弹窗。
一句话归纳便是: DSBridge.WebView
是一个原汁原味的 WKWebView
。
动态 VS 静态
在原来的 DSBridge-iOS 中,你的 JavaScript Object 有必要是 NSObject 子类,且每个你要露出给 JS 的办法都需求标示 @objc
。
在新的 DSBridge-Swift 中,你能够用纯 Swift 的类而不需求继承 NSObject
:
@Exposed
class MyInterface {
func returnValue() -> Int { 101 }
@unexposed
func localMethod()
}
只需求加上 @Exposed
宏就能将你的类型露出给 JS。不想露出的办法则加上 @unexposed
标示即可。
已然咱们现已绕过了动态,那你乃至能够用 struct
和 enum
来声明你的 Interface(对,JavaScriptObject 现在改名叫 Interface):
@Exposed
enum EnumInterface {
case onStreet
case inSchool
func getName() -> String {
switch self {
case .onStreet:
"Heisenberg"
case .inSchool:
"Walter White"
}
}
}
这就声明了一个非常漂亮的一体两面的接口集,供给 getName
接口。
其他比方参数、返回值、回调等,以及如何调用,都和原库相同。
基本原理与开闭准则
前面咱们提到了[开闭准则]({% post_url 2024-02-04-再谈SOLID准则 %}),DSBridge-Swift 充沛遵照了开闭准则。
首先 DSBridge-Swift 的 DSBridge.WebView
中几乎没有逻辑,一切逻辑都在作为中枢的拱心石 Keystone
中。
拱心石(英语:Keystone),是砖石拱门顶上的楔形石头以及圆形石头。这些石块是施工过程中最后一块安放的石头,它首要能将一切的石头固定在方位上。 — 维基百科
解析来自 JS 的调用
你能够修正 Keystone
的 jsonSerializer
和/或 methodResolver
:
(webView.keystone as! Keystone).jsonSerializer = MyJSONSerializer()
(webView.keystone as! Keystone).methodResolver = MyMethodResolver()
这两个目标担任将来自 JS 的调用转化为 IncomingInvocation
(DSBridge-Swift 关于来自 JS 的调用的封装)。
想用 SwiftyJSON 或许 HandyJSON?想修正传参格局?没问题,修正 jsonSerializer
就行。
还有比方 DSBridge-Swift 仅在开发环境中打印 JSON 序列化报错的详情;生产环境中,详细的目标或 JSON 字符串会被替换为*hashed*
或许一个空目标。假如你希望改动这一行为,你能够自己界说过错类型,而不运用 DSBridge.JSON
之下的。
Native 调用 JS
Keystone.javaScriptEvaluator
担任办理一切发向 JS 的音讯,模仿 DSBridge-iOS,它每 50ms 才履行一次 JS 脚本,防止履行过于频频,被 iOS “丢包”。原来的 DSBridge-iOS 只针对回调(呼应来自 JS 的异步调用)做了优化,Native 主动调用仍然会呈现丢包;DSBridge-Swift 则关于 Native 的主动调用也做了等待队列。
假如你需求做进一步的优化,或许不想要这样的优化,复原本来的体验,你彻底能够将 Keystone.javaScriptEvaluator
替换掉。
派发来自 JS 的调用
Keystone.invocationDispatcher
担任办理一切你注册的 Interface,并担任派发 IncomingInvocation
,你能够替换它,供给你自己的完成。
日志
DSBridge-Swift 的大部分日志都是经过 DSBridge.sharedLogger()
打印的,它调用 os_log API,不仅能够从 Xcode 控制台看到打印,也能够在体系的比方 macOS 的控制台流式传输。
为了契合开发者对 DSBridge-iOS 的原本行为的预期,以及为了匹配调用的数据结构,咱们难以抛出、传递和处理过错。包括新增的 Native 调用 JS 的 call(_:with:thatReturns:completion:)
API,尽管 completion
返回的是一个 Result<T, any Swift.Error>
,但也只是返回调用过程中的过错,Native 和 JS 之间并不能互相认错。
因而 DSBridge 将重度依靠日志。经过替换 DSBridge.sharedLogger()
,你能够供给自己的 DSBridge.ErrorLogging
,在测验中把打印出的过错用弹窗展现,或许在生产环境中将日志上签到渠道等。
拱心石
有了上面这样的可扩展性,你乃至能够修正 JS 端的代码,而无需修正 DSBridge-Swift 的源码。
在这之上,你乃至能够从头界说自己的拱心石,彻底替换掉从接收来自 JS 的原始字符串之后的一切逻辑。这需求你完成 DSBridge.KeystoneProtocl
,你能够使用或放弃 DSBridge-Swift 中的现成完成,打造一个彻底不同的 Bridge。