我爱编程

iOS Native 与 JS 交互

2017-11-06  本文已影响179人  流星Meteor

对于 iOS Native 与 JS 交互我们先从调用方向上分为两种情况来看:

JS 调用 Native
Native 调用 JS

  1. JS 调用 Native

其实 JS 调用 iOS Native 也分为两种实现方式:

a. 假 Request 方法
b. JavaScriptCore 方法

a. 假 Request 方法

原理:其实这种方式就是利用了 webview 的代理方法,在 webview 开始请求的时候截获请求,判断请求是否为约定好的假请求。如果是假请求则表示是 JS 想要按照约定调用我们的 Native 方法,按照约定去执行我们的 Native 代码就好。

① UIWebView

UIWebView 代理有用于截获请求的函数,在里面做判断就好:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = request.URL;
    // 与约定好的函数名作比较
    if ([[url scheme] isEqualToString:@"your_func_name"]) {
        // just do it
    }
}

② WKWebView

WKWebView 有两个代理,一个是 WKNavigationDelegate,另一个是 WKUIDelegate。WKUIDelegate 在另一篇文章中讲到,这里我们需要设置并实现它的 WKNavigationDelegate 方法:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURL *url = navigationAction.request.URL;
    // 与约定好的函数名作比较
    if ([[url scheme] isEqualToString:@"your_func_name"]) {
        // just do it
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }

    decisionHandler(WKNavigationActionPolicyAllow);
}

decisionHandler 是当你的应用程序决定是允许还是取消导航时,要调用的代码块。 该代码块使用单个参数,它必须是枚举类型 WKNavigationActionPolicy 的常量之一。如果不调用 decisionHandler 会引起 crash。

这里补充一下 JS 代码:

function callNative() {
    loadURL("your_func_name://xxx");
}   

然后用一下就好了

b. JavaScriptCore 方法

iOS 7 有了 JavaScriptCore 专门用来做 Native 与 JS 的交互。我们可以在 webview 完成加载之后获取 JSContext,然后利用 JSContext 将 JS 中的对象引用过来用 Native 代码对其作出解释或响应:

// 首先引入 JavaScriptCore 库
#import <JavaScriptCore/JavaScriptCore.h>

// 然后再 UIWebView 的完成加载的代理方法中
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    // 获取 JS 上下文
    jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    // 做引用,将 JS 内的元素引用过来解释,比如方法可以解释成 Block,对象也可以指向 OC 的 Native 对象哦
    jsContext[@"iosDelegate"] = self;
    jsContext[@"yourFuncName"] = ^(id parameter){
        // 注意这里的线程默认是 web 处理的线程,如果涉及主线程操作需要手动转到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
        // your code
        });
    }
}

而 JS 这边代码更简单了,干脆声明一个不解释的函数(约定好名字的),用于给 Native 做引用:

var parameter = xxx;
yourFuncName(parameter);
  1. iOS Native 调用 JS

iOS Native 调用 JS 的实现方法也被 JavaScriptCore 划分开来:

a. webview 直接注入 JS 并执行
b. JavaScriptCore 方法

a. webview 直接注入 JS 并执行

在 iOS 平台,webview 有注入并执行 JS 的 API。

UIWebView

UIWebView 有直接注入 JS 的方法:

NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')", @"alert msg"];
[_webView stringByEvaluatingJavaScriptFromString:jsStr];

这个方法会返回运行 JS 的结果(nullable NSString *),它是一个同步方法,会阻塞当前线程!尽管此方法不被弃用,但最佳做法是使用 WKWebView 类的 evaluateJavaScript:completionHandler:method。

WKWebView

不同于 UIWebView,WKWebView 注入并执行 JS 的方法不会阻塞当前线程。因为考虑到 webview 加载的 web content 内 JS 代码不一定经过验证,如果阻塞线程可能会挂起 App。

NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')", @"北京市东城区南锣鼓巷纳福胡同xx号"];
[_webview evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
    NSLog(@"%@----%@", result, error);
}];

这个方法不会阻塞线程,而且它的回调代码块总是在主线程中运行。

b. JavaScriptCore 方法

// 首先引入 JavaScriptCore 库
#import <JavaScriptCore/JavaScriptCore.h>

// 先获取 JS 上下文
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 如果涉及 UI 操作,切回主线程调用 JS 代码中的 YourFuncName,通过数组@[parameter] 入参
dispatch_async(dispatch_get_main_queue(), ^{
    JSValue *jsValue = self.jsContext[@"YourFuncName"];
    [jsValue callWithArguments:@[parameter]];
});

上面的代码调用了 JS 代码中 YourFuncName 函数,并且给函数加了 @[parameter] 作为入参。这里再贴一下 JS 代码:

function YourFuncName(arguments){
    var result = arguments;
    // do what u want to do
}
上一篇下一篇

猜你喜欢

热点阅读