JS and OC call each other under iOS (6) - > WK WebView + WebView Javascript Bridge

Keywords: Javascript github encoding iOS

The last article introduced how UIWebView implements JS and OC calls through WebView Javascript Bridge. This article describes how WK WebView implements JS and OC calls through WebView Javascript Bridge. Using WebView Javascript Bridge under WK WebView is very similar to UIWebView. The main reason is that the classes that are instantiated are different, and some API calls related to webView are different.


Under WK WebView, using WebView Javascript Bridge to realize the mutual call between JS and OC is also achieved by intercepting URL s.
Let's start with how WK WebView implements calls between JS and OC through WebView Javascript Bridge.
About download WebViewJavascriptBridge Then the import part of the project will not be further elaborated.

The first step is to create a WK WebView.

The only thing to notice about this step is that you don't need to set up the navigation Delegate for WKWebView anymore. Next you will know why.

- (void)initWKWebView
{
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    configuration.userContentController = [WKUserContentController new];

    WKPreferences *preferences = [WKPreferences new];
    preferences.javaScriptCanOpenWindowsAutomatically = YES;
    preferences.minimumFontSize = 30.0;
    configuration.preferences = preferences;

    self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];

    NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
    NSString *localHtml = [NSString stringWithContentsOfFile:urlStr encoding:NSUTF8StringEncoding error:nil];
    NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
    [self.webView loadHTMLString:localHtml baseURL:fileURL];

    self.webView.UIDelegate = self;
    [self.view addSubview:self.webView];
}

The second step is to create a WebView JavascriptBridge instance.

Unlike the previous article, WK WebView uses WK WebView Javascript Bridge, while UIWebView uses WebView Javascript Bridge.

_webViewBridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView];
// If the controller needs to be monitored WKWebView Of`navigationDelegate`Method, you need to add the following line.
[_webViewBridge setWebViewDelegate:self];

The navigation Delegate of the WKWebView is no longer required in the previous step because the navigation Delegate of the WKWebView has been set as an instance of the WKWebView JavascriptBridge in {-bridge for WebView:}.

+ (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;
}

The third step is to register the Native function to be invoked by js

For ease of maintenance, I put all the js to call the Native function into a method to add, and then each function is processed separately.
The sample code is as follows:

#pragma mark - private method
- (void)registerNativeFunctions
{
    [self registScanFunction];

    [self registShareFunction];

    [self registLocationFunction];

    [self regitstBGColorFunction];

    [self registPayFunction];

    [self registShakeFunction];
}

// The Registration Function of Getting Location Information
- (void)registLocationFunction
{
    [_webViewBridge registerHandler:@"locationClick" handler:^(id data, WVJBResponseCallback responseCallback) {
        // Getting Location Information

        NSString *location = @"Xuefu Road, Nanshan District, Shenzhen City, Guangdong Province XXXX Number";
        // Return the result to js
        responseCallback(location);
    }];
}

With regard to -(void) registerHandler:(NSString*) handler Name handler:(WVJBHandler) handler, we can understand that the following block parameter is the Native implementation to be invoked by js, and the former handler Name is an alias for the Native implementation. Then the handler Name alias is called in js, and the WebView Javascript Bridge eventually executes the Native implementation in the block.

Step 4, add key js to HTML

In HMTL, before calling the Native function, we need to add a js method first, and then actively call the method once.
The method to be added is:

function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

If you've seen it JS and OC call each other under iOS (1) -- UIWebView intercepts URL s You will find that this method is very similar to the load URL.
Then actively call the above setupWebView JavascriptBridge once in js.

setupWebViewJavascriptBridge(function(bridge) {

      // Here register the js function that Native calls.
     bridge.registerHandler('testJSFunction', function(data, responseCallback) {
        alert('JS Method is called:'+data);
        responseCallback('js It has been implemented.');
     })
     // If there are other js functions called by Native, add them here in the format above.
})

