iOS-Swift快速进入Hybrid开发

前言:

Hybrid(混合)开发在现今的app开发中相当常见,本文的目的是从iOS端引导大家快速进入Hybrid开发。

正文:

首先,我们先来了解一下为什么混合开发是如此必要。无论是Android,还是iOS,如果你仅采用原生开发,那么你的每一次变动,都需要将应用打包后发给应用商店,等待审核成功后,用户安装了新版本才可以使用。这无疑是浪费了太多的时间。那么你自然会想到,把容易变化的页面放在html上,而将程序的固定模块用原生开发。也就是在客户端接入h5(即web前端,后文简称h5)的页面,完成我们所需的功能。

而由于h5的页面,不仅仅是展示功能,也包含用户的操作,因此还需要客户端与h5的互相调用,那么仅仅用webView显示,那是远远不够的。例如:h5页面需要上传相片,那么就需要客户端将相片传给h5。这里h5需要向客户端发送打开相机或相册的请求,客户端选中相片后,也需要向h5上传。那么,我们进入了今天的主题,Hybrid开发。

Hybrid开发的基础在于两点:

1:客户端对h5的调用

这里的关键方法只有一个

webView.evaluateJavaScript(javascriptString, completionHandler: nil)

其中webView是WKWebView的实例.
WKWebView是苹果在iOS 8之后推出的框架WebKit中的浏览器控件, 其加载速度比UIWebView快了许多, 但内存占用率却下降很多, 也解决了加载网页时的内存泄露问题. 现在的项目大多数只需适配到iOS 8, 所以用WKWebView来替换项目中的UIWebView是很有必要的.

evaluateJavaScript这个方法是向浏览器传递javaScript,其中参数javascriptString是JSON格式的字符串。
无论是向h5传递参数,还是调用方法,都要将执行的语句作为参数,并用WKWebView调用evaluateJavaScript来传递给h5。

2.h5调客户端的调用

关键方法也是只有一个

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {}

需要遵循WKScriptMessageHandler协议, 在本方法中获取h5端传递的事件和参数,接下来再调用客户端的原生方法。
其中事件和参数都包含再message参数的body属性里

3.实例演示

这里先给大家推荐一个第三方库AXWebViewController,该控制器自带WKWebView,并且对进度条等控件进行了封装,使用起来非常方便。只需要继承该控制器就可以了。

(1)首先引入AXWebViewController,然后新建一个控制器WebViewController并继承AXWebViewController,作为主控制器。

import AXWebViewController
class WebViewController: AXWebViewController {}

(2)为主控制器添加extension,遵循AXWebViewControllerDelegate

//MARK: -AXWebViewControllerDelegate
extension WebViewController: AXWebViewControllerDelegate {
    //开始加载
    func webViewControllerDidStartLoad(_ webViewController: AXWebViewController) {}
    //完成加载
    func webViewControllerDidFinishLoad(_ webViewController: AXWebViewController) {
        //DOM操作,对webView的基础设置
        //1.禁止用户选中文本
        webView.evaluateJavaScript("document.documetElement.style.webkitUserSelect='none';", completionHandler: nil)
        //2.禁止按住目标的手势
        webView.evaluateJavaScript("document.documentElement.style.webkitTouchCallout='none';", completionHandler: nil)
        //3.向h5注入所需要的数据,如:app版本号,token等
        self.invokeJavascript(method: "dataInit", data: ["token":token,"platform":"iOS","appVersion":sysManager.appVersion,"UUID":SysManager.main.uuid()])
    }
    //加载失败
    func webViewController(_ webViewController: AXWebViewController, didFailLoadWithError error: Error) {}
}

