Recently, we are going to replace the JS and native call function implemented with UIWebView with WKWebView. By the way, I searched and sorted out the ways in which JS and OC interact. Currently, I know how to deal with the interaction between JS and OC:

  • 1. Make a URL jump in JS and block the jump in OC. (There are UIWebView and WKWebView. Last year, we had to use UIWebView because it was compatible with iOS 6.)
  • 2. Use WKWebView MessageHandler.
  • 3. Use the system library JavaScriptCore to make mutual calls. (iOS 7)
  • 4. Use the third-party library WebViewJavascriptBridge.
  • 5. Leverage a third-party cordova library, formerly called PhoneGap. (This is a library platform library)
  • 6. React Native

I also wrote a summary of intercalling last year: JS on iOS and native OC intercalling (summary). It’s a bit rough, so we’re going to start a new catalog to document how JS interacts with native. Just record the various ways in which JS and OC interact. You can choose your own way according to the actual situation and scenario.


Today, we will introduce in detail how to use UIWebView to intercept URLS to realize the interaction between JS and OC. Why not use a third-party library or RAC? Because there are very few interfaces to call each other, just three or two, there is no need to use a knife.

UIWebView to intercept the URL

I have used UIWebView + URL blocking method to achieve JS and OC interaction. The reason is to be compatible with iOS 6.

1. Create a UIWebView and load the local HTML.

The purpose of loading local HTML is to make it easier to write YOUR own JS calls for testing, and ultimately, to load web HTML.

    self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];
    self.webView.delegate = self;
    NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
//    NSURL *htmlURL = [NSURL URLWithString:@"http://www.baidu.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL]; / / if you don't want the webView to the rebound effect of the self. The webView. ScrollView. Bounces = NO; / / UIWebView rolling slowly, this is set to the normal speed self. WebView. ScrollView. DecelerationRate = UIScrollViewDecelerationRateNormal; [self.webView loadRequest:request]; [self.view addSubview:self.webView];Copy the code

In native HTML, I’ve defined a few buttons that trigger calls to native methods and then call back the results to JS.

<input type="button" value="Sweep it." onclick="scanClick()" />
<input type="button" value="Get location" onclick="locationClick()" />
<input type="button" value="Modify background color" onclick="colorClick()" />
<input type="button" value="Share" onclick="shareClick()" />
<input type="button" value="Pay" onclick="payClick()" />
<input type="button" value="Shake it up." onclick="shake()" />
<input type="button" value="Return" onclick="goBack()"/> < span style = "box-sizing: border-box! Importantfunctions:

function loadURL(url) {
    var iFrame;
    iFrame = document.createElement("iframe");
    iFrame.setAttribute("src", url);
    iFrame.setAttribute("style"."display:none;");
    iFrame.setAttribute("height"."0px");
    iFrame.setAttribute("width"."0px");
    iFrame.setAttribute("frameborder"."0"); document.body.appendChild(iFrame); / / after the initiating the iFrame is useless, so get rid of it from the dom iFrame. ParentNode. RemoveChild (iFrame); iFrame = null; }function asyncAlert(content) {
    setTimeout(function(){ alert(content); }, 1); }function locationClick() {
    loadURL("haleyAction://getLocation");
}
            
function setLocation(location) {
    asyncAlert(location);
    document.getElementById("returnValue").value = location;
}
Copy the code

There is little HTML content, but there is a lot of learning:

1. Why not use window.location.href instead of loadURL? A: If you call the OC native method in window.location.href at the same time that the current page is loading using window.location.href, the loading operation will be cancelled. Similarly, two OC native calls using window.location.href in a row may cause the first operation to be cancelled. So we use a custom loadURL to avoid this problem. The loadURL implementation comes from the summary article on UIWebView and PhoneGap.

