WKWebView gives SecurityError when bundling html and javascript with app - ios

We are trying to migrate a hybrid app from UIWebView (iOS < 8) to WKWebView (iOS 8), but we are getting SecurityErrors when trying to store stuff using the DOM WebDatabase API (i.e. 'web sql databases').
The following throws an error if the index.html has been loaded from a bundled file with the app
// throws SecurityError: DOM Exception 18
var db = openDatabase('mydb', '1.0', 'key value store', 1);
The same code works fine with UIWebView. I can fallback to using Local Storage for some reason, but using WebSQL databases is a no go. I can only speculate that this has something to do with the same origin policy or something related.
The funny thing is that loading index.html from the network works fine :-/
Any clues as to how I can work around this? Any options to set on the WKWebView that fixes it?
This is how we load the web related stuff:
NSString *htmlPath = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSURL *baseURL = [NSURL fileURLWithPath:htmlPath];
NSURLRequest *request = [NSURLRequest requestWithURL:baseURL];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addScriptMessageHandler:self.myCallbacks name:#"NativeApp"];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:config];
[self.webView loadRequest:request];
The html file simply loads a javascript file that has a relative path, "myCode.js".

There is an issue (OpenRadar) with WKWebView in iOS 8.0 (and 8.1 B1, I think) that prevents it from loading local files. It might be affecting local storage too. See this question for more details.

You can fix this by adding the following method to the UIDelegate of your WKWebView.
- (void) _webView:(WKWebView *)webView
decideDatabaseQuotaForSecurityOrigin:(WKSecurityOrigin *)securityOrigin
currentQuota:(unsigned long long)currentQuota
currentOriginUsage:(unsigned long long)currentOriginUsage
currentDatabaseUsage:(unsigned long long)currentUsage
expectedUsage:(unsigned long long)expectedUsage
decisionHandler:(void (^)(unsigned long long newQuota))decisionHandler {
decisionHandler(1024*1024*50); //default to 50MB
}
It gives all databases a quota of 50MB, instead of the default of 0 which allows them to be opened. This behavior isn't documented, so I don't know where Apple stands with this.
Also, it appears this issue will be fixed in iOS 10.

I've made a 'plugin' that allows you to use WebSQL (more an implementation of it) in the WKWebView. It can be found here
https://github.com/ajwhiteway/WKWebSQL
import WKWebSQL
.
.
.
var webView = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
WKWebSQL.LoadPlugin(webView)
To get it loaded into the page. Versioning isn't really supported at this time. Feel free to add it.

Related

Cannot load local html file into WKWebView on ios device (not simulator) [duplicate]

