Reputation: 550
I have loaded HTML string (after downloading the css file to documents) like this:
/* the result is HTML file with references to local resources like
<link href="file:///Users/user/Library/Developer/CoreSimulator/Devices/123/data/Containers/Data/Application/123/Documents/gen1.css" rel="stylesheet" type="text/css">
and to external resources like
<link href="http://api.tiles.mapbox.com/mapbox.js/v1.6.4/mapbox.css" rel="stylesheet" />
*/
PageGenerator *generator = [PageGenerator sharedGenerator];
NSString *result = [generator generatePageWithOptions:options];
// filesDir is file:// + [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] + /
[self.webView loadHTMLString:result baseURL:[NSURL URLWithString:[generator filesDir]]];
Now, the problem is that The external resources contain url's like "//www.site.com/img.png" and the UIWebView rewrites them to "file://www.site.com/img.png", and gets 404 error.
How can I access a local resource without the need to set the base URL? Or how can I rewrite "//" to "http://" instead to "file://"?
Thanks a lot.
Upvotes: 1
Views: 300
Reputation: 11341
What you want to do is install a custom NSURLProtocol by creating a class that subclasses the NSURLProtocol type and implements the NSURLConnectionDelegate and registering it using [NSURLProtocol registerClass:[MyURLProtocol class]];
This is a comprehensive tutorial on doing what you want to do.
Anytime your webview makes a request for an asset, be it a css file or an image, it's going to call + (BOOL)canInitWithRequest:(NSURLRequest *)request
with the request. You can inspect the NSURLRequest to see if the asset has a certain scheme using [request.URL.scheme isEqualToString:@"file']
. In this scenario, you can then return YES
so that your protocol delegate can take over the request.
You'll want to override the URL used however, (or at least the result of what happens with it).
I think you should be able to use something like this:
- (void)startLoading {
NSURLRequest* defaultRequest = self.request; // This is the request for file://...
NSMutableURLRequest* request = [defaultRequest mutableCopy];
// Change the URL or whatever you want on your request here.
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
}
If that doesn't work, you can always look at changing + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
to return your modified request. Note that the result returned should always be identical for a given request as iOS caching mechanisms use the NSURLRequest you return.
You can then add in the rest of the code as shown in the link above to your delegate:
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)stopLoading {
[self.connection cancel];
self.connection = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
This is another example where someone has overridden image requests to always return pictures of David Hasselhoff.
This mechanism is very powerful. For instance, you could use it to theme an app by substituting every css file requested with another at runtime without changing any html.
Upvotes: 1