WKWebView does load resources from local document

2020-01-27 03:52发布

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

6条回答
forever°为你锁心
2楼-- · 2020-01-27 04:15

This works nicely with file URL or remote URL, and whether file is in the bundle or in documents:

if url.isFileURL {
    webView.loadFileURL(url, allowingReadAccessTo: url)
} else {
    let request = URLRequest(url: url)
    webView.load(request)
}
查看更多
Fickle 薄情
3楼-- · 2020-01-27 04:17

This solution helped me:

[configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];
查看更多
虎瘦雄心在
4楼-- · 2020-01-27 04:21

Swift 4 Method

This method allows WKWebView to properly read your hierarchy of directories and sub-directories for linked CSS/JS files. You do NOT need to change your HTML, CSS or JS code.

Updated for Xcode 9.3

Step 1

Import the folder of local web files anywhere into your project. Make sure that you:

Xcode > File > Add Files to "Project"

☑️ Copy items if needed

☑️ Create folder references (not "Create groups")

☑️ Add to targets

Step 2

Go to the View Controller with the WKWebView and add the following code to the viewDidLoad method:

let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
  • index – the name of the file to load (without the .html extension)
  • website – the name of your web folder (index.html should be at the root of this directory)

Conclusion

The overall code should look something like this:

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

    @IBOutlet weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.uiDelegate = self
        webView.navigationDelegate = self

        let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "Website")!
        webView.loadFileURL(url, allowingReadAccessTo: url)
        let request = URLRequest(url: url)
        webView.load(request)
    }

}

If any of you have further questions about this method or the code, I'll do my best to answer. :)

查看更多
混吃等死
5楼-- · 2020-01-27 04:23

Check that ticket: iOS: How to load local files (not in the bundle) in a WKWebView?

var nsurl = URL(fileURLWithPath: URL(fileURLWithPath: URL(fileURLWithPath: documentsDirectory()).appendingPathComponent(user_appli).absoluteString).appendingPathComponent("index.html").absoluteString) //locally
var readAccessToURL: URL? = nsurl.deletingLastPathComponent?.deletingLastPathComponent

if let anURL = readAccessToURL {
    webView?.loadFileURL(nsurl, allowingReadAccessTo: anURL)
}
查看更多
我只想做你的唯一
6楼-- · 2020-01-27 04:24

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.

查看更多
▲ chillily
7楼-- · 2020-01-27 04:33

This works well (Swift 3, Xcode 8):

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    var webView: WKWebView!

    override func loadView() {
        webView = WKWebView()
        webView.navigationDelegate = self
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        if let url = Bundle.main.url(forResource: "file", withExtension: "txt")
        {
            do
            {
                let contents = try String(contentsOfFile: url.path)
                webView.loadHTMLString(contents, baseURL: url.deletingLastPathComponent())
            }
            catch
            {
                print("Could not load the HTML string.")
            }
        }
    }
}
查看更多
登录 后发表回答