WKWebView-Bridge 在react-native封装的实现 iOS开发

需求:

1、监控与重写url跳转。

2、自定义Loading样式不够强大。

3、RN与Web通信的Bridge。

4、一些特殊应用:tel://,mailto://,微信的,甚至于一些自定义协议。

5、将来做自己的WebAPI。


实现

一、文件结构

OC文件示意图.png
js文件示意图.png

二、导入说明

参考链接:

UIWebView-bridge:GItHub链接

WKWebView:GitHub链接;

上述两个示例,一个可以实现RN与Web的通信,另一个无法通信却可以实现对加载进度的监控,方便我们实现自己的loading.

导入方式我们选择WKWebView的方式导入(项目中我已经导入完毕);
然后在导入的源代码里加入我们要实现的Bridge功能即可。

三、使用说明

1、监控与重写URL跳转
    /*
     *Allows custom handling of any webview requests by a JS handler. Return true
     *or false from this method to continue loading the request.
     *@platform ios
     */
    onShouldStartLoadWithRequest: PropTypes.func,

这个方法是webView的一个属性,可以绑定一个函数,例如:

<WKWebView  source={{uri:this.state.src}}
                    ref = {'webviewref'}
                    startInLoadingState={true}
                    renderLoading={()=><Text>正在加载页面...</Text>}
                    style={styles.webViewStyle}
                    onLoad = {()=>TestMBProgressManager.textExample("加载中.....")}
                    onShouldStartLoadWithRequest  = {(e)=>{
                      console.log(e);
                      return true;
                    }}
                    injectedJavaScript = {injectScript}
                    onBridgeMessage = { this.onBridgeMessage.bind(this) }
        />

在这个函数里我们就可以对当前加载的URL进行筛选和判断。

输出的日志示例如下:

{ target: 9,
canGoBack: false,
lockIdentifier: 1189458900,
loading: false,
title: '百度一下',
canGoForward: false,
 navigationType: -1,
  url: 'wkwvb://message1473669712719' }```

在获取到URL之后,返回false就是不让加载当前URL。

若是想跳转新的页面,只需重新设置soruce并reload即可。

###### 2、自定义Loading样式不够强大

在这里提供了多种方式去自定义loading。

(1)在react里自定义视图,设置样式。
  
  示例如下:

//WebView的属性之一,返回一个视图,在加载的时候呈现。
renderLoading={()=><Text>正在加载页面...</Text>}

(2) 通过native实现。

 在这里我创建了一个MBPrograssHUD的管理类,可以实现自定义文字、图片等的 加载图。在开始加载的时候,使他显示,加载完毕后,使其消失。

但不建议使用这种方法去实现。

###### 3、RN与Web通信的Bridge

我们用一个简单的打招呼的过程来理解Bridge的使用(这仅仅是一个示例,更复杂的逻辑,根据需求由我们的工程师自己去实现吧。)
首先在建立WebView的时候,我们为当前页面注入如下js代码,由Web向RN打招呼:(reactive中实现)

const injectScript = `
(function(){
if (WebViewBridge) {

    WebViewBridge.onMessage = function(message){
      if (message === "hello from react-native") {
        WebViewBridge.send("got the message inside webview");
      }
    };

    WebViewBridge.send("hello from webview");
    }

}());