2. Why is a unified scheme used for loadURL links? A: Easy to do interception processing in OC, reduce in JS call some OC methods not implemented, webView do jump. Because when I intercept urls in OC, I use Scheme (haleyAction) to distinguish between calling native methods and normal web hops. It then differentiates what to do based on host (the part after // getLocation).

3. Why customize an asyncAlert method? A: Because some JS calls require OC to return the result to JS. StringByEvaluatingJavaScriptFromString is a synchronization method, will wait for js method completes, and a pop-up alert will also block the interface for the user response, so they may cause a deadlock. The Alert screen freezes. If the JS callback is a time-consuming operation, it is recommended that the time-consuming operation also be put into the function of setTimeout.

2. Intercept the URL

UIWebView has a proxy method that intercepts each linked Request. Return YES, the webView will load the link; Return NO, the webView will not load the connection. We will process our URL in this intercepting proxy method. Here is my sample code:

#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSURL *URL = request.URL;
    NSString *scheme = [URL scheme];
    if ([scheme isEqualToString:@"haleyaction"]) {
        [self handleCustomAction:URL];
        return NO;
    }
    return YES;
}
Copy the code

In this case, it is very easy to use Scheme to intercept custom urls. If different methods use different Schemes, it is very troublesome to determine. Then, look at my method for handling connections:

#pragma mark - private method
- (void)handleCustomAction:(NSURL *)URL
{
    NSString *host = [URL host];
    if ([host isEqualToString:@"scanClick"]) {
        NSLog(@"Sweep it.");
    } else if ([host isEqualToString:@"shareClick"]) {
        [self share:URL];
    } else if ([host isEqualToString:@"getLocation"]) {
        [self getLocation];
    } else if ([host isEqualToString:@"setColor"]) {
        [self changeBGColor:URL];
    } else if ([host isEqualToString:@"payAction"]) {
        [self payAction:URL];
    } else if ([host isEqualToString:@"shake"]) {
        [self shakeAction];
    } else if ([host isEqualToString:@"goBack"]) { [self goBack]; }}Copy the code

By the way, take a look at how to call the result back into JS:

- (void)getLocation {// Get the location information // return the result to js NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')"The @XXXX xuefu Road, Nanshan District, Shenzhen City, Guangdong Province, China];
    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];
}
Copy the code

When we call the OC method in JS, we also need to pass the parameter to the OC. Just like a GET request, place the arguments after:

function shareClick() {
    loadURL("haleyAction://shareClick? Title = Test share title &content= test share content &url=http://www.baidu.com");
}
Copy the code

So what if we get these parameters? All arguments are in the URL query, with & splitting the string and = splitting the argument into the key and the actual value. Here is the sample code:

- (void)share:(NSURL *)URL
{
    NSArray *params =[URL.query componentsSeparatedByString:@"&"];
    
    NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
    for (NSString *paramStr in params) {
        NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
        if (dicArray.count > 1) {
            NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            [tempDic setObject:decodeValue forKey:dicArray[0]];
        }
    }
    
    NSString *title = [tempDic objectForKey:@"title"];
    NSString *content = [tempDic objectForKey:@"content"];
    NSString *url = [tempDic objectForKey:@"url"]; NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];
}
Copy the code

3. OC calls the JS method

Some things to note about returning OC execution results to JS:

If the callback executes a JS method with an argument that is not a string, do not enclose the single quotation marks; otherwise, the invocation of the JS method may fail. Like me:

NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userProfile options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *jsStr = [NSString stringWithFormat:@"loginResult('%@',%@)".type, jsonStr];
[_webView stringByEvaluatingJavaScriptFromString:jsStr];
Copy the code

If the second argument is enclosed in single quotes, the loginResult on the JS side will not be called.

In addition, the use of [the webView stringByEvaluatingJavaScriptFromString: @ “var arr = [3, 4, ‘ABC’];”] ; , you can insert global variables, JS methods, and so on into the HMTL JS environment.

Example project address: JS_OC_URL

If you can’t download it, go back to the project level 1 directory and download it again.