WebViewJavascriptBridge淺析


WebViewJavascriptBridge是一個Objective-C與JavaScript進行消息互通的三方庫。通過WebViewJavascriptBridge,我們可以很方便的實現OC和Javascript互調的功能。WebViewJavascriptBridge實現互調的過程也容易理解,就是在OC環境和Javascript環境各自保存一個相互調用的bridge對象,每一個調用之間都有id和callbackid來找到兩個環境對應的處理。從Github上下載源碼之后,可以看到核心類主要包含如下幾個:

  • WebViewJavascriptBridge_JS:Javascript環境的Bridge初始化和處理。負責接收OC發給Javascript的消息,並且把Javascript環境的消息發送給OC。
  • WKWebViewJavascriptBridge/WebViewJavascriptBridge:主要負責OC環境的消息處理,並且把OC環境的消息發送給Javascript環境。
  • WebViewJavascriptBridgeBase:主要實現了OC環境的Bridge初始化和處理。

下面我們來詳細看一下具體的實現(這里的介紹以WKWebView環境為例)。

1.初始化

1.1OC環境的初始化

WebViewJavascriptBridge *bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView ];  //初始化調用


//初始化一個OC環境的橋WKWebViewJavascriptBridge並且初始化
+ (instancetype)bridgeForWebView:(WKWebView*)webView {
    WKWebViewJavascriptBridge* bridge = [[self alloc] init];
    [bridge _setupInstance:webView];
    [bridge reset];
    return bridge;
}

//初始化
- (void) _setupInstance:(WKWebView*)webView {
    _webView = webView;
    _webView.navigationDelegate = self;
    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}

- (id)init {
    if (self = [super init]) {
        //用於保存OC環境注冊的方法,key是方法名,value是這個方法對應的回調block
        self.messageHandlers = [NSMutableDictionary dictionary];
        //用於保存交互過程中需要發送給javascirpt環境的消息
        self.startupMessageQueue = [NSMutableArray array];
        //用於保存OC和javascript環境相互調用的回調模塊。通過_uniqueId加上時間戳來確定每個調用的回調
        self.responseCallbacks = [NSMutableDictionary dictionary];
        _uniqueId = 0;
    }
    return self;
}

【結論】:所有與Javascript之間交互的信息都存儲在messageHandlers和responseCallbacks中。這兩個屬性記錄了OC環境與Javascript交互的信息。

1.2OC環境注冊方法

注冊一個OC方法給Javascript調用,並且把它的回調實現保存在messageHandlers中。具體代碼如下:

    //注冊OC方法,以供Javascript調用
[self.bridge registerHandler:@"testClientCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
    NSLog(@"Javascript傳遞數據: %@", data);
    responseCallback(@"OC發給JS的返回值");
}];

- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
    _base.messageHandlers[handlerName] = [handler copy];
}

1.3Javascript初始化&注冊方法

這個我們先來看一下WebViewJavascriptBridge上的示例ExampleAPP.html:

    function setupWebViewJavascriptBridge(callback) {
        //第一次調用這個方法的時候,為false
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        //第一次調用的時候,為false
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        //把callback對象賦值給對象
        window.WVJBCallbacks = [callback];
        //加載WebViewJavascriptBridge_JS中的代碼
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
    //驅動所有hander的初始化
    setupWebViewJavascriptBridge(function(bridge) {
        //把WEB中要注冊的方法注冊到bridge里面
        bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
            //OC中傳過來的數據
            log('ObjC called testJavascriptHandler with', data);
            //JS返回數據
            var responseData = { 'Javascript Says':'Right back atcha!' };
            responseCallback(responseData);
        })
    })

【說明】:調用setupWebViewJavascriptBridge函數,並且這個函數傳入的參數也是一個函數。參數函數中有在Javascript環境中注冊的setupWebViewJavascriptBridge的實現過程中,我們可以發現,如果不是第一次初始化,會通過window.WVJBCallbacks兩個判斷返回。iframe可以理解為webview中的窗口,當我們改變iframe的src屬性的時候,相當於我們瀏覽器實現了鏈接的跳轉。比如從www.google.com。下面這段代碼的目的就是實現一個到https://__bridge_loaded__的跳轉。從而達到初始化Javascript環境的bridge的作用。

//加載WebViewJavascriptBridge_JS中的代碼
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)