This question already has answers here:
Load local web files & resources in WKWebView
(5 answers)
Closed 3 years ago.
I'm having troubles with the following code. It's supposed to load a local HTML file into a WKWebView.
It works fine on any simulator I've tried (ios 12 / ios 13).
For a reason I do not understand yet, it does not work on device.
Instead, I get a blank screen. Debugging the webview with the Safari debugger shows that the page is "about:blank". It did not load my file... Why ?
Note that I have checked the existence of the file by downloading the app container from the phone and the file is there...
- (void)loadIndexConteneurWithHash:(NSString *)hash
{
NSString *fileName = #"index.html";
NSString *subpath = hash ? [NSString stringWithFormat:#"%##%#", fileName, hash] : fileName;
NSString *rootContainerDirectoryPath = [NSFileUtility pathRelativeToContentDirectoryForSubpath:#"/www/v2"];
NSURL *URL = [NSURL URLWithString:subpath relativeToURL:[NSURL fileURLWithPath:rootContainerDirectoryPath]];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0f];
[self.webView performSelectorOnMainThread:#selector(loadRequest:) withObject:request waitUntilDone:true];
}
Edit: this question is NOT a duplicate of Load local web files & resources in WKWebView
I do not load my HTML file from the bundle but from the app container as noted originally in my question. I've also tried the loadFileURL method and it does not work either.
In order to access a Local ressource you have to use :
Bundle.main.url(forResource: "MyFile", withExtension: "html")

How to open a web (.html) ressource file with native code (ios app)

I have an ios app built with Cordova.
I would like to open a web ressource file (.html) after a deep linking, in a UIWebView with the handleURL ios method.
I know I'm supposed to do this within my cordova JS files, but I would like to know the way to do it natively?
Let's say I define a my-app URL scheme and want to open file1.html.
What are the ways for doing that?
I have found this:
// Load the html as a string from the file system
NSString *path = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
// Tell the web view to load it
[WebView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
But nothing happens, like it can't find that file.
Why is that?
put all files in a folder and while adding drag drop in project. and select create folder references and copy items if needed. and try to load in UIWebView
You make a new class that inherits from NSURLProtocol.
You need to call [NSURLProtocol registerClass:YOURCLASS] in your app
In your class, implement + (BOOL) canInitWithRequest:(NSURLRequest *) to return true if the scheme matches your scheme (e.g. my-app)
See the docs for NSURLProtocol to see how to implement the rest of the functionality. For simple file system reads, you can implement startLoading and call self.client callbacks (e.g. URLProtocol:didLoadData:). For more complex things, you might need to override more.
See this tutorial: http://www.raywenderlich.com/59982/nsurlprotocol-tutorial

Random error on loading a static resource with a UIWebView

I'm using a UIWebView to load a static resource shipped within my application bundle. Sometimes, I have no clear what the problem could be, I receive the following error within the delegate method - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
NSUnderlyingError = "Error Domain=kCFErrorDomainCFNetwork Code=-1001
The resource is loaded through a NSURLRequest where I set up a timeout interval of 10 seconds, but that interval is not followed. In fact, in the debug console I'm able to see that the error delegate is called after about 2 seconds.
NSURL *htmlFile = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"root" ofType:#"html"] isDirectory:NO];
NSURLRequest* htmlRequest = [NSURLRequest requestWithURL:htmlFile cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
[webView loadRequest:htmlRequest];
The fact is that I cannot always replicate the problem I have. Any suggestions?
P.S. I'm working with an app that runs from 4.3. The problem is also with iOS 7.
According to apple CFNetwork Error Codes Reference this code is a timeout error:
kCFURLErrorTimedOut = -1001
Timeout are not always easy to reproduce most likely you should extend the timeout value when you init the NSURLRequest.
According to the same docs you can query the object for additional information.
For example:
if (CFEqual(CFErrorGetDomain(err), kCFErrorDomainCFNetwork) && CFErrorGetCode(err) == kCFHostErrorUnknown) {
CFDictionaryRef userInfo = CFErrorCopyUserInfo(err);
CFNumberRef number = (CFNumberRef) CFDictionaryGetValue(userInfo, kCFGetAddrInfoFailureKey);
...
CFRelease(userInfo);
}
I suppose that the resource you are looking for is not available. Be sure that the path is correct (ios is case sensitive) and if you are downloading some contents from internet be sure that the download is completed and the file is copied from the temporary folder to the final destination.

Encrypted pdf files crashes on UIWebView in iOS 7

I am trying to open pdf files in my UIWebView in below steps.
self.docViewer is a strong property.
self.docViewer.delegate = self;
NSURLRequest *urlReq = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:docurl]];
[self.docViewer loadRequest:urlReq];
It opens normal pdf files and password protected files with no issues.
When the file is an encrypted one, it crashes after executing delegate methods.
- (void)webViewDidFinishLoad:(UIWebView *)webView
with below log
unsupported security handler `Adobe.PubSec'.
FlateDecode: decoding error: incorrect header check.
FlateDecode: decoding error: incorrect header check.
unsupported security handler `Adobe.PubSec'.
I checked all possible ways mentioned in suggested treads. I didn't find any solution.
Could anyone please help me out of this crash.Thanks a Lot

Clear UIWebView cache when use local image file

I use a UIWebView to load a local html, and there is a PNG file inside the html created by Objc.
After the PNG file has been modified, I reload the html in UIWebView, but the image doesn't change. However, if I quit the app and reopen it, the image file will be changed to the new one.
I have checked the PNG file in Documents with Finder, so I'm sure it has been modified, but the old one is still in UIWebView.
So, as I think that it's a UIWebView cache problem, I've tried:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:url isDirectory:NO ] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:1]]; or NSURLRequestReloadIgnoringCacheData
None of them works, and I can't change the filename, because the PNG file is used in a lot of places (other html and objc code).
I've tried this too:
some.png?r=randomNumber
but it can't be showed.
How do I clear the UIWebView cache when using a local image file inside a local html?
Other than renaming every file on each access, I've only seen one thing work for this and that is modifying the HTML with javascript to add a timestamp onto the image url so it tricks the webview into thinking it's a different file. Images (usually) load the same no matter what you put after the ? in their url. I think this would be easier than renaming every file each time you load the web view. Something like this (using jQuery):
<img src="myimg.jpg" />
<script>
$(function() {
$('img').each(function(i, el) {
$(el).attr('src', $(el).attr('src')+'?pizza='+(new Date()).getTime());
});
});
</script>
I guess this is assuming that this javascript loads and runs before the images are loaded, but in theory this should work.
For a little background, I've made a page in a webview that used RequireJS to asynchronously load quite a few files. I had the EXACT same problem that this question is talking about except that I was loading javascript files instead of images. The key to fixing this issue was adding a timestamp to every path of javascript file and thus tricking the webview (ex me.js?ts=236136236 and whatever.js?ts=3153524623). I found this to work great.
One other thing I needed to do was add a timestamp to the path of my HTML file when loading it into the webview like this:
[NSURL URLWithString:[[NSString stringWithFormat:#"%#/index.html?pizza=%f", webDirectoryPath, [[NSDate date] timeIntervalSince1970]]
I now can modify all the local files and each time the webview appears the changes come through.
You can try this, in your AppDelegate.m
+(void)initialize {
[[NSURLCache sharedURLCache] setDiskCapacity:0];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
}
If your html didn't change and the only change was in image you should use UIWebView's reload message instead of loading request again.
Something like this:
- (void)reloadWebView:(id)sender
{
if (self.loaded) {
[self.webView reload];
} else {
NSString *html = [[NSBundle mainBundle] pathForResource:#"web" ofType:#"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:html]];
[self.webView loadRequest:request];
self.loaded = YES;
}
}
You don't even need any manipulations with cache.

Resources