WKWebView not loading local files under iOS 8
Solution 1
They finally solved the bug! Now we can use -[WKWebView loadFileURL:allowingReadAccessToURL:]
.
Apparently the fix was worth some seconds in WWDC 2015 video 504 Introducing Safari View Controller
For iOS8 ~ iOS10 (Swift 3)
As Dan Fabulish's answer states this is a bug of WKWebView which apparently is not being solved any time soon and as he said there is a work-around :)
I am answering just because I wanted to show the work-around here. IMO code shown in https://github.com/shazron/WKWebViewFIleUrlTest is full of unrelated details most people are probably not interested in.
The work-around is 20 lines of code, error handling and comments included, no need of a server :)
func fileURLForBuggyWKWebView8(fileURL: URL) throws -> URL {
// Some safety checks
if !fileURL.isFileURL {
throw NSError(
domain: "BuggyWKWebViewDomain",
code: 1001,
userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
}
try! fileURL.checkResourceIsReachable()
// Create "/temp/www" directory
let fm = FileManager.default
let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("www")
try! fm.createDirectory(at: tmpDirURL, withIntermediateDirectories: true, attributes: nil)
// Now copy given file to the temp directory
let dstURL = tmpDirURL.appendingPathComponent(fileURL.lastPathComponent)
let _ = try? fm.removeItem(at: dstURL)
try! fm.copyItem(at: fileURL, to: dstURL)
// Files in "/temp/www" load flawlesly :)
return dstURL
}
And can be used as:
override func viewDidLoad() {
super.viewDidLoad()
var fileURL = URL(fileURLWithPath: Bundle.main.path(forResource:"file", ofType: "pdf")!)
if #available(iOS 9.0, *) {
// iOS9 and above. One year later things are OK.
webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
} else {
// iOS8. Things can (sometimes) be workaround-ed
// Brave people can do just this
// fileURL = try! pathForBuggyWKWebView8(fileURL: fileURL)
// webView.load(URLRequest(url: fileURL))
do {
fileURL = try fileURLForBuggyWKWebView8(fileURL: fileURL)
webView.load(URLRequest(url: fileURL))
} catch let error as NSError {
print("Error: " + error.debugDescription)
}
}
}
Solution 2
WKWebView can't load content from file: URLs via its loadRequest:
method. http://www.openradar.me/18039024
You can load content via loadHTMLString:
, but if your baseURL is a file: URL, then it still won't work.
iOS 9 has a new API that will do what you want, [WKWebView loadFileURL:allowingReadAccessToURL:]
.
There is a workaround for iOS 8, demonstrated by shazron in Objective-C here https://github.com/shazron/WKWebViewFIleUrlTest to copy files into /tmp/www
and load them from there.
If you're working in Swift, you could try nachos4d's sample instead. (It's also much shorter than shazron's sample, so if you're having trouble with shazron's code, give that a try instead.)
Solution 3
An example of how to use [WKWebView loadFileURL:allowingReadAccessToURL:] on iOS 9.
When you are moving the web folder to a project, select "Create folder references"
Then use code that is something like this(Swift 2):
if let filePath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp/index.html"){
let url = NSURL(fileURLWithPath: filePath)
if let webAppPath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp") {
let webAppUrl = NSURL(fileURLWithPath: webAppPath, isDirectory: true)
webView.loadFileURL(url, allowingReadAccessToURL: webAppUrl)
}
}
In the html file use filepaths like this
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
not like this
<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
An example of directory that is moved to a xcode project.
Solution 4
Temporary workaround: I'm using GCDWebServer, as suggested by GuidoMB.
I first find the path of my bundled "www/" folder (which contains an "index.html"):
NSString *docRoot = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"].stringByDeletingLastPathComponent;
... then start it up like so:
_webServer = [[GCDWebServer alloc] init];
[_webServer addGETHandlerForBasePath:@"/" directoryPath:docRoot indexFilename:@"index.html" cacheAge:3600 allowRangeRequests:YES];
[_webServer startWithPort:port bonjourName:nil];
To stop it:
[_webServer stop];
_webServer = nil;
Performance appears fine, even on an iPad 2.
I did notice a crash after the app goes into the background, so I stop it on applicationDidEnterBackground:
and applicationWillTerminate:
; I start/restart it on application:didFinishLaunching...
and applicationWillEnterForeground:
.
Solution 5
[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];
This solved the problem for me iOS 8.0+ dev.apple.com
also this seems to worked just fine too...
NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
loadFileURL: [NSURL fileURLWithPath:FILE_PATH]
allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];
Lim Thye Chean
Virtual GS studio develops and publishes multimedia eBooks, retro games and children applications for iOS and Android devices.
Updated on May 19, 2020Comments
-
Lim Thye Chean about 4 years
For previous iOS 8 betas, load a local web app (in Bundle) and it works fine for both
UIWebView
andWKWebView
, and I even ported a web game using the newWKWebView
API.var url = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("car", ofType:"html")) webView = WKWebView(frame:view.frame) webView!.loadRequest(NSURLRequest(URL:url)) view.addSubview(webView)
But in beta 4, I just got a blank white screen (
UIWebView
still work), looks like nothing is loaded or executed. I saw an error in the log:Could not create a sandbox extension for
/
Any help to guide me to the right direction? Thanks!
-
Lim Thye Chean almost 10 yearsThanks I will try it out.
-
Lim Thye Chean almost 10 yearsThis seems to work only for the HTML files but not other resources.
-
Lim Thye Chean almost 10 yearsThis only works for HTML file itself not the resources, right?
-
Oleksii almost 10 yearsUnfortunately yes, only the HTML file seems to be loading. Let's hope that it's just a bug, and not new restriction to load local files. I'm trying to find this bug in the WebKit sources.
-
mszaro almost 10 yearsI've run into the same issue - HTML files are loaded but images and other resources that are on the local filesystem are not loaded. In the console, WebKit is throwing an error "not allowed to load local resource". I filed a bug for this, radar# 17835098
-
Mike M over 9 yearsOne workaround (mentioned here: devforums.apple.com/message/1051027) is to move content into tmp and access it from there. My quick test seems to indicate that that it does work...
-
invalidArgument over 9 yearsHi @Dan. I downloaded your example from git. Thank you for that. I seem to get a valid URL, with all needed files copied to a new folder in /Documents. But the webview now tries to load the page forever. Any clue? I had already posted a question here: stackoverflow.com/q/26455432/873436, if you prefer to answer there...
-
andrew k over 9 yearsUsing a "server" probably eats up any performance benefits that
WKWebView
provides overUIWebView
. Might as well stick with the old API until this is fixed. -
EthanB over 9 years@ray Not with a Single-Page App.
-
xhg over 9 yearsI tried moving to /tmp, but seems still blank, anybody tried and succeed?
-
geppy over 9 yearsDoes the /tmp/ workaround work on 8.0? It's not working for me.
-
Greg Maletic over 9 yearsMoving the files to /tmp works for me. But...my god, how did this get through testing?
-
Tom Hamming over 9 years
-
nacho4d over 9 yearsThat sample is WAY TOO MUCH code just for a demo. The file to be read must be inside
/tmp/www/
. UseNSTemporaryDirectory()
andNSFileManager
to createwww
directory (because there is no such directory by default). Then copy your file in there and now read this file :) -
Dan Fabulich over 9 years@nacho4d If you prepare a simpler demo, I'll link to that instead.
-
AwDogsGo2Heaven over 9 yearsI'm guessing this works because maybe with normal remote websites its downloading the files into the TMP directory anyways, right? So dumping your files there makes it treat it as if it did download that. It sucks to have to copy all those files, but I guess it only has to happen once during start up. If you did it this way too, it wouldn't be too hard to switch things out when this bug is fixed, as the only thing that would change is not copying the files, and perhaps a different api call based on the description of the wkwebkit fix.
-
Stefan Arentz over 9 yearsIs it safe to store data in the
NSTemporaryDirectory()
directory? What happens if the device runs low on space? Will iOS remove files from the temporary directories? -
nacho4d over 9 yearsAs its name suggests it is temporary. So before showing something you should check it exists. The OS wipes that directory from time to time.
-
chipbk10 about 9 yearsAnd how to show it on a WebView? I really don't understand what GCDWebServer is for?
-
EthanB about 9 yearsInstead of pointing the webview to "file://.....", you point it to "http ://localhost:<port>/...".
-
ninjaneer about 9 years8.3 and it's still not fixed...?
-
Steven Fisher about 9 yearsThis sounds more like a choice than a bug to me.
-
chmaynard about 9 yearsI decided to use XWebView after looking at other work-arounds for this problem. XWebView is a framework implemented in Swift but I had no problem using it in my iOS 8 Objective-C app.
-
Karol Klepacki about 9 yearsSeems to be fixed in 9.0
-
Piwaf almost 9 yearsSome modifications for it to copy the entire folder, images and all.
let orgFolder = NSBundle.mainBundle().resourcePath! + "/www"; var newFilePath = pathForBuggyWKWebView(orgFolder) self.loadingWebView!.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(newFilePath!+"/Loading.html")!))
-
user3246173 almost 9 yearsI can't get this to work on iOS 8 in simulator. I want to put an .png in /tmp/www and then use img tag in my html. What should I use for the img tag src?
-
Patrick Fabrizius almost 9 yearsPiwaf's solution worked slightly better for me, note that it should be "self.webView" and not "self.loadingWebView" given the example above though
-
jvoll almost 9 yearsDoes this workaround work on the simulator? It seems to be failing on the copyItemAtPath. Perhaps I can't write to that directory on the simulator?
-
nacho4d almost 9 yearsIn the simulator you don't this workaround. You can place an #ifdef inside pathForBuggyWKWebView and return
filePath
without doing anything :) -
jvoll almost 9 yearsThanks @nacho4d. tldr; the /tmp folder solution will not work on 8.0 but will on 8.0.2. I was having trouble getting this solution to work on my test device and eventually cloned shazron's repo to give it a try. That didn't work either. Turns out that shazron's solution doesn't work on the version of iOS my device was running 8.0 (12A366) on iPhone 6 Plus. I tried it on a device (iPad Mini) running iOS 8.0.2 and it works fine.
-
Tinkerbell almost 9 yearswas exactly what I needed. Thanks!
-
SleepNot over 8 yearsIs there a delegate method like webViewDidFinishLoad for loadFileURL:allowingReadAccessToURL:?
-
DàChún over 8 yearsit should be let _ = try? fm.removeItemAtURL(dstURL) instead of let _ = try? fileMgr.removeItemAtURL(dstURL)
-
nacho4d over 8 yearsThanks, I just fixed it!
-
User1 over 8 years@KarolKlepacki are you sure? From the bug comments it seems to suggest it is only partially fixed? http://www.openradar.me/18039024#ag9zfm9wZW5yYWRhci1ocmRyFAsSB0NvbW1lbnQYgICA4LO39AoM
-
jenson-button-event over 8 yearsOnly for simple websites. If you are using ajax or loading local views via angular, expect "Cross origin requests are only supported for HTTP". Your only fallback is the local webserver approach which i do not like since it is visible on the local network. This needs noting the in the post, save folk some hours.
-
TruMan1 about 8 yearsWhat if the
index.html
needs to be dynamically generated, such as being a template with placeholders then reused for different content records? I guess I'm forced to useloadHTMLString
, but then localcss
andjs
files stop working? -
Rodrigo Lima about 8 years@jenson-button-event have you found any other solutions to deal with CORS issues with local files on iOS 9+? As you mentioned, loadFileURL:allowReadAccess does not seem to help in those cases.
-
jenson-button-event about 8 years@RodrigoLima - no - went with the local web server approach. had no issues with it. I just don't like the fact the server is publicly visible.
-
nullqube almost 7 yearsinstead of FILE you can put DIR too .
-
plivesey over 6 yearsThis is a great find. I don't know why it isn't voted higher.
-
plivesey over 6 yearsThis post has more info (including links to webkit source): stackoverflow.com/questions/36013645/…
-
Grigory Entin over 5 yearsFor those wondering why their
-[WKWebView loadFileURL:allowingReadAccessToURL:]
doesn't work: make sure to check you give it "standard" file URL. I could not get it working with URL built withURL(string: path, relativeTo: fileURL)
. (And yes, isFileURL returned true for it/it worked in Simulator but not on the device). Applying.standardizedFileURL
to the URL solved the issue for me. -
shahil over 5 yearsin the swift version, please add a semicolon before base64.
result += "data:image/" + attachmentType + ";base64," + base64String
-
Żabojad over 4 years@GrigoryEntin I have a very similar issue but standardizedURL doesn't help :(. more details there: stackoverflow.com/questions/58437328/…
-
Vignesh Kumar over 4 yearsconfiguration.preferences setValue will give crash on iOS 9.3
-
hotdogsoup.nl over 4 yearsThanks, this is the only thing that worked for me, because I download a .gif file to a temp folder on the device, and then load that file into the
WKWebView
as an<img>
within a HTMLstring. -
arlomedia about 4 yearsI'm using the iOS 13 SDK but trying to keep compatibility with iOS 8, and the
allowFileAccessFromFileURLs
approach crashes withNSUnknownKeyException
. -
Curtis about 4 yearsthis worked for me only if I replaced "productURL" with "FILE_PATH"
-
iMinion almost 4 years@nacho4d the above solution does not work for ios13 if u could help me out stackoverflow.com/questions/63800637/…
-
Dipesh Pokhrel over 3 years@patrickd105 , I have some doubt regarding the result variable, and can you post the code with the calling and caller function so that everything become clear
-
famfamfam over 2 yearsi have problem when inject bootstrapt file, please help stackoverflow.com/questions/71421255/…