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:
With func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { , you get the file url to download. Then download it with JS.
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:
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! :)
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 } }
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:
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">
insteadGiven 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.
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
Import the folder of local web files anywhere into your project. Make sure that you:
☑️ Copy items if needed
☑️ Create folder references (not "Create groups")
☑️ Add to targets
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)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. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With