前言

其实这两年的更新频次相比21年是下降很多的,主要自22年之后,手上的项目就接连不断,被客户项目整的死去活来,当然在目前这个方式下,有活就说明能持续作业,我应该感到欣喜。

其实,就我自己的认知而言,我觉得自己很难写出十分艰深的技术文章,我也一向为此而感到苦恼与不安,毕竟谁都有一个大神梦,不过我或许只能在此停驻吧。

今天给咱们共享一例用enum处理JS传递给Native侧的事情音讯,算是在作业中总结的一点小实践,假如能够给咱们一些启发就好了。

JS事情在Native侧的监听

在App开发过程中,咱们会运用到H5页面,而加载H5页面,或许就不可避免需要和Web进行交互。

在iOS侧,咱们一般都会运用到WebKit中,WebView相关的API进行设置与交互,其核心代码如下:


let config = WKWebViewConfiguration()
config.userContentController.add(WeakScriptMessageDelegate(scriptDelegate: self), name:"showNativeAlert")
let preferences = WKPreferences()
preferences.javaScriptCanOpenWindowsAutomatically = true
config.preferences = preferences
let webView = WKWebView(frame: view.bounds, configuration: config)

在初始化WebView的时分,咱们对WebView的配置项里边config.userContentController.add(WeakScriptMessageDelegate(scriptDelegate: self), name:"showNativeAlert")咱们增加了一个叫"showNativeAlert"的监听事情,也就是说当JS那边调用下面这个方法的时分,咱们能够在署理的回调中监听到:

JS侧触发事情

    window.webkit.messageHandlers.showNativeAlert.postMessage('来个原生弹窗');

Native侧监听事情

    extension MyJueJinController: WKScriptMessageHandler {
      func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("方法名:\(message.name)")
        print("参数:\(message.body)")
        
            if message.name = "showNativeAlert" {
                print("监听了JS给Nativ的showNativeAlert方法,触发对应的逻辑方法")
            }
      }
    }

一起,咱们在Controller析构的时分要记住移除"showNativeAlert"这个句柄:

deinit {
    webView.configuration.userContentController.removeScriptMessageHandler(forName: "showNativeAlert")
}

咱们能够看到在整个Native侧,咱们一向都在运用"showNativeAlert"这个硬编码字符串,假如咱们WebView监听的事情不多,这样写如同也没什么问题,可是假如监听的事情多了,这样写既不高雅,一起持续维护的成本也会特别高。一起,硬编码的字符串也是危险。

你不得不在func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)写更多的if else if或者switch:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print("方法名:\(message.name)")
    print("参数:\(message.body)")
    if message.name = "showNativeAlert" {
        print("监听了JS给Nativ的showNativeAlert方法,触发对应的逻辑方法")
    } else if message.name = "goToNext" {
    } else {
    }
    switch message.name {
    case "showNativeAlert":
        break
    case "goToNext":
        break
    default:
        break
    }
}

难道就没有什么好的方法去处理这些JS传递给Native侧的事情音讯的句柄吗?

假如你看过Swift:通过Protocol封装统和入参这篇文章,你一定会有主意的。

这儿,我先说结论,通过enum来处理JS传递给Native侧的事情音讯的句柄。

你们或许会有疑惑,为什么又又又又是enum,且听我娓娓道来。

运用enum的优势

  • enum能够很好的处理硬编码字符串
enum ScriptMessageHandlerType: String {
  case showNativeAlert
  case goToNext
    case helloWorld = "hello_world"
}

enum ScriptMessageHandlerType这种”看似“继承String的方式,实际上是给每个枚举值界说了一个rawValue,而且它直接映射的就是其字符串编码,比方这样打印:

print(ScriptMessageHandlerType.showNativeAlert.rawValue)
"showNativeAlert"

一起咱们关于枚举值的rawValue也能够自己进行界说与映射,比方上面代码的case helloWorld = "hello_world",咱们对其打印:

print(ScriptMessageHandlerType.helloWorld.rawValue)
"hello_world"

一起咱们也能够容易的将一个匹配的字符串转成枚举值,比方下面:

let type = ScriptMessageHandlerType(rawValue: "showNativeAlert")

type此刻是一个可选类型,因为ScriptMessageHandlerType(rawValue: "")假如是一个匹配不上的字符串,那么此刻type就nil,在实战运用的时分咱们需要留意。

  • enum能够遵守CaseIterable协议,简略容易的将一切的枚举事情转为数组
extension ScriptMessageHandlerType: CaseIterable {}

看见上面这个理由,你或许会觉得,转成数组和监听JS事情有什么关系?

记住这段代码吗:

/// 添加监听句柄
config.userContentController.add(WeakScriptMessageDelegate(scriptDelegate: self), name:"showNativeAlert")
/// 移除监听句柄
webView.configuration.userContentController.removeScriptMessageHandler(forName: "showNativeAlert")

假如咱们有更多的句柄需求,这段代码你或许就多写几回了,

不过假如此刻咱们运用的是enum,那么写起来就不费吹灰之力了:

/// 运用for循序添加句柄
for type in ScriptMessageHandlerType.allCases {
    config.userContentController.add(WeakScriptMessageHandlerDelegate(delegate: self), name: type.rawValue)
}
/// 运用for循序移除句柄
for type in ScriptMessageHandlerType.allCases {
    webView.configuration.userContentController.removeScriptMessageHandler(forName: type.rawValue)
}

是不是这样看起来,十分简练了呢?

  • enum与switch配合运用,不会遗漏逻辑

终究让我来看看怎么处理监听的JS事情,咱们重构了func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)里边代码:

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("方法名:\(message.name)")
        print("参数:\(message.body)")
        guard let type = ScriptMessageHandlerType(rawValue: message.name) else {
      return
    }
    switch type {
    case .showNativeAlert:
      break
    case .goToNext:
      break
    case .helloWorld:
      break
    }
    }

因为此刻ScriptMessageHandlerType类型是有穷的,所以不用写default这个判断。当你在ScriptMessageHandlerType枚举类型中新增枚举值的时分,假如在switch type中忘掉增加新增枚举值的代码判断,会直接编译报错,从而下降因为新增句柄而忘掉处理新增句柄逻辑的情况产生。

所以,基于以上几点原因,我用enum处理JS传递给Native侧的事情音讯,各位觉得怎么样?

总结

这篇文章,写了这么多,成果到头来,终究还是在说enum的用法。

讲真的,Swift的enum真的十分强壮,强壮到有的时分我自己在运用的时分也会感叹居然能够这样?

假如持续做Swift开发,咱们无妨这样来反推一下自己代码,这儿可不能够试试enum?或许一个好点子就来了。

参阅文档

Swift:通过Protocol封装统和入参

自己写的项目,欢迎咱们star⭐️

RxStudy:RxSwift/RxCocoa框架,MVVM模式编写wanandroid客户端。

GetXStudy:运用GetX,重构了Flutter wanandroid客户端。

附上WeakScriptMessageDelegate的相关代码


    /// 专用WKScriptMessageHandler的署理层
    class WeakScriptMessageDelegate: NSObject {
      //MARK: - 特点设置 之前这个特点没有用weak润饰,所以一向持有,无法释放
      private weak var scriptDelegate: WKScriptMessageHandler!
      //MARK: - 初始化
      convenience init(scriptDelegate: WKScriptMessageHandler) {
        self.init()
        self.scriptDelegate = scriptDelegate
      }
    }
    extension WeakScriptMessageDelegate: WKScriptMessageHandler {
      func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        scriptDelegate.userContentController(userContentController, didReceive: message)
      }
    }