WKWebView and NSURLProtocol not working
Updated answer for iOS 11 and macOS 10.13
Since iOS 11 it is possible to declare an object that conforms to the WKURLSchemeHandler
protocol and register it in the WKWebView
configuration: -[WKWebViewConfiguration setURLSchemeHandler:forURLScheme:]
.
Old answer
WKWebView
makes requests and renders content out-of-process, meaning your app does not hear the requests they make. If you are missing a functionality, now is the time to open a bug report and/or an enhancement request with Apple.
As of iOS 10.3 SDK, WKWebView
is still unable to make use of custom NSURLProtocol
s using public APIs.
Enterprising developers have found an interesting method:+[WKBrowsingContextController registerSchemeForCustomProtocol:]
It supposedly adds the provided scheme to a list of custom protocol handled schemes and should then work with NSURLProtocol
.
NSURLProtocol isn't asked to load after YES response to canInitWithRequest
I was able to workaround this issue by cache-busting the UIWebView cache, while not busting the NSURLCache.
- Add a unique param to the query params of the original request. I chose 'key=000000' where the value is zero-led six digit random number.
- In the protocol, strip the key in
canonicalRequestForRequest:
and ininitWithRequest:cachedResponse:client
My stripping code looks like this (there might be a cleaner way to strip the param, but this works):
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
NSURLRequest *canonicalRequest = request;
BOOL myProtocolRequest = [[NSURLProtocol propertyForKey:kMYProtocolRequest inRequest:request] boolValue];
if (myProtocolRequest)
{
NSMutableURLRequest *mutableRequest = [request mutableCopyWorkaround];
NSString *originalURLString = mutableRequest.URL.absoluteString;
NSString *regexString = [NSString stringWithFormat:@"(?:[?&])(key=[[:digit:]]{%d}&*)", kMYKeyLength];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:0];
NSTextCheckingResult *result = [regex firstMatchInString:originalURLString options:0 range:NSMakeRange(0, originalURLString.length)];
if (result.numberOfRanges > 1)
{
NSRange keyRange = [result rangeAtIndex:1];
NSLog(@"Removing '%@' from request", [originalURLString substringWithRange:keyRange]);
NSString *replacementURLString = [originalURLString stringByReplacingCharactersInRange:keyRange withString:@""];
mutableRequest.URL = [NSURL URLWithString:replacementURLString];
canonicalRequest = mutableRequest;
}
}
return canonicalRequest;
}
My init code looks like this:
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
{
self = [super initWithRequest:[MYURLProtocol canonicalRequestForRequest:request] cachedResponse:cachedResponse client:client];
return self;
}
I don't like that I have to do this, but I'm finally getting exactly the behavior I want. Hopefully it helps someone out there.
Intercept request with WKWebView
I see that after 5 years this question still generates curiosity, so I describe how I solved it and about some main problems I faced up.
As many who answered here, I have implemented WKURLSchemeHandler
and used new schemes.
First of all the URL that wkwebview launches must not be HTTP (or HTTPS) but one of yours new schemes.
Example
mynewscheme://your-server-application.com
In you WKWebViewConfiguration
conf, I set the handler:
[conf setURLSchemeHandler:[CustomSchemeHandler new] forURLScheme:@"mynewscheme"];
[conf setURLSchemeHandler:[CustomSchemeHandler new] forURLScheme:@"mynewschemesecure"];
In CustomSchemeHandler
I have implemented webView:startURLSchemeTask:
and webView:stopURLSchemeTask:
.
In my case I check if the request is for a file that I just saved locally, otherwise I change actual protocol ("mynewscheme or "mynewschemesecure") with http (or https) and I make request by myself.
At this point I solved the "interception problem".
In this new way we have the webview "location" (location.href
via javascript) with my new scheme and with it new problems started.
First problem is that my applications work mainly with javascript,
anddocument.cookie
has stopped working. I'm using Cordova
framework, so I've develeped a plugin to set and get cookie to
replace document.cookie (I had to do this, because, obviously, I
have also http header set-cookie).Second problem is that I've got a lot of "cross-origin" problems, then
I changed all my urls in relative url (or with new schemes)Third problem is that browser automatically handle server port 80
and 443, omitting them, but has now stopped (maybe because of "not
http location"). In my server code I had to handle this.
Writing down these few rows I admit that it seems to was an easy problem to solve, but I ensure that find out a workaround, how to solve it and integrate with the infinite amount of code has been hard. Every step towards the solution corresponded to a new problem.
WKWebView and authentication
The solved it in beta 3 using the function didReceiveAuthenticationChallenge in WKNavigationDelegate. I've tried it and it works perfectly
Related Topics
Xcode 10, Command Codesign Failed with a Nonzero Exit Code
Error: Cuicatalog: Invalid Asset Name Supplied: (Null), or Invalid Scale Factor:2.000000
Creating Thumbnail from Local Video in Swift
Xcode Debugger Doesn't Print Objects and Shows Nil, When They Aren'T
iOS 7 Uisearchdisplaycontroller Search Bar Overlaps Status Bar While Searching
Avplayer Stops Playing and Doesn't Resume Again
Uirefreshcontrol on Uicollectionview Only Works If the Collection Fills the Height of the Container
iOS Uialertview Button to Go to Setting App
iOS 11 Safe Area Layout Guide Backwards Compatibility
How to Draw a Circle in iOS Swift
How to Detect That the App Is Running on a Jailbroken Device
What Does Get-Task-Allow Do in Xcode
Email & Phone Validation in Swift
Development Team Not Showing in Xcode
Presenting Viewcontroller with Navigationviewcontroller Swift