iOS H5 container: UIWebView and WKWebView

Basic usage of UIWebView

1. Load web page

    UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    webView.delegate = self;
    [self.view addSubview:webView];
    //network address
    NSURL *url = [[NSURL alloc] initWithString:@"http://www.taobao.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];

2. Several commonly used proxy methods of UIWebViewDelegate

//Perform pre judgment before loading. If it returns YES,Will enter the subsequent process( StartLoad,FinishLoad). If return NO,This will not enter the subsequent process.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
//Start loading web page
- (void)webViewDidStartLoad:(UIWebView *)webView;
//Load complete
- (void)webViewDidFinishLoad:(UIWebView *)webView;
//Load failed
- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error;

3. Native calls methods in JS

For example, we have the following js code in the loaded HTML file:

<script type="text/javascript">
function hello(){
    alert("Hello!");
}

function helloWithName(name){
    alert(name + ",Hello!");
}
</script>
We can call<code>- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;</code>Function proceed js Call.
[webView stringByEvaluatingJavaScriptFromString:@"hello()"];
[webView stringByEvaluatingJavaScriptFromString:@"helloWithName('jack')"];

The js code does not have to be reserved in the js file, but can also be called in the code in the form of string, such as the following:

    //custom js function
    NSString *jsString = @"function sayHello(){ \
                                alert('jack11')   \
                            }                   \
                           sayHello()";
    [_webView stringByEvaluatingJavaScriptFromString:jsString];
    
    NSString *jsString = @" var p = document.createElement('p'); \
                            p.innerText = 'New Line';            \
                            document.body.appendChild(p);        \
    ";
    [_webView stringByEvaluatingJavaScriptFromString:jsString];

4. Methods calling Naitve in JS

Specifically, let js notify native to make method calls. We can let js generate a special request. The native code can be intercepted, and otherwise the user will notice. The general implementation scheme in the industry is to load a hidden iframe in the web page to realize this function. By specifying the src of the iframe as a special URL, the <code>- (bool) webview: (uiwebview *) WebView shouldstartloadwithrequest: (nsurlrequest *) request navigationtype: (uiwebviewnavigationtype) navigationtype</ Code> in the scheme. The corresponding js calling code is as follows:
    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 request is made iFrame It's useless, so take it from dom Remove up
        iFrame.parentNode.removeChild(iFrame);
        iFrame = null;
    }

For example, in the js code, we call the following two js methods:

    function iOS_alert() {//Call custom dialog
        loadURL("alert://abc");
    }
    function call() {//  js Call processing in
        loadURL("tel://17715022071");
    }

When you trigger the above methods, you will enter the proxy method of webview to intercept.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    NSURL * url = [request URL];
    if ([[url scheme] isEqualToString:@"alert"]) {//Intercept the request and pop up the customization dialog box
        UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"test" message:[url host] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alertView show];
        return NO;
    }else if([[url scheme] isEqualToString:@"tel"]){//Block call requests
        BOOL result = [[UIApplication sharedApplication] openURL:url];
        if (!result) {
            NSLog(@"Your device does not support making calls");
        } else {
            NSLog(@"I called");
        }
        return NO;
    }
    
    return YES;
}

In this way, we can make js call native ly.

 

 

Basic usage of WKWebView

1. Load web page

    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    NSURL *url = [NSURL URLWithString:@"http://www.taobao.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
    [self.view addSubview:webView];

2. Several commonly used proxy methods

/**
 *  Determine whether this jump can continue according to the webView and navigationAction related information. These information include HTTP sending requests. For example, the header contains user agent, accept, and refer
 *  Agent that decides whether to jump before sending the request
 *  @param webView
 *  @param navigationAction
 *  @param decisionHandler
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    decisionHandler(WKNavigationActionPolicyAllow);
}

/**
 *  This proxy method means that when the client receives the response header from the server, it can decide whether the jump can continue or not according to the relevant response information.
 *  Agent that decides whether to jump after receiving the response
 *  @param webView
 *  @param navigationResponse
 *  @param decisionHandler
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    decisionHandler(WKNavigationResponsePolicyAllow);
}

/**
 *  Ready to load the page. Equivalent to UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
 *
 *  @param webView
 *  @param navigation
 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
}

/**
 *  This proxy is called when the server redirect s
 *  Agent that received the server jump request
 *  @param webView
 *  @param navigation
 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
    
}

/**
 *  Content starts loading Equivalent to UIWebViewDelegate: - webViewDidStartLoad:
 *
 *  @param webView
 *  @param navigation
 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{
    
}

/**
 *  Page loading completed. Equivalent to UIWebViewDelegate: - webViewDidFinishLoad:
 *
 *  @param webView
 *  @param navigation
 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    
}

/**
 *  Page load failed. Equivalent to UIWebViewDelegate: - webView:didFailLoadWithError:
 *
 *  @param webView
 *  @param navigation
 *  @param error      
 */
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
    
}

- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0){
    
}

/*
 Let's take a look at several proxy methods of WKUIDelegate. Although it is not necessary to implement them, if our page has called the alert, confirm and prompt methods of js, we should implement the following proxy methods, and then call the native pop-up window here. Because after using WKWebView, the alert, confirm and prompt methods in HTML will not pop up again, Only the native callback proxy method converted to ios
 */
#pragma mark - WKUIDelegate

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"h5Container" message:message preferredStyle:UIAlertControllerStyleAlert];
//    [alertView addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
//        textField.textColor = [UIColor redColor];
//    }];
    [alertView addAction:[UIAlertAction actionWithTitle:@"I'm sure" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];
    [self presentViewController:alertView animated:YES completion:nil];
}

Obviously, the proxy method of WKWebView provides a finer granularity method than UIWebView. This allows developers to configure and process in more detail.

3. Native calls methods in JS

The functions provided by WKWebView to call js code are:

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;

For example, we have the following js code in the loaded HTML file:

<script type="text/javascript">
function hello(){
    alert("Hello!");
}

function helloWithName(name){
    alert(name + ",Hello!");
}
</script>

We can call js by calling the following code:

[_wkView evaluateJavaScript:@"hello()" completionHandler:^(id item, NSError * error) {
      
}];
    
 [_wkView evaluateJavaScript:@"helloWithName('jack')" completionHandler:^(id item, NSError *error) {
      
}];

Like UIWebView, we can also make js calls in the form of strings.

    NSString *jsString = @"function sayHello(){ \
                                    alert('jack11')   \
                                }                   \
                               sayHello()";
    [_wkView evaluateJavaScript:jsString completionHandler:^(id item, NSError *error) {
        
    }];
    
    jsString = @" var p = document.createElement('p'); \
    p.innerText = 'New Line';            \
    document.body.appendChild(p);        \
    ";
    [_wkView evaluateJavaScript:jsString completionHandler:^(id item, NSError *error) {
        
    }];

4. Methods calling Naitve in JS

In addition to loading a hidden ifame with UIWebView, WKWebView itself also provides a set of specifications for js calling native.

We can set a config parameter to WKWebView when initializing it.

//High end configuration
    //Create configuration
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    //establish UserContentController(provide javaScript towards webView Method of sending message)
    WKUserContentController *userContent = [[WKUserContentController alloc] init];
    //Add message processing. Note: self Refers to the need to comply WKScriptMessageHandler Agreement, to be removed at the end
    [userContent addScriptMessageHandler:self name:@"NativeMethod"];
    //take UserContentController Set to profile
    config.userContentController = userContent;
    //High end custom configuration creation WKWebView
    _wkView = [[YXWKView alloc] initWithFrame:self.view.bounds configuration:config];
    NSURL *url = [NSURL URLWithString:@"http://localhost:8080/myDiary/index.html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [_wkView loadRequest:request];
    _wkView.UIDelegate = self;
    _wkView.navigationDelegate = self;
    [self.view addSubview:_wkView];

In js, we can use the NativeMethod Handler to make js code call native.

For example, in the js code, I added a new method

<script type="text/javascript">
    function invokeNativeMethod(){
        window.webkit.messageHandlers.NativeMethod.postMessage("I want to call native Method of");
    }
</script>

When the above methods are triggered, the following native methods will be used for interception processing.

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    //Here is the high-end configuration, js call native Disposal place. We can name and body,Handle the bridge protocol.
    NSString *messageName = message.name;
    if ([@"NativeMethod" isEqualToString:messageName]) {
        id messageBody = message.body;
        NSLog(@"%@",messageBody);
    }
}

Comparison and selection of UIWebView and WKWebView

WKWebView is a new H5 container used by apple to announce WebKit when it released IOS8 at the WWDC2014 conference. Compared with UIWebView, it has faster loading speed and performance, and lower memory consumption. UIWebViewDelegate and UIWebView are reconstituted into 14 classes and 3 protocols, allowing developers to make more detailed configuration.

However, it has one of the most fatal defects, that is, the request of WKWebView cannot be intercepted by the NSURLProtocol. The best optimization point for H5 container in the app developed by our team mainly lies in the use of NSURLProtocol technology to process offline packages for H5 and share a set of caching technology for H5 images and Native images. Because of this problem, our team has not yet used WKWebView to replace UIWebVIew.

 

The above is a learning article. With the migration of time, many contents have become obsolete.

'UIWebView' was deprecated in iOS 12.0: No longer supported; please adopt WKWebView.

UIWebView is out of date

Posted by bb_sarkar on Tue, 31 May 2022 21:01:27 +0530