IOS应用通过UIWEBVIEW实现与JS交互

众所周知,苹果商店的审核是不允许应用加载外部可执行文件的。即如果你想要给APP增加功能就需要修改代码增加新功能的代码后重新上传至苹果商店,经过漫长的审核之后再上线。那么有没有办法绕过这个步骤去动态的添加新功能呢?答案是有的。我们可以通过拥有历史悠久的javascript来实现这个。是的,你没看错。就是javascript!具体怎么实现呢?别着急,接着往下看。
在iOS里打开网页是用UIWebView实现的。查一下它的API文档,发现有个方法叫stringByEvaluatingJavaScriptFromString。从字面意思就可以看出这个方法正是我们所要的。文档里对它的描述是这样的。

stringByEvaluatingJavaScriptFromString:
Returns the result of running a script.
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
Parameters
script
The script to run.
Return Value
The result of running script or nil if it fails.
Discussion
JavaScript execution time is limited to 10 seconds for each top-level entry point. If your script executes for more than 10 seconds, the web view stops executing the script. This is likely to occur at a random place in your code, so unintended consequences may result. This limit is imposed because JavaScript execution may cause the main thread to block, so when scripts are running, the user is not able to interact with the webpage.
JavaScript allocations are also limited to 10 MB. The web view raises an exception if you exceed this limit on the total memory allocation for JavaScript.

看起来很容易使用嘛。先创建一个UIWebView的实例。

//创建要打开的html文件的完整路径
NSBundle *bundle = [NSBundle mainBundle];
NSString *resPath = [bundle resourcePath];
NSString *filePath = [resPath stringByAppendingPathComponent:@"hello.html"];
//初始化一个UIWebView实例
myWebView = [[UIWebView alloc] initWithFrame:self.view.frame];
//加载指定的html文件
[myWebView loadRequest:[[NSURLRequest alloc] initWithURL:[[NSURL alloc] initFileURLWithPath:filePath]]];

下面就是我们的主角登场了。注意,这个方法得在WebView加载完成后执行。如下所示:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *jsCode = [NSString stringWithFormat:@"alert(1);"];
[myWebView stringByEvaluatingJavaScriptFromString:jsCode];
}

运行程序就可以看到一个由js弹出的弹窗了。
但是这仅仅是Objc调用js而已,怎样才能用js去调Objc的方法呢?如果能实现这一步,那么我们就可以实现提供一些系统层面的api去供js调用,从而去让js实现更多功能。查看一下UIWebViewDelegate的文档,会发现有如下接口

webView:shouldStartLoadWithRequest:navigationType:
Sent before a web view begins loading a frame.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

当一个web视图开始加载一个frame的时候会触发它。看起来很符合我们的需求。我们可以在页面里创建一个iframe,使用js代码去改变它的src来触发这个调用,从而实现js对Objc的调用。具体的实现方式就不在此赘述,有兴趣的可以看一下我写的这个简单测试。
github地址:https://github.com/feeyar/ObjcCallJS

在github上也找到了一个 oc 和 js 之间能够交互的类,可以看一下 https://github.com/marcuswestin/WebViewJavascriptBridge

后记:这篇文章只是提出了实现的一种思路,但实现起来不太优雅。其实还有很多思路,比如封装一个js委托类,把js对oc的调用缓存起来,在oc中定时的去检测缓存的调用队列做相应处理。推荐看看apache的Cordova项目源码 https://github.com/apache/incubator-cordova-ios

看到一篇很不错的文章,也顺道记录下来:

JavascriptCore(强烈推荐)

你可能感兴趣的:(iOS)