我們知道,只要webview有跳轉,就會調用webview的代理方法,我們重點看下面這個代理方法。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if (webView != _webView) { return; }
    NSURL *url = navigationAction.request.URL;
    __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
    //如果是WebViewJavascriptBridge發送或者接收的消息,則特殊處理。否則按照正常流程處理。
    if ([_base isWebViewJavascriptBridgeURL:url]) {
        //1第一次注入JS代碼
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {//處理WEB發過來的消息
            [self WKFlushMessageQueue];
        } else {
            [_base logUnkownMessage:url];
        }
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    //webview的正常代理執行流程
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
        [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

上面的方法中,首先通過isWebViewJavascriptBridgeURL 來判斷是普通的跳轉還是webViewJavascriptBridge跳轉。如果是__bridge_loaded__ 表示是初始化Javascript環境的消息;如果是__WVJB_QUEUE_MESSAGE__ 表示是發送Javascript消息。我們繼續看幾個核心方法的實現:

#define kOldProtocolScheme @"wvjbscheme"
#define kNewProtocolScheme @"https"
#define kQueueHasMessage   @"__wvjb_queue_message__"
#define kBridgeLoaded      @"__bridge_loaded__"

//是否是WebViewJavascriptBridge框架相關的鏈接
- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url {
    if (![self isSchemeMatch:url]) {
        return NO;
    }
    return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url];
}
//是否是WebViewJavascriptBridge發送或者接收的消息
- (BOOL)isSchemeMatch:(NSURL*)url {
    NSString* scheme = url.scheme.lowercaseString;
    return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme];
}
//是WebViewJavascriptBridge發送的消息還是WebViewJavascriptBridge的初始化消息
- (BOOL)isQueueMessageURL:(NSURL*)url {
    NSString* host = url.host.lowercaseString;
    return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage];
}
//是否是https://__bridge_loaded__這種初始化加載消息
- (BOOL)isBridgeLoadedURL:(NSURL*)url {
    NSString* host = url.host.lowercaseString;
    return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];
}

接下來調用injectJavascriptFile方法,將WebViewJavascriptBridge_JS中的方法注入到webview中並且執行,從而達到初始化Javascript環境的brige的作用。

- (void)injectJavascriptFile {
    NSString *js = WebViewJavascriptBridge_js();
    //把javascript代碼注入webview中執行
    [self _evaluateJavascript:js];
    //javascript環境初始化完成以后,如果有startupMessageQueue消息,則立即發送消息
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}

//把javascript代碼寫入webview
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
    [_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
    return NULL;
}

那么WebViewJavascriptBridge_JS到底是怎么實現的呢?

