Search code examples
iosswiftwkwebview

WKWebView does load resources from local document folder


In my Swift iOS app, I want to download some dynamic HTML pages from a remote server, save them in the document directory, and display those pages from document directory.

I was using this to load the page:

var appWebView:WKWebView?
...
appWebView!.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: htmlPath)))

Everything works on the simulator, but when I moved to real phones, it just showed a blank page. I then connected to the app using Safari, and found it complained with "Failed to load resource".

I then tried to first read the content of the page at htmlPath, then use

appWebView!.loadHTMLString()

to load the page. It works when the HTML page is simple. But if the HTML references something else, i.e. a JavaScript file also in the document directory (with an absolute path like <script src="file:////var/mobile/Containers/Data/Application/762035C9-2BF2-4CDD-B5B1-574A0E2B0728/Documents/xxxxx.js">), it will fail to load.

Does anyone know why this happens, and how to resolve the issue?

More info:

  • XCode version: 7.3.1
  • Deployment Target: 8.1 (I tried to use 9.3 too, but that didn't help.)

Solution

  • This is a simplified version of what I have used to load local files in a project of mine (iOS 10, Swift 3). I have just updated my code (7.5.2017) after testing it out again on iOS 10.3.1 and iPhone 7+ as requested by Raghuram and Fox5150 in the comments.

    I just created a completely new project and this is the folder structure:enter image description here

    Update 19.04.2018: Added a new feature to download a .zip with HTML, CSS, JS files, unzip it in /Documents/ (Alamofire + Zip) and then load those files into the webView. You can find it in the GitHub sample project as well. Again, feel free to fork & star! :)

    Update 08.02.2018: finally added a GitHub sample project, which also includes a local JavaScript file. Feel free to fork & star! :)

    Version 1 with webView.loadFileURL()

    ViewController.swift

    import UIKit
    import WebKit
    class ViewController: UIViewController, WKNavigationDelegate {
        override func viewDidLoad() {
            super.viewDidLoad()
            let webView = WKWebView()
            let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
            let htmlUrl = URL(fileURLWithPath: htmlPath!, isDirectory: false)
            webView.loadFileURL(htmlUrl, allowingReadAccessTo: htmlUrl)
            webView.navigationDelegate = self
            view = webView
        }
    }
    

    Version 2 with webView.loadHTMLString()

    ViewController.swift

    import UIKit
    import WebKit
    class ViewController: UIViewController, WKNavigationDelegate {
        override func viewDidLoad() {
            super.viewDidLoad()
            let webView = WKWebView()
            let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
            let folderPath = Bundle.main.bundlePath
            let baseUrl = URL(fileURLWithPath: folderPath, isDirectory: true)
            do {
                let htmlString = try NSString(contentsOfFile: htmlPath!, encoding: String.Encoding.utf8.rawValue)
                 webView.loadHTMLString(htmlString as String, baseURL: baseUrl)
            } catch {
                // catch error
            }
            webView.navigationDelegate = self
            view = webView
        }
    }
    

    Gotchas to look out for:

    • Make sure that your local html/js/css files are in Project -> Target -> Build Phases -> Copy Bundle Resources
    • Make sure that your html files don't reference relative paths e.g. css/styles.css because iOS will flatten your file structure and styles.css will be on the same level as index.html so write <link rel="stylesheet" type="text/css" href="styles.css"> instead

    Given the 2 versions and the gotchas here are my html/css files from the project:

    web/index.html

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
        <title>Offline WebKit</title>
        <link rel="stylesheet" type="text/css" href="styles.css">
      </head>
      <body>
        <h1 id="webkit-h1">Offline WebKit!</h1>
      </body>
    </html>
    

    web/css/styles.css

    #webkit-h1 {
      font-size: 80px;
      color: lightblue;
    }
    

    If somebody wants a GitHub sample project, tell me in the comments section and I'll upload it.