iOS WKWebView Tips

Problem list

WKWebView skips some features whose UIWebView implemented by default

  • It is not easy to delete cookie
  • It is not easy to delete cache
  • Cannot open target=”_blank” page
  • When opening Google Map, allowance dialog doesn’t show up(not WK, this is iOS8)
  • Basic authentication prompt doesn’t work by default
  • Prompt doesn’t work by default
  • App link doesn’t work by default
  • URL schema for Application doesn’t work by default
  • Share Cookie within all WKWebView
  • WkWebView video player layout has constraint bus
  • Cannot prevent long tap link if we don’t have another UIActionController

There are some ways to prevent above problems

Contents


JavaScript doesn’t work in decidePolicyForNavigationAction

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler

decidePolicyForNavigationAction is equivalent for shouldStartLoadWithRequest in UIWebView

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

In shouldStartLoadWithRequest, javascript works using stringByEvaluatingJavaScriptFromString.
But javascript doesn’t work in evaluateJavaScript of WKWebView.


Sync JavaScript in WKWebView

evaluateJavaScript is callback type. result should be handled by callback so, it is async.

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)javascript {
    __block NSString *res = nil;
    __block BOOL finish = NO;
    [self evaluateJavaScript:javascript completionHandler:^(NSString *result, NSError *error){
        res = result;
        finish = YES;
    }];
    
    while(!finish) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
    return res;
}

target=”_blank”

Some link has target=”_blank” parameter.
In this case, WkWebView doesn’t work correctly by default.
To work this, we need to implement WKUIDelegate

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    
    if (!navigationAction.targetFrame.isMainFrame) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}

Cookie

WKWebView cookies are following.

  • NSHttpCookieStorage
  • Cookie directory in app(Application/Library/Cookies)
  • Webstorage in WebKit directory(Application/Library/WebKit) Application is your app name
  • WKWebView in memory(You need to remove from any view if you want to dele cookies)

To delete all cookies from app, if you need to delete.
WKWebView seems to restore and get data from all of them. So even if you delete one of them, webview still can restore data.


Cache

WKWebView caches are following.

  • NSURLCache
  • Cache directory in app(Application/Library/Caches)
  • Webstorage in WebKit directory(Application/Library/WebKit) Application is your app name
  • WKWebView in memory(You need to remove from any view if you want to dele cookies)

Google Map(Location permission)

This is iOS8 issue
Google Map requires location acquire permission by iOS8.
To manage this correctly, we need the parameter in Info.plist
You need to add key NSLocationWhenInUseUsageDescription, the value is
Websites you visit may request your location.
Google map location permission


Prompt doesn’t work by default

This is link of my blog entry.
WKWebView Prompt


App link doesn’t work by default

App link is itunes link like http://itunes.apple.com/xxxxxxx
UIWebView handles this as special URL. When the user taps this link, iOS opens iTunes application
WKWebView doesn’t have it.

[[UIApplication sharedApplication] openURL:navigationAction.request.URL];
decisionHandler(WKNavigationActionPolicyCancel);

To handle URL as OS operation, we need to use following codes.
Finally, we need to integrate this into decidePolicyForNavigationAction

These are helper of Regular Expression NSString Category

- (BOOL)isMatch:(NSString *)pattern {
    NSError *error = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
    if (error) {
        return NO;
    }
    NSTextCheckingResult *res = [regex firstMatchInString:self options:0 range:NSMakeRange(0, self.length)];
    return res != nil;
}

- (BOOL)isiTunesURL {
    return [self isMatch:@"\\/\\/itunes\\.apple\\.com\\/"];
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSURL *url = navigationAction.request.URL;
    if ([urlString isiTunesURL]) {
            // This is for iTunes
            [[UIApplication sharedApplication] openURL:url];
            decisionHandler(WKNavigationActionPolicyCancel);
    }
}

Share Cookie within all WKWebView

To share cookie in all WKWebView, we need to use same WKProcessPool(This is configuration)

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.processPool = [WKWebViewPoolHandler pool];
WKWebView *wkwebView = [[TBWKWebView alloc] initWithFrame:CGRectZero configuration:configuration];

Pool is singleton method

+ (WKProcessPool *)pool {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _pool = [[WKProcessPool alloc] init];
    });
    return _pool;
}

Run javascript in WKWebView

WKWebView has feature to run javascript(
evaluateJavaScript:completionHandler:
)
This is asynchronous methods and different from UIWebView(UIWebView is sync method).
iOS WKWebView Javascript

But this has problem. If res is not required, it happens something wrong. In my case, UITextField has auto enter, why?
I have no idea why this happens. Basically, if you don’t need to wait, we should use async.


Prevent default long tap

In UIWebView, following javascript prevents default long press menu

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [webView stringByEvaluatingJavaScriptFromString:@"document.body.style.webkitTouchCallout='none';"];
}

We can add long press event using UILongPressGestureRecognizer
But, this event and default long tap event seem to be conflicted.
If you use ActionSheet to handle your long press event, by using UIActionController, we could prevent double UIActionController and fix this issue(always prior your long press UIActionController)


URL scheme

Following are test URL
Google Mobile App
Pixitail
Evernote
Google Map


Ref

ShingoFukuyama/WKWebViewTips