NSString * WebViewJavascriptBridge_js() {
#define __wvjb_js_func__(x) #x
    
    // BEGIN preprocessorJSCode
    static NSString * preprocessorJSCode = @__wvjb_js_func__(
    ;(function() {
        //如果已經初始化了,則返回
        if (window.WebViewJavascriptBridge) {
            return;
        }
        
        if (!window.onerror) {
            window.onerror = function(msg, url, line) {
                console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line);
            }
        }
        //初始化Bridge對象,OC可以通過WebViewJavascriptBridge來調用JS里面的各種方法
        window.WebViewJavascriptBridge = {
            registerHandler: registerHandler,  //JS中注冊方法
            callHandler: callHandler,  //JS中調用OC中的方法
            disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
            _fetchQueue: _fetchQueue,  //把消息轉換成JSON串
            _handleMessageFromObjC: _handleMessageFromObjC  //OC調用JS的入口方法
        };
        
        var messagingIframe;
        //用於存儲消息列表
        var sendMessageQueue = [];
        //用於存儲消息
        var messageHandlers = {};
        //通過下面兩個協議組合來確定是否是特定的消息,然后攔擊
        var CUSTOM_PROTOCOL_SCHEME = 'https';
        var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
        //oc調用js的回調
        var responseCallbacks = {};
        //消息對應的id
        var uniqueId = 1;
        //是否設置消息超時
        var dispatchMessagesWithTimeoutSafety = true;
        //web端注冊一個消息方法
        function registerHandler(handlerName, handler) {
            messageHandlers[handlerName] = handler;
        }
        //web端調用一個OC注冊的消息
        function callHandler(handlerName, data, responseCallback) {
            if (arguments.length == 2 && typeof data == 'function') {
                responseCallback = data;
                data = null;
            }
            _doSend({ handlerName:handlerName, data:data }, responseCallback);
        }
        function disableJavscriptAlertBoxSafetyTimeout() {
            dispatchMessagesWithTimeoutSafety = false;
        }
        //把消息從JS發送到OC,執行具體的發送操作
        function _doSend(message, responseCallback) {
            if (responseCallback) {
                var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
                //存儲消息的回調ID
                responseCallbacks[callbackId] = responseCallback;
                //把消息對應的回調ID和消息一起發送,以供消息返回以后使用
                message['callbackId'] = callbackId;
            }
            //把消息放入消息列表
            sendMessageQueue.push(message);
            //下面這句話會出發JS對OC的調用,讓webview執行跳轉操作,從而可以在
            //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
            //中攔截到JS發給OC的消息
            messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
        }
        //把消息轉換成JSON字符串返回
        function _fetchQueue() {
            var messageQueueString = JSON.stringify(sendMessageQueue);
            sendMessageQueue = [];
            return messageQueueString;
        }
        //處理從OC返回的消息
        function _dispatchMessageFromObjC(messageJSON) {
            if (dispatchMessagesWithTimeoutSafety) {
                setTimeout(_doDispatchMessageFromObjC);
            } else {
                _doDispatchMessageFromObjC();
            }
            
            function _doDispatchMessageFromObjC() {
                var message = JSON.parse(messageJSON);
                var messageHandler;
                var responseCallback;
                //回調
                if (message.responseId) {
                    responseCallback = responseCallbacks[message.responseId];
                    if (!responseCallback) {
                        return;
                    }
                    responseCallback(message.responseData);
                    delete responseCallbacks[message.responseId];
                } else {  //主動調用
                    if (message.callbackId) {
                        var callbackResponseId = message.callbackId;
                        responseCallback = function(responseData) {
                            _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
                        };
                    }
                    //獲取JS注冊的函數
                    var handler = messageHandlers[message.handlerName];
                    if (!handler) {
                        console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
                    } else {  //調用JS中的對應函數處理
                        handler(message.data, responseCallback);
                    }
                }
            }
        }
        //OC調用JS的入口方法
        function _handleMessageFromObjC(messageJSON) {
            _dispatchMessageFromObjC(messageJSON);
        }
        
        messagingIframe = document.createElement('iframe');
        messagingIframe.style.display = 'none';
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
        document.documentElement.appendChild(messagingIframe);
        
        //注冊_disableJavascriptAlertBoxSafetyTimeout方法,讓OC可以關閉回調超時,默認是開啟的
        registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
        //執行_callWVJBCallbacks方法
        setTimeout(_callWVJBCallbacks, 0);
        
        //初始化WEB中注冊的方法。這個方法會把WEB中的hander注冊到bridge中
        //下面的代碼其實就是執行WEB中的callback函數
        function _callWVJBCallbacks() {
            var callbacks = window.WVJBCallbacks;
            delete window.WVJBCallbacks;
            for (var i=0; i<callbacks.length; i++) {
                callbacks[i](WebViewJavascriptBridge);
            }
        }
    })();
    ); // END preprocessorJSCode
    
#undef __wvjb_js_func__
    return preprocessorJSCode;
};

從上面可以看到,整個類就是一個立即執行的Javascript方法。

  • 首先會初始化一個WebViewJavascriptBridge對象,並且這個對象是賦值給window對象,這里的window對象可以理解為webview。所以如果在OC環境中要調用js方法,就可以通過在加上具體方法來調用。
  • WebViewJavascriptBridge對象中有Javascript環境注入的提供給OC調用的方法registerHandler,javascript調用OC環境方法的callHandler。
  • _fetchQueue這個方法的作用就是把Javascript環境的方法序列化成JSON字符串,然后傳入OC環境再轉換。
  • _handleMessageFromObjC就是處理OC發給Javascript環境的方法。

在這個文件中也初始化了一個iframe實現webview的url跳轉功能,從而激發webview代理方法的調用。

        messagingIframe = document.createElement('iframe');
        messagingIframe.style.display = 'none';
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
        document.documentElement.appendChild(messagingIframe);

上面的src就是https://__wvjb_queue_message__/。這個是javascript發送的OC的第一條消息,目的和上面OC環境的startupMessageQueue一樣,就是在javascript環境初始化完成以后,把javascript要發送給OC的消息立即發送出去。

1.4OC發消息給Javascript

- (void)p_callJSHandler:(id)sender {
    id data = @{ @"OC調用JS方法": @"OC調用JS方法的參數" };
    [self.bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {
        NSLog(@"JS 響應數據: %@", response);
    }];
}

/**
 OC調用JS方法

 @param handlerName JS中提供的方法名稱
 @param data 參數
 @param responseCallback 回調block
 */
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}

- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
    //所有信息存入的字典
    NSMutableDictionary* message = [NSMutableDictionary dictionary];
    
    if (data) {
        message[@"data"] = data;
    }
    
    if (responseCallback) {
        NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
        self.responseCallbacks[callbackId] = [responseCallback copy];
        message[@"callbackId"] = callbackId;
    }
    
    if (handlerName) {
        message[@"handlerName"] = handlerName;
    }
    [self _queueMessage:message];
}