以上3个代理方法,分别对应webView的开始加载,完成加载和加载失败3种状态。
其中开始加载和加载失败两个代理方法里,我们根据自己的需求来实现。如加载失败,显示失败对应的页面等。
那么重点来看完成加载这个方法。在本方法中:
1,2分别是都是直接调用WKWebView的evaluateJavaScript方法,向h5注入语句达到1.禁止用户选中文本 2.禁止按住这个手势
3中所设置的是h5页面中的基础配置,其中调用了一个我们自己实现的方法,invokeJavascript,目的也是向h5传递javaScript。这里的方法dataInit和参数token,版本号等也都是和h5所约定好的。

func utimesInvokeJavascript(method:String, data:Any?) {
        do {
            let jsonData =  try JSONSerialization.data(withJSONObject: data!, options: [])
            let para = String(data: jsonData, encoding: .utf8)
            let javaScriptString =  "window.bridge.emit('"+method+"',"+para!+")"
            webView.evaluateJavaScript(javaScriptString, completionHandler: { (_, error) in})
        } catch {
            print(error)
        }
}

以上这个自定义的方法就是将参数和方法转化为JSON字符串,并传递给h5。其中的"window.bridge.emit('"+method+"',"+para!+")"也是和h5约定好的格式来接收。

之后每次想要向h5注入javaScript,也就是让h5响应客户端,我们都可以通过utimesInvokeJavascript来调用evaluateJavaScript.

(3)遵循WKScriptMessageHandler协议

//messageHanlderName为和h5约定好的通信名称
webView.configuration.userContentController.add(self, name: messageHandlerName)

实现协议方法

//MARK: -WKScriptMessageHandler
extension WebViewController: WKScriptMessageHandler {
    //负责接收h5对客户端原生方法的调用
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        let dictionary = message.body as! Dictionary<String,Any>
        let method = dictionary["method"] as? String
        let args = dictionary["args"] as? Dictionary<String,Any>
        self.reflectMethod(method:method, args: args)
    }
}

h5对客户端调用时,需要传递参数对象过来,就包含在message对象的body属性里。
这里面的method和args两个参数对应调用的方法和参数,也是h5和客户端所约定的方式
其中reflectMethod这个方法是自定义以响应h5对客户端的调用。

//响应javascript对原生方法的调用
    func reflectMethod(method:String?, args:Dictionary<String,Any>?) {
        if method == "goBack" {
            perform(#selector(back), with: args, afterDelay: 0.1)
        }
    }
    
    @objc func goBack() {
        navigationController?.popViewController(animated: true)
    }

例如我们定义了一个名为goBack的方法,那么h5只需按约定方式将goBack对应字符串放进method参数里再传递过来,我们就在识别后就可以响应此方法
(4)当h5页面加载出现异常可能会有以下情况

1.可能因为h5需要的参数未传递

//此方法属于AXWebViewControllerDelegate的代理方法
func webViewController(_ webViewController: AXWebViewController, didFailLoadWithError error: Error) {
        //对错误进行处理,如显示加载失败等
}

2.可能因内存过高而导致webView进程的结束,如多次上传图片

//此方法属于WKNavigationDelegate,因AXWebViewController遵循WKNavigationDelegate,因此可直接重写
override func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        //可调用webView.reload()重新加载,或者返回上一页面
}

(5)当需要调用时,我们只需要实例化WebViewController就好了

let webController = WebViewController(address: urlString)   //urlString为html地址
navigationController?.pushViewController(vc, animated: true)

总结:

那么再来总结一下关键点:
1.创建控制器,继承于第三方控制器AXWebViewController
1.使用WKWebView,通过webView.evaluateJavaScript(javaScriptString, completionHandler: nil)向h5注入javascript,使h5端响应客户端要求;
2.遵循WKScriptMessageHandler,实现代理方法func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage){},使客户端响应h5端要求
3.在func webViewControllerDidFinishLoad(_ webViewController: AXWebViewController) {}在完成h5端需要的基本配置,如token,uuid等
4.javaScript的传递格式需要客户端和h5端共同约定

以上就是iOS端快速接入Hybrid的方法,喜欢的朋友点个赞吧😊~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容