JS and OC call each other under iOS (3) >MessageHandler

Keywords: Attribute Javascript

When using WKWebView, if you want to implement JS calling OC methods, there is a simple way to intercept URL s. That is to use MessageHandler, a new feature of WKWebView, to implement JS calls to native methods.

What is MessageHandler?

When WKWebView is initialized, there is a parameter called configuration, which is a parameter of WKWebViewConfiguration type. WKWebViewConfiguration has an attribute called userContentController, which is also a parameter of WKUserContentController type. The WKUserContentController object has a method - addScriptMessageHandler:name:, which I call MessageHandler for short.

- addScriptMessageHandler:name: There are two parameters, the first is the proxy object of userContentController and the second is the object sending postMessage in JS.
So to use the MessageHandler function, we must implement the WKScript MessageHandler protocol.
In the description of the API, we can see how to use it in JS:

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
//Where < name > is the second parameter `name ` in the above method.
//For example, when we call the API, the second parameter is @ "Share", which is in JS:
//window.webkit.messageHandlers.Share.postMessage(<messageBody>)
//MessageBody > is a key-value pair, the key is body, and the value can have many kinds of parameters.
// In the `WKScriptMessageHandler'protocol, we can see that mssage is of `WKScriptMessage' type and has an attribute called body.
// The notes indicate the type of body:
Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.

How to use MessageHandler?

1. Create WKWebViewConfiguration object and configure MessageHandler corresponding to each API.

WKUserContentController objects can add multiple scriptMessageHandler s.

It's easy to understand when you look at the sample code. The sample code is as follows:

    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    configuration.userContentController = [WKUserContentController new];

    [configuration.userContentController addScriptMessageHandler:self name:@"ScanAction"];
    [configuration.userContentController addScriptMessageHandler:self name:@"Location"];
    [configuration.userContentController addScriptMessageHandler:self name:@"Share"];
    [configuration.userContentController addScriptMessageHandler:self name:@"Color"];
    [configuration.userContentController addScriptMessageHandler:self name:@"Pay"];
    [configuration.userContentController addScriptMessageHandler:self name:@"Shake"];
    [configuration.userContentController addScriptMessageHandler:self name:@"GoBack"];
    [configuration.userContentController addScriptMessageHandler:self name:@"PlaySound"];

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

2. Create WK WebView.

There's nothing to say here. Look directly at the sample code.

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

    NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
    NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
    [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];

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

3. Implementation of the protocol.

I implemented two protocols here <WKUI Delegate, WKScript MessageHandler>, WKUI Delegate because I popped alert in JS. WKScript MessageHandler is because we need to process JS requests to call OC methods.
First, look at the sample code for implementing the protocol approach:

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
//    message.body  --  Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.
    if ([message.name isEqualToString:@"ScanAction"]) {
        NSLog(@"Scan");
    } else if ([message.name isEqualToString:@"Location"]) {
        [self getLocation];
    } else if ([message.name isEqualToString:@"Share"]) {
        [self shareWithParams:message.body];
    } else if ([message.name isEqualToString:@"Color"]) {
        [self changeBGColor:message.body];
    } else if ([message.name isEqualToString:@"Pay"]) {
        [self payWithParams:message.body];
    } else if ([message.name isEqualToString:@"Shake"]) {
        [self shakeAction];
    } else if ([message.name isEqualToString:@"GoBack"]) {
        [self goBack];
    } else if ([message.name isEqualToString:@"PlaySound"]) {
        [self playSound:message.body];
    }
}

WKScriptMessage has two key attributes, name and body.
Because we take a name for each OC method, we can differentiate the execution of different methods according to the name. There are parameters that JS will pass to OC in body.
As for the analysis of parameter body, I will give an example of placing dictionary in body. The others can be seen later in demo.
Parse the parameters that JS calls OC to share:

- (void)shareWithParams:(NSDictionary *)tempDic
{
    if (![tempDic isKindOfClass:[NSDictionary class]]) {
        return;
    }

    NSString *title = [tempDic objectForKey:@"title"];
    NSString *content = [tempDic objectForKey:@"content"];
    NSString *url = [tempDic objectForKey:@"url"];
    // Sharing is performed here

    // Return shared results to js
    NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
    [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@",result, error);
    }];
}

message.boby is the parameter passed from JS. Let's first make a fault-tolerant judgment in different ways. Then the normal value is OK.

4. Processing JS calls in HTML.

The source code of HMTL is similar to the previous HTML content, only the JS call part has changed.

// Chuan null
function scanClick() {
    window.webkit.messageHandlers.ScanAction.postMessage(null);
}
// Pass the dictionary              
function shareClick() {
    window.webkit.messageHandlers.Share.postMessage({title:'Test Sharing Title',content:'Test Shared Contents',url:'http://www.baidu.com'});
}
// Passing strings
function playSound() { 
    window.webkit.messageHandlers.PlaySound.postMessage('shake_sound_male.wav');
}
// Transmit array
function colorClick() {
    window.webkit.messageHandlers.Color.postMessage([67,205,128,0.5]);
}

5.OC calls JS

Is the implementation of OC invoking JS method using WKWebView the same as the previous article, or is it using WKWebView?
- evaluate JavaScript: completionHandler:. Use as follows:

// Return shared results to js
    NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
    [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@",result, error);
    }];

Benefits of using MessageHandler

  • 1. It's easy to write in JS, so you don't have to create URL s as much trouble.
  • 2.JS is more convenient to transfer parameters. Passing parameters by intercepting URL s can only splice the parameters back. If there are special characters in the parameters to be passed, such as &, =,? So, we have to convert, otherwise we will make mistakes in parameter analysis.
    For example, the url passed is as follows:
    http://www.baidu.com/share/openShare.htm?share_uuid=shdfxdfdsfsdf&name=1234556
    JS calls using intercepted URL s
    loadURL("firstClick://shareClick?title = shared title & content = shared content & url = link address & imagePath = picture address); }
    After putting the URL above into the link address, it is impossible to distinguish whether share_uuid is another parameter or an additional parameter in the url.
    But using MessageHandler can avoid problems caused by special characters.

Design sketch

Posted by Todd_Z on Sun, 16 Dec 2018 15:18:04 -0800