WKWebView與H5交互

iOS 與js交互方法

下面主要來(lái)說(shuō)說(shuō)WKScriptMessageHandler,WKWebView已經(jīng)內(nèi)置了JS與OC的互調(diào)、傳值等方法,在H5頁(yè)面中,可以通window.webkit.messageHandlers接口與Native進(jìn)行交互。您可以通過(guò)這個(gè)接口向原生代碼發(fā)送消息,并且獲取到原生代碼處理的結(jié)果。

JS調(diào)OC

H5實(shí)現(xiàn)下面方法
// JS調(diào)OC,方法名就是交互的名稱(chēng),數(shù)據(jù)就是JS給OC傳的值  
  window.webkit.messageHandlers.<方法名>.postMessage(<data>)

坑點(diǎn)

  • 如果傳的數(shù)據(jù)為空,需要這樣寫(xiě)postMessage(null)
OC響應(yīng)
  1. viewWillAppear添加配置
-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"getVerifyResult"]; 
}
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"getVerifyResult"]; 
}

注意

  • 這里的name就是JS的方法名字,方法名必須一致"
  1. 實(shí)現(xiàn) WKScriptMessageHandler 協(xié)議
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if([message.name isEqualToString:@"getVerifyResult"]){
        // 獲取到驗(yàn)證結(jié)果后,可以進(jìn)行不同的業(yè)務(wù)操作
        NSLog(@"data: %@", message.body);
    }
}

注意

  • 這里的name也是js的方法名字,方法名必須一致,通過(guò)方法名字判斷響應(yīng)H5對(duì)應(yīng)的js方法

OC調(diào)JS

webView加載完成,傳值給H5
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
       NSDictionary *dict = @{@"status":@(1)};
       NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingPrettyPrinted) error:nil];
       NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
       NSString *js = [NSString stringWithFormat:@"updateStatus(%@)", jsonStr];
      [self.webView evaluateJavaScript: js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
          if (error) {
               NSLog(@"js執(zhí)行失敗:%@", error);             
           } else {
               NSLog(@"js執(zhí)行成功");
          }
      }];
}

注意

  • H5 中的js方法也要和updateStatus(params)定義的方法名字一致

實(shí)踐例子

需求
Native引入H5的一個(gè)驗(yàn)證服務(wù),當(dāng)H5中觸發(fā)驗(yàn)證時(shí),需要將驗(yàn)證相關(guān)參數(shù)傳遞給Native, 然后Native再去異步驗(yàn)證,并且要把驗(yàn)證結(jié)果回傳給H5,但其中要求H5發(fā)出與Native傳值交互后,需要同步拿到Native驗(yàn)證結(jié)果,并在在觸發(fā)驗(yàn)證方法里面返回驗(yàn)證結(jié)果。

上述列子要實(shí)現(xiàn)的大概流程

 // 觸發(fā)驗(yàn)證服務(wù)的回調(diào)函數(shù)(帶驗(yàn)證信息)
 
  async function captchaVerifyCallback(verifyParam) {
    // 1. 向Native發(fā)送驗(yàn)證參數(shù)
    window.webkit.messageHandlers.getVerifyResult.postMessage(verifyParam)
    //2. 獲取驗(yàn)證結(jié)果
    const isSucceed = await xxxx('http://您的業(yè)務(wù)請(qǐng)求地址', {
        verifyParam: verifyParam, // 驗(yàn)證碼參數(shù)
     });
     // 3. 構(gòu)造標(biāo)準(zhǔn)返回參數(shù)
    const verifyResult = {
        status: isSucceed
    };
    return verifyResult;
  }

由于我們第二步的驗(yàn)證結(jié)果是在Native中進(jìn)行的,通常,這種交互是異步的,因?yàn)樗蕾?lài)于原生代碼的處理和回調(diào)。然而,window.webkit.messageHandlers本身并不直接支持Promise或者async/await機(jī)制。消息的發(fā)送通常是單向的,從JavaScript發(fā)送到原生代碼,而原生代碼的回復(fù)則需要通過(guò)其他機(jī)制來(lái)實(shí)現(xiàn)。如果要讓這種交互能夠使用await,需要構(gòu)建一個(gè)Promise并在原生代碼處理完畢后通過(guò)某種方式(通常是一個(gè)回調(diào)函數(shù))來(lái)解決(resolve)這個(gè)Promise

