zoukankan      html  css  js  c++  java
  • JS&Swift相互交互

    1. 加载本地HTML文件
     
     
     
    x
     
     
     
     
    override func loadView() {
        super.loadView()
        let conf = WKWebViewConfiguration()
      //JS调用HTML时使用的name
        conf.userContentController.add(self, name: "wkbridge")
        self.wk = WKWebView(frame: CGRect(x: 0, y:20, width: self.view.frame.size.width, height: self.view.frame.size.height - 20), configuration: conf)
        self.wk.navigationDelegate = self
        self.wk.uiDelegate = self
        if let path = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "www") {
            let fileUrl = URL(fileURLWithPath: path)
            self.wk.loadFileURL(fileUrl, allowingReadAccessTo: fileUrl)
        }
        
        //每个页面注入 JS 插件代码 (runPluginJS方法在下面)
        self.runPluginJS(names: ["Base", "Console", "Sandbox"])
        //注入页面间传递的参数
        if pageParam != nil {
            self.wk.evaluateJavaScript("win.pageParam=(pageParam!)", completionHandler: nil)
        }
        self.view.addSubview(self.wk)
        
        //添加一个等待指示器
        self.view.backgroundColor = UIColor.white
        activityIndicator = UIActivityIndicatorView()
        activityIndicator.center = CGPoint(x: self.view.bounds.width/2, y: self.view.bounds.height/2)
        activityIndicator.color = UIColor.black
        activityIndicator.startAnimating()
        self.view.addSubview(activityIndicator)
    }
    //注入插件文件
    func runPluginJS(names: Array<String>) {
        for name in names {
            if let path = Bundle.main.path(forResource: name, ofType: "js") {
                do {
                    let js = try NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue)
                    self.wk.evaluateJavaScript(js as String, completionHandler: nil)
                } catch let error as NSError {
                    print(error.debugDescription)
                }
            }
        }
    }
     

    在项目根目录中新建 www 文件夹(自定义) 放入 html js css 图片 文件,前端文件都放在www中,方便管理

    注意

    将wkwebview需要访问的本地文件 添加到项目的 Bundle Resources

    1. HTML 与 Native 交互

    ViewController 实现 三个协议 WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler

    • JS调用Swift代码

      js直接使用

     
     
     
    xxxxxxxxxx
     
     
     
     
    // wkbridge为在WKWebViewConfiguration定定义的name
    // js传递的param使用json对象 ,比如类似{className: "Console", functionName: "log", data: {msg: "来自js的console"}}
    window.webkit.messageHandlers.wkbridge.postMessage(param);
     

    会触发swift方法

     
     
     
    xxxxxxxxxx
     
     
     
     
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "wkbridge" {
            if let dic = message.body as? NSDictionary,
               let className = (dic["className"] as AnyObject).description,
               let functionName = (dic["functionName"] as AnyObject).description {
                if let cls = NSClassFromString((Bundle.main.object(forInfoDictionaryKey: "CFBundleName")! as AnyObject).description + "." + className) as? Plugin.Type{
                    let obj = cls.init()
                    obj.viewController = self
                    obj.wk = self.wk
                    obj.taskId = (dic["taskId"] as AnyObject).integerValue
                    obj.data = (dic["data"] as AnyObject) as? NSDictionary
                    let functionSelector = Selector(functionName)
                    if obj.responds(to: functionSelector) {
                        obj.perform(functionSelector)
                    } else {
                        print("Undefined function :(functionName)")
                    }
                } else {
                    print("Class Not Found: (className)")
                }
            }
        }
    }
     
    • js的方法在 Xcode的控制台打印内容

    在swift新建类Console.swift

     
     
     
    xxxxxxxxxx
     
     
     
     
    import UIKit
    class Console: Plugin {
        func log() {
            if let string = self.data?["msg"] {
                print(string)
            }
        }
    }
     

    这里使用到一个基类 Plugin 用来处理 js 与 swift的交互

     
     
     
    xxxxxxxxxx
     
     
     
     
    import UIKit
    import WebKit
    class Plugin: NSObject {
        var viewController: MeiWebView!
        var wk: WKWebView!
        var taskId: Int!
        var data: NSDictionary?
        required override init() {
        }
        func callback(values: NSDictionary) -> Bool {
            do {
                let jsonData = try JSONSerialization.data(withJSONObject: values, options: JSONSerialization.WritingOptions())
                if let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue) as? String,
                   let tTaskId = self.taskId{
                    let js = "fireTask((tTaskId), '(jsonString)');"
                    self.wk.evaluateJavaScript(js, completionHandler: nil)
                    return true
                }
            } catch let error as NSError{
                NSLog(error.debugDescription)
                return false
            }
            return false
        }
        func errorCallback(errorMessage: String) {
            let js = "onError((self.taskId), '(errorMessage)');"
            self.wk.evaluateJavaScript(js, completionHandler: nil)
        }
        
        func convertToDictionary(text: String) -> [String: Any]? {
            if let data = text.data(using: .utf8) {
                do {
                    print(text)
                    print(data)
                    return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
                } catch {
                    print("JSON 转换失败 :" + error.localizedDescription)
                }
            }
            return nil
        }   
    }
     

    这样js的方法就会在 Xcode的控制台打印出 内容了

    对于隐藏接口,给前段一个友好的方式,使用自定义JS文件,使用上面的runJSPlugin直接写入到html中,在runJSPlugin 方法中 用到的js文件, 在项目中新建一个GROUP 统一管理,我觉得与www文件夹中的前端文件分离比较好。方法使用数组传递需要写入的JS的名称,这里列出Console.js

     
     
     
    xxxxxxxxxx
     
     
     
     
    var console = {
        log: function(message) { //console.log(msg);
            window.webkit.messageHandlers.wkbridge.postMessage({className: "Console", functionName: "log", data: {msg: message}});
        }
    };
     

    这样在js中只要执行 console.log() 就可以在Xcode控制台打印内容了。

    • Swift接口回调

    js 层使用队列管理, 新建Base.js

     
     
     
    xxxxxxxxxx
     
     
     
     
    Queue = [];
    function Task(id, callback, errorCallback) {
        var mTask = new Object;
        mTask.id = id;
        mTask.callback = callback;
        mTask.errorCallback = errorCallback;
        mTask.once = false;
        return mTask;
    }
    fireTask = function(i, j) {
        if (typeof Queue[i].callback == 'function') {
            Queue[i].callback(JSON.parse(j));
            if (Queue[i].once) Queue[i] = null;
        } 
    };
    onError = function (i, j) {
        Queue[i].errorCallback(j);
    };
     
    • 实现获取沙盒根目录

      Sandbox.swift

     
     
     
    x
     
     
     
     
    import UIKit
    class Sandbox: Plugin {
        
        func getRootPath() {
            let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let dic = NSDictionary.init(object: path.absoluteString, forKey: "rootPath" as NSCopying)
            _ = callback(values: dic)
        }
    }
     

    新建 Sandbox.js

     
     
     
    xxxxxxxxxx
     
     
     
     
    Sandbox = {
        getRootPath: function(onSuccess, onError) {
            Queue.push(Task(Queue.length, onSuccess, onError));
            window.webkit.messageHandlers.wkbridge.postMessage({className: "Finder", functionName: "getRootPath", taskId: Queue.length - 1});
        },
    };
     

    js中执行

     
     
     
    xxxxxxxxxx
     
     
     
     
    Sandbox.getRootPath(function(path){
        console.log(path.rootPath); //获取沙盒路径
    })
     
    • Swift调用JS代码
     
     
     
    xxxxxxxxxx
     
     
     
     
    let js = "jsFunction(param);"
    self.wk.evaluateJavaScript(js, completionHandler: nil)
     
    1. 访问沙盒文件
    • 将文件转换成base64的字符串传递给js, src直接设置data。
    • WKWebView 只能读取tmp中的文件,所以将文件写入到 NSTemporaryDirectory() 的路径下面就可以了,以后js需要访问什么文件,将文件复制到tmp文件夹就可以访问了,按需删除就好了。
  • 相关阅读:
    阿里巴巴微服务开源项目盘点(持续更新)
    云计算、大数据、编程语言学习指南下载,100+技术课程免费学!这份诚意满满的新年技术大礼包,你Get了吗?
    【机器学习PAI实战】—— 玩转人工智能之综述
    泡沫下的破浪者,智能语音产品到底落地何处?
    我们总结了每个技术团队都会遇到的 4 个难题
    在 Ali Kubernetes 系统中,我们这样实践混沌工程
    云上护航服务—保障云上的尖峰时刻
    本地 vs. 云:大数据厮杀的最终幸存者会是谁?— InfoQ专访阿里云智能通用计算平台负责人关涛
    WAF开放规则定义权:专家策略+用户自定义策略=Web安全
    队列的其本应用_迷官问题
  • 原文地址:https://www.cnblogs.com/AliliWl/p/14039249.html
Copyright © 2011-2022 走看看