OC 与 JS 交互

446 阅读2分钟

日常App 项目中存在很多嵌入的H5页面,前端和移动端就需要有很多交互。这里简单罗列汇总一下常见的交互方,不包括使用第三方库,且仅针对 WKWebView。

OC 调用 JS

1. 通过evaluateJavaScript 方法
[webView evaluateJavaScript:@"OCCallJS('params')"
         completionHandler:^(id _Nullable data, 
                             NSError * _Nullable error) {
        if (error) {
            NSLog(@"error:%@",error);
        }
    }];
2. JS 方法注入
向网页中注入我们的js方法,

- (void)addUserScript:(WKUserScript *)userScript;

这种注入后,任何时候都可以注入,或者参数改变后可以再次注入。

项目中有一种场景是:

前端页面正常可以浏览,但需要同步获取原生登录状态。 目前前端的做法是缓存了登录成功后的Token,根据token 是否存在,判断登录状态。

未登录时

NSString *js =

[NSString stringWithFormat:@"window.sessionStorage.setItem('token','%@')",@""]; 

成功登录时

NSString *js =

[NSString stringWithFormat:@"window.sessionStorage.setItem('token','%@')",token];

每次登录状态更改后都可以再次注入

WKUserScript * userScript
= [[WKUserScript alloc]initWithSource:js 
                        injectionTime:WKUserScriptInjectionTimeAtDocumentStart 
                     forMainFrameOnly:NO]; 
                     
[self.webView.configuration.userContentController addUserScript:userScript];

JS 调用 OC

1. 通过 messageHandlers postMessage 方式

前端调用OC 最常用最便捷的方式

window.webkit.messageHandlers.callNativeAndSend.postMessage('params');

方法名:callNativeAndSend 参数就是:params 移动端通过解析params 获取相应的值

但这种方法很难获取获取回调值,或者说前端通过这个方法获取回调值还没成功过。

2. 通过WKUIDelegate 代理方法

在JS端调用alert函数alert(content)时,会触发此代理方法,

通过message可以拿到JS端所传的数据,在iOS端得到结果后,需要回调JS

通过completionHandler回调给JS端

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {


    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"JS调用alert" message:message preferredStyle:UIAlertControllerStyleAlert];

    [alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {

        completionHandler();
    }]];

    [self presentViewController:alert animated:YES completion:nil];

}

JS端调用confirm函数时,会触发此方法,通过message可以拿到JS端所传的数据,

在iOS端显示原生alert得到YES/NO后,通过completionHandler回调给JS端

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {

  
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm" message:@"JS调用confirm" preferredStyle:UIAlertControllerStyleAlert];

    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

        completionHandler(YES);

    }]];

    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {

        completionHandler(NO);

    }]];

    [self presentViewController:alert animated:YES completion:NULL];

}

JS端调用prompt函数时,会触发此方法,要求输入一段文本,

在原生输入得到文本内容后,通过completionHandler回调给JS

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {

   
    NSLog(@"%s", __FUNCTION__);

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS调用输入框" preferredStyle:UIAlertControllerStyleAlert];

    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {

        textField.textColor = [UIColor redColor];

    }];

    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

        completionHandler([[alert.textFields lastObject] text]);

    }]];

    [self presentViewController:alert animated:YES completion:NULL];

}

3. 拦截URL 请求

事先跟前端约定好需要拦截的请求地址

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    NSURL * url = navigationAction.request.URL;
    NSString * scheme = url.scheme;
    NSString * query = url.query; 
    NSString * host = url.host; 

    if ([[url absoluteString] hasSuffix:@"js_oc"]) {
      
      [self handleJSMessage]; 

        decisionHandler(WKNavigationActionPolicyCancel); 
        
        return; 
       }
     decisionHandler(WKNavigationActionPolicyAllow);

}

回调JS方法

- (void)handleJSMessage { 
    [_wkWebView evaluateJavaScript:@"nativeCallbackJscMothod('123')" 
                 completionHandler:^(id _Nullable result,
                                    NSError * _Nullable error) {
                                    
                  NSLog(@"x = %@, error = %@", result, error.localizedDescription); 
              }];
 }