以下是H5Native的具體實(shí)現(xiàn)

H5頁(yè)面js實(shí)現(xiàn)
<script>
  async function verifyCallback(verifyParam) {
    // 1.向向Native發(fā)送相關(guān)驗(yàn)證信息
    let isSucceed = false;
    try {
        result = await sendMessageToNative('getVerifyResult', verifyParam);
        console.log('Received response from native:', result);
        isSucceed = result. isSucceed === 1;
    } catch (error) {
        console.error('error:', error);
    }
     // 2.構(gòu)造標(biāo)準(zhǔn)返回參數(shù)
    const verifyResult = {
        result: isSucceed
    };
    return verifyResult;
  }
    // 在H5頁(yè)面中定義一個(gè)函數(shù),用于發(fā)送消息給原生,并返回一個(gè)Promise
    // actionName: 函數(shù)名稱(chēng),這里為getVerifyResult
    function sendMessageToNative(actionName, params) {
        return new Promise((resolve, reject) => {
            // 創(chuàng)建一個(gè)唯一的回調(diào)函數(shù)名稱(chēng)
            const callbackName = 'method_' + Math.random().toString(36).substring(3);
            // 將回調(diào)函數(shù)掛載到window對(duì)象上,以便原生代碼可以調(diào)用
            window[callbackName] = (response) => {
                resolve(response); // 注意可能要格式轉(zhuǎn)換,根據(jù)實(shí)際情況
                // 移除掛載的回調(diào)函數(shù),避免內(nèi)存泄露
                delete window[callbackName];
            };
            // 發(fā)送消息給原生代碼
            window.webkit.messageHandlers[actionName].postMessage({
                data: params,
                callback: callbackName
            });
        });
    }
</script>
Native 實(shí)現(xiàn)

WKWebView初始化

  // 配置頁(yè)面自適應(yīng)縮放
  NSString *javascript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta)";
  WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
  // 設(shè)置UserAgent
  configuration.applicationNameForUserAgent = @"iOS_ua";
  WKUserScript *userScript = [[WKUserScript alloc]initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
   WKUserContentController *usercontroller = [[WKUserContentController   [usercontroller addUserScript:userScript];
  configuration.userContentController = usercontroller;
  _webView = [[WKWebView alloc]initWithFrame:CGRectZero configuration:configuration];
  _webView.navigationDelegate = self;

實(shí)現(xiàn)WKScriptMessageHandler協(xié)議

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    YLTLog(@"didReceiveScriptMessage: %@", message.name);
    if([message.name isEqualToString:@"getVerifyResult"]){
        // 獲取到驗(yàn)證結(jié)果后,可以進(jìn)行不同的業(yè)務(wù)操作
        NSDictionary *msgDict = message.body;
        NSString *callbackName = msgDict[@"callback"];
        NSString *verifyParam = msgDict[@"data"];
        if (!callbackName || !verifyParam) {
            return;
        }
        // 此處以延時(shí)操作模擬驗(yàn)證過(guò)程
       dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
             NSDictionary *dict = @{@"isSucceed":@(1)};
             NSData *data = [NSJSONSerialization dataWithJSONObject:params options:(NSJSONWritingPrettyPrinted) error:nil];
             NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
             NSString *js = [NSString stringWithFormat:@"%@(%@)", callbackName, response];
            // 執(zhí)行回調(diào),結(jié)果傳遞回H5頁(yè)面
            [self.webView evaluateJavaScript:js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
                if (error) {
                    NSLog(@" js 執(zhí)行失敗 error: %@", error);
               }else {// 驗(yàn)證碼發(fā)送成功
                   NSLog(@" js 執(zhí)行成功");
              }
            }];
       });
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容