美文网首页
Wkwebview 组件 封装 开闭原则

Wkwebview 组件 封装 开闭原则

作者: jianshudxw | 来源:发表于2019-03-24 20:54 被阅读0次

前言

公司以前已经对 WKWebView 进行了封装,但当时的需求很少,主要是展示网页和右上角分享等几个功能。当时主要是通过拦截 url的方式实现。

但现在的功能需求增多,比如选择图片,上次传图片,发布文章等。。。

一般我们用一个类比如 WebBrowerController 开打开所有的网页,本地的或服务器。 这里我用 swift 写代码。 详细代码见 用代码一步一步实现自己的 ios 架构 Hybird 目录

以前的方式缺点

  • 不好扩展新功能
  • URL 解析复杂,编码易出错

本文侧重封装,解决上面的缺点,所以不详细介绍 js ios 互调的知识点,网络上已经很多。我会以后在代码里将互调的功能补充。我使用Wkwebview 新的JS、 iOS互调的方式,和 新的架构依据 开闭原则 代码容易扩展。

目标

封装一个易于维护和扩展的 Wkwebview。

不易扩展的要代码

//注册 js
 web_.configuration.userContentController.add(self, name: "func1")
 web_.configuration.userContentController.add(self, name: "func2")
 web_.configuration.userContentController.add(self, name: "func3")
// js 回调处理
  if message.name == func1 {
     } else if message.name == func2  {
} else if message.name == func3
...
...
...

这样当功能增多时,这个文件变大 功能混乱, 并且要不停修改无法封装成底层库。

分析

分析耦合,主要是 注册JS和处理JS,将她们分离出去即可。

我们可以给 WebBrowerController 添加代理,由外部不同的代理去实现,不同的代理实现不同方法,由服务器告知需要调用哪些代理

服务器告知需要哪些代理

两种方案:

  1. 解析url
    在 调用 WebBrowerController 时 在url 中约定 详细代码见具体代码。
    主要代码:
    init(url: String) {
        super.init(nibName:nil, bundle:nil)
        guard let components = URLComponents(string: url) else {
            assertionFailure("malformed url: \(url)")
            return
        }
        self.url = URL(string: url)
//        DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) {
//            self.config(self.parseConfig(components.queryItems))
//        }
       config(parseConfig(components.queryItems))
    }
    func parseConfig(_ queryPairs: [URLQueryItem]?) -> Dictionary<String,Array<String>>? {
        var dic: Dictionary<String,Array<String>>? = nil
        _ = queryPairs?.map {
            if $0.name == webconfig, let value = $0.value {
                if  let data = Data(base64Encoded: value) {
                    dic = (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as? Dictionary<String,Array<String>>
                }
            }
        }
        return dic
    }
  • url 的方式请按照标准的url规则处理url。不然很容易解析失败!(url encode 、base64 编码等)
  1. WkWebview js 注册
    主要代码:
  lazy var web: WKWebView = {
        let web_ = WKWebView()
        web_.configuration.userContentController.add(self, name: webconfig) //url注册和这个注册 二选一
        return web_
    }()
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == webconfig {
        config(message.body as? Dictionary<String,Array<String>>)
    }
}

ios 调用对应的 代理

主要代码

    func config(_ serverDic: Dictionary<String,Array<String>>?) {
        if let modules = serverDic?["module"] {
            _ = modules.map { module in
                let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"]as! String
                let clsName = namespace + "." + module
                let cls = NSClassFromString(clsName) as! FeakModule.Type
                let instance = cls.init()
                instance.web = web
                instance.configWebView(web.configuration)
            }
        }
    }

这里需要说明一下:let cls = NSClassFromString(clsName) as! FeakModule.Type 我发现 swift 只能根据类名反射到类,无法根据 协议名 反射到 遵守协议的 类。 这里我使用了一个 FeakModule 其实相对于一个 抽象基类。

protocol WebBrowerDelegate {
    func configWebView(_ config: WKWebViewConfiguration);
}

class FeakModule: UIViewController, WebBrowerDelegate {
    var web: WKWebView!
    func configWebView(_ config: WKWebViewConfiguration) {
    }
}

再定义具体的代理子类如:

class Module1: FeakModule, WKScriptMessageHandler
class Module2: FeakModule, WKScriptMessageHandler

这样 就可以编译成功,当运行时,会生成具体的 Module1 或者 Module2

具体的子类 去做 js 注册 和 处理回调

class Module2: FeakModule, WKScriptMessageHandler {
    override func configWebView(_ config: WKWebViewConfiguration) {
        print("module2 config webview")
        config.userContentController.add(self, name: "module2_func1")
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "module2_func1" {
            print("js call ios module2_func1")
        }
    }
}

每增加一个新需求,就新建一个 子类。 代码清晰简洁了!

结果

如此处理后,WebBrowerController 就说一个基础组件,可以在里面做一些基础的公共的工作。以后不管增加什么功能,WebBrowerController 都不需要改变。做到了 开闭原则

相关文章

网友评论

      本文标题:Wkwebview 组件 封装 开闭原则

      本文链接:https://www.haomeiwen.com/subject/rlajvqtx.html