Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WKWebView not loading local files under iOS 8

People also ask

Is WKWebView the same as Safari?

WKWebView - This view allows developers to embed web content in your app. You can think of WKWebView as a stripped-down version of Safari. It is responsible to load a URL request and display the web content. WKWebView has the benefit of the Nitro JavaScript engine and offers more features.

What is the difference between UIWebview and WKWebView?

Difference Between UIWebview and WKWebViewUIWebview is a part of UIKit, so it is available to your apps as standard. You don't need to import anything, it will we there by default. But WKWebView is run in a separate process to your app,. You need to import Webkit to use WKWebView in your app.

How do I replace webView with WKWebView?

Open “Main. storyboard” and on your ViewController's view drag “WebKit View” i.e. WKWebView. Select a WKWebView and place on your view of a view controller.


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

https://developer.apple.com/videos/wwdc/2015/?id=504

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)
        }
    }
}

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.)


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"

enter image description here

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.

enter image description here


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:.


[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]
];