Actively calling setupWebView JavascriptBridge has two purposes:
1. Execute a wvjbscheme://u BRIDGE_LOADED_ request.
2. Register the js function that Native calls.

Execute wvjbscheme://u BRIDGE_LOADED_u, then intercept the URL in the navigation Delegate method of WKWebView, and then inject js into HMTL. The following source code is extracted from WebView Javascript Bridge:

- (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;

    if ([_base isCorrectProcotocolScheme:url]) {
        // Here intercept wvjbscheme://u BRIDGE_LOADED_u
        if ([_base isBridgeLoadedURL:url]) {
            // js will be injected here 
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
            [self WKFlushMessageQueue];
        } else {
            [_base logUnkownMessage:url];
        }
        decisionHandler(WKNavigationActionPolicyCancel);
    }

    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
        [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

- (void)injectJavascriptFile {
    //Read js content
    NSString *js = WebViewJavascriptBridge_js();
    // Execute Native API to inject js into HMTL.
    [self _evaluateJavascript:js];
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}

WKWebView's API for executing JS is somewhat different from UIWebView's. WKWebView uses {- evaluateJavaScript: completionHandler:} which does not immediately return execution results, and js's execution results are returned in the block.

The fifth step is to call the Native function in js.

After that, it's finally time for js to call Native. In fact, it's very simple. For example, if I want to use Native to get location information, then add a button in HTML. The onclick event is location Click (), which can be implemented as follows.

function locationClick() {
    WebViewJavascriptBridge.callHandler('locationClick',null,function(response) {
        alert(response);
        document.getElementById("returnValue").value = response;
    });
}

Native executes the code and returns the location information to js through the third-party parameters of callHandler.
response can be a single value, or an array, or a key-value peer.
Of course, if we call Native, we don't have parameters or need Native to return information to js. We can also write as follows:

// No parameters, callbacks can be written like this
function locationClick() {
    WebViewJavascriptBridge.callHandler('locationClick',function(response) {
        alert(response);
        document.getElementById("returnValue").value = response;
    });
}

// No parameters and no callbacks are required to write this
function shake() {
    WebViewJavascriptBridge.callHandler('shakeClick');
}

At this point, JS calls Native through WebView Javascript Bridge.

Step 6, Native calls the JS function.

Native calls the js function in the same way as js calls the principle and process of Native.
1. Now registered in js, Native will call the function.
2. When Native calls registration, the alias name of the function can complete the call.
Registering the function Native calls in js also requires setting an alias HandlerName for the function.
In fact, this step was introduced earlier, the code is as follows:

setupWebViewJavascriptBridge(function(bridge) {

      // Here register the js function that Native calls.
     bridge.registerHandler('testJSFunction', function(data, responseCallback) {
        alert('JS Method is called:'+data);
        responseCallback('js It has been implemented.');
     })
     // If there are other js functions called by Native, add them here in the format above.
})

The above code registers a js function called testJSFunction in js, and the second parameter is a function. data in function is the parameter passed by Native when it calls the function. Response Callback is to return the necessary information to Native after executing the js code.

Native calls the function registered in js, sample code:

- (void)rightClick
{
    //    // If no parameters are required, no callbacks are required, use this
    //    [_webViewBridge callHandler:@"testJSFunction"];
    //    // If you need parameters, no callbacks are required, use this
    //    [_webViewBridge callHandler:@"testJSFunction" data:@"A string"];
    // If you need both parameters and callbacks, use this
    [_webViewBridge callHandler:@"testJSFunction" data:@"A string" responseCallback:^(id responseData) {
        NSLog(@"End of call JS Later callback:%@",responseData);
    }];
}

WK WebView implements the interaction between js and Native through WebView Javascript Bridge, which has been completed here.
This is the effect diagram in the engineering example:

Posted by Billy2007 on Thu, 13 Dec 2018 08:21:06 -0800