Permalink
Find file
724fe4c Jan 24, 2016
@ShingoFukuyama @getaaron
264 lines (177 sloc) 11.1 KB

This is what I learned about WKWebView, Apple's new WebKit API debuted on iOS 8.

As of this writing, the latest iOS version is iOS 8.1.3.

file:/// doesn't work without tmp directory

Only the tmp directory access can be accessed with the file: scheme, as of iOS 8.0.2.

You can see what directory access is allowed on the shazron / WKWebViewFIleUrlTest GitHut repo.

Note: On iOS 9.0.0 (beta), you can use the below method to load files from Documents, Library and tmp folders. But App Bundle cannot. - (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);

Can't handle in Storyboard and Interface Builder

You need to set WKWebView and any NSLayoutConstraints programmatically.

HTML <a> tag with target="_blank" won't respond

See Stack Overflow: Why is WKWebView not opening links with target=“_blank”

URL Scheme and App Store links won't work

Example

// Using [bendytree/Objective-C-RegEx-Categories](https://github.com/bendytree/Objective-C-RegEx-Categories) to check URL String
#import "RegExCategories.h"

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSURL *url = navigationAction.request.URL;
    NSString *urlString = (url) ? url.absoluteString : @"";

    // iTunes: App Store link
    if ([urlString isMatch:RX(@"\\/\\/itunes\\.apple\\.com\\/")]) {
        [[UIApplication sharedApplication] openURL:url];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    // Protocol/URL-Scheme without http(s)
    else if (![urlString isMatch:[@"^https?:\\/\\/." toRxIgnoreCase:YES]]) {
        [[UIApplication sharedApplication] openURL:url];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

alert, confirm, prompt from JavaScript needs to set WKUIDelegate methods

If you want to show dialog boxes, you have to implement the following methods:

webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler: webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler: webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:

Here is how to set those.

Basic/Digest/etc authentication input dialog boxes need to set a WKNavigationDelegate method

If you want to present an authentication challenge to user, you have to implement the method below:

webView:didReceiveAuthenticationChallenge:completionHandler:

Example:

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    NSString *hostName = webView.URL.host;

    NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodDefault]
        || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]
        || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {

        NSString *title = @"Authentication Challenge";
        NSString *message = [NSString stringWithFormat:@"%@ requires user name and password", hostName];
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
        [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
            textField.placeholder = @"User";
            //textField.secureTextEntry = YES;
        }];
        [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
            textField.placeholder = @"Password";
            textField.secureTextEntry = YES;
        }];
        [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {

            NSString *userName = ((UITextField *)alertController.textFields[0]).text;
            NSString *password = ((UITextField *)alertController.textFields[1]).text;

            NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:userName password:password persistence:NSURLCredentialPersistenceNone];

            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);

        }]];
        [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }]];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self presentViewController:alertController animated:YES completion:^{}];
        });

    }
    else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // needs this handling on iOS 9
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
        // or, see also http://qiita.com/niwatako/items/9ae602cb173625b4530a#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89
    }
    else {
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}

Cookie sharing between multiple WKWebViews

Use a WKProcessPool to share cookies between web views.

Example:

self.processPool = [[WKProcessPool alloc] init];

WKWebViewConfiguration *configuration1 = [[WKWebViewConfiguration alloc] init];
configuration1.processPool = self.processPool;
WKWebView *webView1 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration1];
...
WKWebViewConfiguration *configuration2 = [[WKWebViewConfiguration alloc] init];
configuration2.processPool = self.processPool;
WKWebView *webView2 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration2];
...

See this Stack Overflow question.

Cannot work with NSURLProtocol, NSCachedURLResponse, NSURLProtocol

UIWebView can filter ad URLs and cache to read websites offline using NSURLProtocol, NSURLCache, and NSCachedURLResponse.

But WKWebView cannot work with those APIs.

Cookie, Cache, Credential, WebKit data cannot easily delete

iOS 8

After much trial and error, I've reached the following conclusion:

  1. Use NSURLCache and NSHTTPCookie to delete cookies and caches in the same way as you used to do on UIWebView.
  2. If you use WKProccessPool, re-initialize it.
  3. Delete Cookies, Caches, WebKit subdirectories in the Library directory.
  4. Delete all WKWebViews

iOS 9

//// Optional data
NSSet *websiteDataTypes
= [NSSet setWithArray:@[
                        WKWebsiteDataTypeDiskCache,
                        WKWebsiteDataTypeOfflineWebApplicationCache,
                        WKWebsiteDataTypeMemoryCache,
                        WKWebsiteDataTypeLocalStorage,
                        WKWebsiteDataTypeCookies,
                        WKWebsiteDataTypeSessionStorage,
                        WKWebsiteDataTypeIndexedDBDatabases,
                        WKWebsiteDataTypeWebSQLDatabases
                        ]];
//// All kinds of data
//NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
//// Date from
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
//// Execute
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
   // Done
}];

Stack Overflow How to remove cache in WKWebview?

Scroll rate bug on iOS 9

On iOS 8, the below code works fine, it can scroll with more inertia.

webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;

As for iOS 9, this code is meaningless, without setting the rate value within UIScrollView delegate scrollViewWillBeginDragging.

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
}

See Stack Overflow: Cannot change WKWebView's scroll rate on iOS 9

Cannot disable long press link menu

CSS: -webkit-touch-callout: none; and JavaScript: document.documentElement.style.webkitTouchCallout='none'; won't work.

Note: This bug was fixed in iOS 8.2.

Sometimes capturing WKWebView fails

Sometimes capturing a screenshot of WKWebView itself failed, try to capture WKWebView's scrollView property instead.

Otherwise, if you are not afraid of using private API, try lemonmojo/WKWebView-Screenshot.

Xcode 6.1 and above doesn't indicate precise memory usage for WKWebView

As of Xcode 6.1, it indicates lower memory usage than is actually used.

window.webkit.messageHandlers won't work on some websites

Some websites somehow override JavaScript's window.webkit. To prevent this issue, you should cache this to a variable before a website's content starts loading. WKUserScriptInjectionTimeAtDocumentStart can help you.

Cookie saving sometimes failed

Are cookies synced between NSHTTPCookie and WKWebView at some point?

Thanks to @winzig, he gives me information: "Cookie discussion / ajax #3"

See this Stack Overflow question: Can I set the cookies to be used by a WKWebView?

At WKWebView initialization time, it can set cookies to both cookie management areas without waiting for the areas to be synced.

WKWebView's backForwardList is readonly

I want WKWebView to restore its paging history.

Hard to coexist with UIWebView on iOS 7 and below

Before some person tried to submit thier app for both iOS 7 and iOS 8 using UIWebView and WKWebView, the submission was rejected right at the time.

See this issue Cannot coexist with UIWebView on iOS 7 and below

Links

Naituw/WBWebViewConsole "WBWebViewConsole is an In-App debug console for your UIWebView && WKWebView"

Conclusion

As you can see, WKWebView still looks hard to use and UIWebView looks easy.

However, Apple announced to developers:

Starting February 1, 2015, new iOS apps uploaded to the App Store must include 64-bit support and be built with the iOS 8 SDK, included in Xcode 6 or later.

It is possible Apple will make UIWebView deprecated. See 64-bit and iOS 8 Requirements for New Apps.

If you're curious how WKWebView works for web browser apps, try my Ohajiki Web Browser. http://en.ohajiki.ios-web.com