`;



在native的RCTWebView.m中,我实现了以下方法:

//since there is no easy way to load the static lib resource in ios,
//we are loading the script from this method.

  • (NSString*)webViewBridgeScript{

    return NSStringMultiline((function (window) {
    'use strict';

    //Make sure that if WebViewBridge already in scope we don't override it.
    if (window.WebViewBridge) {
    return;
    }

    var RNWBSchema = 'wkwvb';
    var sendQueue = [];
    var receiveQueue = [];
    var doc = window.document;
    var customEvent = doc.createEvent('Event');

    function callFunc(func, message) {
    if ('function' === typeof func) {
    func(message);
    }
    }

    function signalNative() {
    window.location = RNWBSchema + '://message' + new Date().getTime();
    }

    //I made the private function ugly signiture so user doesn't called them accidently.
    //if you do, then I have nothing to say. :(
    var WebViewBridge = {
    //this function will be called by native side to push a new message
    //to webview.
    push: function (message) {
    receiveQueue.push(message);
    //reason I need this setTmeout is to return this function as fast as
    //possible to release the native side thread.
    setTimeout(function () {
    var message = receiveQueue.pop();
    callFunc(WebViewBridge.onMessage, message);
    }, 15); //this magic number is just a random small value. I don't like 0.
    },
    fetch: function () {
    //since our sendQueue array only contains string, and our connection to native
    //can only accept string, we need to convert array of strings into single string.
    var messages = JSON.stringify(sendQueue);

    //we make sure that sendQueue is resets
    sendQueue = [];
    
    //return the messages back to native side.
    return messages;
    

    },
    //make sure message is string. because only string can be sent to native,
    //if you don't pass it as string, onError function will be called.
    send: function (message) {
    alert(message);
    if ('string' !== typeof message) {
    callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string");
    return;
    }

    //we queue the messages to make sure that native can collects all of them in one shot.
    sendQueue.push(message);
    //signal the objective-c that there is a message in the queue
    signalNative();
    

    },
    onMessage: null,
    onError: null
    };

    window.WebViewBridge = WebViewBridge;

    //dispatch event
    customEvent.initEvent('WebViewBridge', true, true);
    doc.dispatchEvent(customEvent);
    }(window));
    );
    }


这是一段js代码,在每次加载完毕后,都为当前应用注入,旨在建立web与RN的通信通道。

在RN中,我们监听Web消息的方法如下:

onBridgeMessage(message) {

  // var webview = this.refs.webview.getDOMNode();
  // const { webviewref } = this.refs;
  const webview = this.refs['webviewref']
  ;
  switch (message) {
    case "hello from webview":
    webview.sendToBridge("hello from react-native");
    console.log('我们打招呼给WebView');
    break;
    case "got the message inside webview":
    console.log("we have got a message from webview!yeah!");
    break;
  }

}
//绑定到WebView的属性上面
onBridgeMessage = { this.onBridgeMessage.bind(this) }

每当web有消息发送过来的时候,这个方法都会被触发,然后我们就可以在RN里面根据我们自己的需求去处理相应的消息。

这里通过sendToBridge的方法,由RN向web发送消息。

然后又由注入的js代码中的WebViewBridge.onMessage这个函数接受消息,并做处理。(即我们最开始注入的js代码)。

这样,就实现了Bridge的通信。

###### 4、一些特殊应用:tel://,mailto://,微信的,甚至于一些自定义协议。

在native代码中,有这样一个方法:
  • (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler

在这个方法中:

NSURLRequest request = navigationAction.request;
NSURL
url = request.URL;
NSString* scheme = url.scheme;

 
这样就可以根据不同的scheme对不同的协议进行处理了。包括上述bridge,在这里也运用了scheme去实现的,部分代码:

if (isJSNavigation) {
decisionHandler(WKNavigationActionPolicyCancel);
}
else if (navigationAction.targetFrame && ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])) {
decisionHandler(WKNavigationActionPolicyAllow);
}
else {
if([scheme isEqualToString:@"wkwvb"]){
decisionHandler(WKNavigationActionPolicyCancel);
return;
}

if (![scheme isEqualToString:@"about"]) {
  [[UIApplication sharedApplication] openURL:url];
}
decisionHandler(WKNavigationActionPolicyAllow);

}


在这里wkwvb就是RN与web互相通信时,我自定义的协议,如果有其他的协议,都可以在这个方法中进行处理。具体的逻辑,就根据需求由工程师去实现了。

###### 5、将来做自己的WebAPI

具体逻辑,在将来,根据需求由工程师去实现。

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

推荐阅读更多精彩内容