//把OC消息序列化、並且轉化為JS環境的格式,然后在主線程中調用_evaluateJavascript
- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    [self _log:@"SEND" json:messageJSON];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
    
    NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
    if ([[NSThread currentThread] isMainThread]) {
        [self _evaluateJavascript:javascriptCommand];

    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self _evaluateJavascript:javascriptCommand];
        });
    }
}

打印javascriptCommand,結果如下:

WebViewJavascriptBridge._handleMessageFromObjC('{\"callbackId\":\"objc_cb_1\",\"data\":{\"OC調用JS方法\":\"OC調用JS方法的參數\"},\"handlerName\":\"testJavascriptHandler\"}');

實際上就是執行JS環境中的_handleMessageFromObjC方法:

        //處理從OC返回的消息
        function _dispatchMessageFromObjC(messageJSON) {
            if (dispatchMessagesWithTimeoutSafety) {
                setTimeout(_doDispatchMessageFromObjC);
            } else {
                _doDispatchMessageFromObjC();
            }
            
            function _doDispatchMessageFromObjC() {
                var message = JSON.parse(messageJSON);
                var messageHandler;
                var responseCallback;
                //回調
                if (message.responseId) {
                    responseCallback = responseCallbacks[message.responseId];
                    if (!responseCallback) {
                        return;
                    }
                    responseCallback(message.responseData);
                    delete responseCallbacks[message.responseId];
                } else {  //主動調用
                    if (message.callbackId) {
                        var callbackResponseId = message.callbackId;
                        responseCallback = function(responseData) {
                            _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
                        };
                    }
                    //獲取JS注冊的函數
                    var handler = messageHandlers[message.handlerName];
                    if (!handler) {
                        console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
                    } else {  //調用JS中的對應函數處理
                        handler(message.data, responseCallback);
                    }
                }
            }
        }

        //把消息從JS發送到OC,執行具體的發送操作
        function _doSend(message, responseCallback) {
            if (responseCallback) {
                var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
                //存儲消息的回調ID
                responseCallbacks[callbackId] = responseCallback;
                //把消息對應的回調ID和消息一起發送,以供消息返回以后使用
                message['callbackId'] = callbackId;
            }
            //把消息放入消息列表
            sendMessageQueue.push(message);
            //下面這句話會出發JS對OC的調用,讓webview執行跳轉操作,從而可以在
            //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
            //中攔截到JS發給OC的消息
            messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
        }

最重要的是最后面的通過改變iframe的messagingIframe.src,只有這樣才能觸發webview的代理方法webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler從而在OC中處理javascript環境觸發過來的回調。

        //第一次注入JS代碼
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {//處理WEB發過來的消息
            [self WKFlushMessageQueue];
        } else {
            [_base logUnkownMessage:url];
        }
        decisionHandler(WKNavigationActionPolicyCancel);

這里會走[self WKFlushMessageQueue];方法:

//把消息或者WEB回調發送到OC
- (void)WKFlushMessageQueue {
    [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
        if (error != nil) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
        }
        [_base flushMessageQueue:result];
    }];
}

最后執行flushMessageQueue,通過Javascript傳過來的responseId獲取對應的WVJBResponseCallback,執行這個block。到這里從OC發送消息到Javascript並且Javascript返回消息給OC的流程就完成了。

1.5JS發消息給OC

bridge.callHandler('testObjcHandler', {'foo': 'bar'}, function(response) {
    log('JS got response', response)-->
})

//web端調用一個OC注冊的消息
function callHandler(handlerName, data, responseCallback) {
    if (arguments.length == 2 && typeof data == 'function') {
        responseCallback = data;
        data = null;
    }
    _doSend({ handlerName: handlerName, data: data }, responseCallback);
}

//JS調用OC方法,執行具體的發送操作
function _doSend(message, responseCallback) {
    if (responseCallback) {
        var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
        //存儲消息的回調ID
        responseCallbacks[callbackId] = responseCallback;
        //把消息對應的回調ID和消息一起發送,以供消息返回以后使用
        message['callbackId'] = callbackId;
    }
    //把消息放入消息列表
    sendMessageQueue.push(message);
    //下面這句話會發起JS對OC的調用,讓webview執行跳轉操作,從而可以在
    //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
    //中攔截到JS發給OC的消息
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}        

具體執行和OC調用javascript過程一樣。

2.總結

  • 分別在OC環境和Javascript環境都保存一個bridge對象,里面維持着requestId、callbackId,以及每個id對應的具體實現。
  • OC通過Javascript環境的window.WebViewJavascriptBridge對象來找到具體的方法,然后執行。
  • Javascript通過改變iframe的src來喚起webview的代理方法webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler從而實現把Javascript消息發送給OC的功能。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM