iOS下JS和原生交互,函数互调

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS下JS和原生交互,函数互调相关的知识,希望对你有一定的参考价值。

现在越来越多的APP都是H5和原生混合开发,这样确实方便快捷,但是H5的部分总避免不了很多与原生的交互,原生调JS函数还比较简单,原生的API函数stringByEvaluatingjavascriptFromString就可以完成需求,但是JS调原生的函数,系统没有提供API,所以很多人公司都采用标记位的形式完成,我们公司的也不例外,项目开发了很久,原生和JS交互一直是我负责,我们项目这块也要大量的交互操作,随便版本迭代,问题也越来越多,然后就想了很多办法去解决这个问题。

一,我原本的方案,拦截URL完成

拦截URL的方式就是在如下方法中拦截抛出的URL,URL中包含关键的标识字段,根据这些字段调用原生的函数,这样H5中的相应操作全部可以抛出包含关键字段的URL,然后原生根据各自的字段采取不同的操作,完成H5和原生的交互.

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

返回YES的话Webview继续加载新的URL,返回NO就不加载,这样的话凡是需要采取操作的URL都可以返回NO,让webview不再加载,只调用原生的函数就行。

但是项目中H5的页面实在太多,很多公共的接口都是固定的,例如弹出用户登录,播放视频,打开图片游览器,还有跳转商品,活动,新闻,通知详情,所以就需要封装出一个框架,让所有的公共接口都可以相应公共接口的JS和原生互调,我这里采取的是用分类的方式,在这里采用分类的方式,设置代理,也有一些技巧:

1,在分类中定义一个代理,并用运行时实现setter和getter,这里设定代理的协议是UIWebViewDelegate的好处就是外面感觉不到代理转换了,还以为是直接的设置webview的代理,对外面暴露的越少,使用越简单。

@property(nonatomic,weak) IBInspectable id<UIWebViewDelegate>customDelegate;

-(id<UIWebViewDelegate>)customDelegate

{

    return objc_getAssociatedObject(self, @selector(customDelegate));

}

-(void)setCustomDelegate:(id<UIWebViewDelegate>)customDelegate

{

    objc_setAssociatedObject(self, @selector(customDelegate), customDelegate, OBJC_ASSOCIATION_ASSIGN);

}

2,设置自己的delegate是自己,并且实现代理方法,

self.delegate = self;

3,关键的地方来了,先上代码吧:

//让每个webView有依然可以设置代理并且相应各自的方法

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

{

    //H5抛出的URL

    NSString *absoluteString = request.URL.absoluteString;

    BOOL action =  [self takeActionWith:absoluteString]; // 根据抛出的URL采取不同的操作

    if (!action) {

        return NO;

    }

    //执行每个webView自己的customDelegate代理方法

    if (self.customDelegate && [self.customDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {

        return [self.customDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];

    }

    return YES;

}

拦截到的URL后根据标识字段采取各自的操作,并且判断是否需要继续加载当前URL,然后调用代理的加载方法,除了公共的接口外,还可以让外面的每个地方都自定义自己需要的操作

4,完工,给用户提供接口

-(void)setUrlString:(NSString *)urlString block:(void(^)(NSArray *arr))postImage delegate:(id<UIWebViewDelegate>)delegate;

然后在这个方法里面可以加载URL,然后设置代理,调用这个分类的地方,如果需要自定义UIWebViewDelegate的代理方法 ,也可以设置delegate,然后实现自己需要单独完成的操作,对外使用webview没有丝毫变化,加载url,设置代理,实现代理方法。

 

至此,封装出一个公共接口让JS和原生互调的分类就算完成了,在最开始我也是这么做的。但是随便版本迭代,交互越来越多,关键字段越来越多,通过字符串比对的方法,很容易跟其他的URL冲突,然后得到不想要的效果,而且,字符串比对还有先后顺序,如果一个URL包含两个关键字段,那么哪个字符串比对写在前面,就先执行哪个标识字段对应的函数。

显然,这是不合理的,也不是我们能容忍的

二,规定抛出URL的字段的规范

于是,我就想如何去解决这个问题,不过思前想后,还是觉得URL抛出没有任何规范,仅仅只是判断URL中是否包含某些字段的做法,其他地方的字段H5那边随便配备,显然是非常不合理的,于是,上网看了很多方案后,和后台约束的URL抛出字段的规范:

func://name=leehonn/age=24

这样的字段简洁明了,func是函数名,后面的都是参数,我们可以通过OC提供的API方法,先通过"//"将URL分割成一个数组,数组的第一个元素就是函数名,再把后面的字符串通过"/"分割,取得各自的参数,这样做的好处就是没有多余的字段,不容易冲突,需要的字段也都在,解析起来也方便。

当取得函数名,就可以执行函数了:

 SEL originalSelector = NSSelectorFromString(absoluteString);

  [self performSelector:originalSelector];

至此大功告成,优化完毕,而且这块和安卓可以通用,安卓那边也可以通过反射去执行函数,但是据说那样做性能会降低,但是即使不用反射,就用原来的字符串比对,也比之前URL的格式规范不少,不过对于ios这边,确实是比之前有优化的。

三,采取第三方框架

后来还是感觉这种形式很蹩脚,就将眼光方向第三方框架,然后就找到了WebViewJavascriptBridge,星星数也挺多,然后就拿来用,另外,这个框架也是采取拦截URL的方式,只是他将细节封装起来了而已,而且这个框架也需要JS代码那边的配合,并且,这个框架也不像大家想的那样能够拦截JS的onClick事件,具体使用方法大家可以去github上看官方介绍,不过别忘了,这个框架是需要JS那边的代码配合的,需要JS为iOS专门适配一套,安卓那边也有自己的一套方案,如果你们做H5的同事不嫌麻烦,可以弄两套

还有据说可以通过stringByEvaluatingJavaScriptFromString函数就可以完成JS和原生的互调,有知道的大神希望分享下

以上是关于iOS下JS和原生交互,函数互调的主要内容,如果未能解决你的问题,请参考以下文章

Android中webview与JS交互互调方法

RN与原生交互——传参并带有回调

iOS WKWebView JS 与 原生交互小结

WebViewJavascriptBridge实现js与android和ios原生交互

RN系列:Android原生与RN如何交互通信

VUE 与 原生交互(iOS为主)