└── README.md /README.md: -------------------------------------------------------------------------------- 1 | This is what I learned about `WKWebView`, Apple's new WebKit API debuted on iOS 8. 2 | 3 | As of this writing, the latest iOS version is iOS 8.1.3. 4 | 5 | ## `file:///` doesn't work without `tmp` directory 6 | Only the `tmp` directory access can be accessed with the `file:` scheme, as of iOS 8.0.2. 7 | 8 | You can see what directory access is allowed on [the shazron / WKWebViewFIleUrlTest GitHut repo](https://github.com/shazron/WKWebViewFIleUrlTest). 9 | 10 | 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. 11 | `- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);` 12 | 13 | ## Can't handle in Storyboard and Interface Builder 14 | You need to set `WKWebView` and any `NSLayoutConstraint`s programmatically. 15 | 16 | ## HTML `` tag with `target="_blank"` won't respond 17 | See Stack Overflow: [Why is WKWebView not opening links with target=“_blank”](http://stackoverflow.com/questions/25713069/why-is-wkwebview-not-opening-links-with-target-blank) 18 | 19 | 20 | ## URL Scheme and App Store links won't work 21 | 22 | #### Example 23 | 24 | ```objc 25 | // Using [bendytree/Objective-C-RegEx-Categories](https://github.com/bendytree/Objective-C-RegEx-Categories) to check URL String 26 | #import "RegExCategories.h" 27 | 28 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler 29 | { 30 | NSURL *url = navigationAction.request.URL; 31 | NSString *urlString = (url) ? url.absoluteString : @""; 32 | 33 | // iTunes: App Store link 34 | if ([urlString isMatch:RX(@"\\/\\/itunes\\.apple\\.com\\/")]) { 35 | [[UIApplication sharedApplication] openURL:url]; 36 | decisionHandler(WKNavigationActionPolicyCancel); 37 | return; 38 | } 39 | // Protocol/URL-Scheme without http(s) 40 | else if (![urlString isMatch:[@"^https?:\\/\\/." toRxIgnoreCase:YES]]) { 41 | [[UIApplication sharedApplication] openURL:url]; 42 | decisionHandler(WKNavigationActionPolicyCancel); 43 | return; 44 | } 45 | decisionHandler(WKNavigationActionPolicyAllow); 46 | } 47 | ``` 48 | 49 | 50 | ## alert, confirm, prompt from JavaScript needs to set `WKUIDelegate` methods 51 | 52 | If you want to show dialog boxes, you have to implement the following methods: 53 | 54 | `webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:` 55 | `webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:` 56 | `webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:` 57 | 58 | [Here is how to set those](http://qiita.com/ShingoFukuyama/items/5d97e6c62e3813d1ae98). 59 | 60 | 61 | ## Basic/Digest/etc authentication input dialog boxes need to set a `WKNavigationDelegate` method 62 | 63 | If you want to present an authentication challenge to user, you have to implement the method below: 64 | 65 | `webView:didReceiveAuthenticationChallenge:completionHandler:` 66 | 67 | #### Example: 68 | 69 | ```objc 70 | - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler 71 | { 72 | NSString *hostName = webView.URL.host; 73 | 74 | NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod]; 75 | if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodDefault] 76 | || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] 77 | || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) { 78 | 79 | NSString *title = @"Authentication Challenge"; 80 | NSString *message = [NSString stringWithFormat:@"%@ requires user name and password", hostName]; 81 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 82 | [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 83 | textField.placeholder = @"User"; 84 | //textField.secureTextEntry = YES; 85 | }]; 86 | [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 87 | textField.placeholder = @"Password"; 88 | textField.secureTextEntry = YES; 89 | }]; 90 | [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 91 | 92 | NSString *userName = ((UITextField *)alertController.textFields[0]).text; 93 | NSString *password = ((UITextField *)alertController.textFields[1]).text; 94 | 95 | NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:userName password:password persistence:NSURLCredentialPersistenceNone]; 96 | 97 | completionHandler(NSURLSessionAuthChallengeUseCredential, credential); 98 | 99 | }]]; 100 | [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { 101 | completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); 102 | }]]; 103 | dispatch_async(dispatch_get_main_queue(), ^{ 104 | [self presentViewController:alertController animated:YES completion:^{}]; 105 | }); 106 | 107 | } 108 | else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { 109 | // needs this handling on iOS 9 110 | completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); 111 | // 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 112 | } 113 | else { 114 | completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); 115 | } 116 | } 117 | ``` 118 | 119 | 120 | ## Cookie sharing between multiple `WKWebView`s 121 | 122 | Use a `WKProcessPool` to share cookies between web views. 123 | 124 | #### Example: 125 | 126 | ```objc 127 | self.processPool = [[WKProcessPool alloc] init]; 128 | 129 | WKWebViewConfiguration *configuration1 = [[WKWebViewConfiguration alloc] init]; 130 | configuration1.processPool = self.processPool; 131 | WKWebView *webView1 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration1]; 132 | ... 133 | WKWebViewConfiguration *configuration2 = [[WKWebViewConfiguration alloc] init]; 134 | configuration2.processPool = self.processPool; 135 | WKWebView *webView2 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration2]; 136 | ... 137 | ``` 138 | 139 | See [this Stack Overflow question](http://stackoverflow.com/questions/25797972/cookie-sharing-between-multiple-wkwebviews). 140 | 141 | 142 | ## Cannot work with `NSURLProtocol`, `NSCachedURLResponse`, `NSURLProtocol` 143 | `UIWebView` can filter ad URLs and cache to read websites offline using `NSURLProtocol`, `NSURLCache`, and `NSCachedURLResponse`. 144 | 145 | But `WKWebView` cannot work with those APIs. 146 | 147 | 148 | ## Cookie, Cache, Credential, WebKit data cannot easily delete 149 | 150 | ### iOS 8 151 | After much trial and error, I've reached the following conclusion: 152 | 153 | 1. Use `NSURLCache` and `NSHTTPCookie` to delete cookies and caches in the same way as you used to do on `UIWebView`. 154 | 2. If you use `WKProccessPool`, re-initialize it. 155 | 3. Delete `Cookies`, `Caches`, `WebKit` subdirectories in the `Library` directory. 156 | 4. Delete all `WKWebView`s 157 | 158 | ### iOS 9 159 | 160 | ```objc 161 | //// Optional data 162 | NSSet *websiteDataTypes 163 | = [NSSet setWithArray:@[ 164 | WKWebsiteDataTypeDiskCache, 165 | WKWebsiteDataTypeOfflineWebApplicationCache, 166 | WKWebsiteDataTypeMemoryCache, 167 | WKWebsiteDataTypeLocalStorage, 168 | WKWebsiteDataTypeCookies, 169 | WKWebsiteDataTypeSessionStorage, 170 | WKWebsiteDataTypeIndexedDBDatabases, 171 | WKWebsiteDataTypeWebSQLDatabases 172 | ]]; 173 | //// All kinds of data 174 | //NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; 175 | //// Date from 176 | NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; 177 | //// Execute 178 | [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{ 179 | // Done 180 | }]; 181 | ``` 182 | 183 | Stack Overflow [How to remove cache in WKWebview?](http://stackoverflow.com/a/32491271/3283039) 184 | 185 | 186 | ## Scroll rate bug on iOS 9 187 | 188 | On iOS 8, the below code works fine, it can scroll with more inertia. 189 | 190 | `webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;` 191 | 192 | As for iOS 9, this code is meaningless, without setting the rate value within UIScrollView delegate `scrollViewWillBeginDragging`. 193 | 194 | ```objc 195 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 196 | { 197 | scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; 198 | } 199 | ``` 200 | 201 | See Stack Overflow: [Cannot change WKWebView's scroll rate on iOS 9](http://stackoverflow.com/questions/31369538/cannot-change-wkwebviews-scroll-rate-on-ios-9-beta) 202 | 203 | 204 | ## Cannot disable long press link menu 205 | 206 | CSS: `-webkit-touch-callout: none;` and JavaScript: `document.documentElement.style.webkitTouchCallout='none';` won't work. 207 | 208 | Note: This bug was fixed in iOS 8.2. 209 | 210 | ## Sometimes capturing `WKWebView` fails 211 | 212 | Sometimes capturing a screenshot of `WKWebView` itself failed, try to capture `WKWebView`'s `scrollView` property instead. 213 | 214 | Otherwise, if you are not afraid of using private API, try [lemonmojo/WKWebView-Screenshot](https://github.com/lemonmojo/WKWebView-Screenshot). 215 | 216 | 217 | ## Xcode 6.1 and above doesn't indicate precise memory usage for WKWebView 218 | 219 | As of Xcode 6.1, it indicates lower memory usage than is actually used. 220 | 221 | 222 | ## `window.webkit.messageHandlers` won't work on some websites 223 | 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. 224 | 225 | 226 | ## Cookie saving sometimes failed 227 | Are cookies synced between `NSHTTPCookie` and `WKWebView` at some point? 228 | 229 | Thanks to @winzig, he gives me information: "Cookie discussion / ajax #3" 230 | 231 | See this Stack Overflow question: [Can I set the cookies to be used by a WKWebView?](http://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview) 232 | 233 | At `WKWebView` initialization time, it can set cookies to both cookie management areas without waiting for the areas to be synced. 234 | 235 | 236 | ## `WKWebView`'s `backForwardList` is readonly 237 | I want `WKWebView` to restore its paging history. 238 | 239 | ## Hard to coexist with `UIWebView` on iOS 7 and below 240 | 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. 241 | 242 | See this issue [Cannot coexist with UIWebView on iOS 7 and below](https://github.com/ShingoFukuyama/WKWebViewTips/issues/2) 243 | 244 | 245 | ## Links 246 | [Naituw/WBWebViewConsole](https://github.com/Naituw/WBWebViewConsole) 247 | "WBWebViewConsole is an In-App debug console for your UIWebView && WKWebView" 248 | 249 | 250 | ## Conclusion 251 | As you can see, `WKWebView` still looks hard to use and UIWebView looks easy. 252 | 253 | However, Apple announced to developers: 254 | 255 | > 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. 256 | 257 | It is possible Apple will make `UIWebView` deprecated. See [64-bit and iOS 8 Requirements for New Apps](https://developer.apple.com/news/?id=10202014a). 258 | 259 | 260 | 261 | If you're curious how `WKWebView` works for web browser apps, try my Ohajiki Web Browser. 262 | http://en.ohajiki.ios-web.com 263 | 264 | --------------------------------------------------------------------------------