WKWebView
iOS 8
local files
debugging
web development

WKWebView not loading local files under iOS 8

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If WKWebView would not load local files correctly on iOS 8.1, that was not just a coding mistake. Early WKWebView releases had real limitations around file URLs and local resource access. The practical answer depended on how complex the local content was and whether you could require a newer iOS version.

Why This Was Hard on iOS 8

WKWebView arrived in iOS 8 as the modern replacement for UIWebView, but its local-file story was weaker at the start. Developers could often load a top-level HTML document, then discover that relative JavaScript, CSS, images, or AJAX requests behaved inconsistently or failed entirely.

The root issue was not just syntax. WKWebView used a stricter multi-process model with tighter file-access behavior than the older web view.

That led to a common pattern:

  • main HTML appears to load
  • local subresources do not load reliably
  • file-based XHR or similar browser features fail

What Works on Modern iOS

On newer iOS versions, the clean solution is loadFileURL(_:allowingReadAccessTo:), which explicitly grants read access to a directory tree.

swift
1import UIKit
2import WebKit
3
4final class LocalPageViewController: UIViewController {
5    private var webView: WKWebView!
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9
10        webView = WKWebView(frame: view.bounds)
11        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
12        view.addSubview(webView)
13
14        if let fileURL = Bundle.main.url(forResource: "index", withExtension: "html") {
15            let accessURL = fileURL.deletingLastPathComponent()
16            webView.loadFileURL(fileURL, allowingReadAccessTo: accessURL)
17        }
18    }
19}

If you only support modern iOS, this is the answer. But it does not help on iOS 8.1, because that API arrived later.

Practical iOS 8 Workarounds

For iOS 8-era support, the two main workarounds were:

  • load the HTML manually with loadHTMLString(_:baseURL:)
  • serve the bundled files through a tiny local HTTP server

The simpler workaround was:

swift
1if let fileURL = Bundle.main.url(forResource: "index", withExtension: "html"),
2   let html = try? String(contentsOf: fileURL, encoding: .utf8) {
3    let baseURL = fileURL.deletingLastPathComponent()
4    webView.loadHTMLString(html, baseURL: baseURL)
5}

This can work for bundled pages that mostly need relative paths for images, CSS, and JavaScript. It is often good enough for static content.

However, it still does not magically solve every local-web-app behavior. If the page expects richer browser semantics around file URLs, you can still hit limitations.

When a Local HTTP Server Was the Real Answer

For more complex offline web content, many teams used a lightweight embedded HTTP server so the page was loaded as http://127.0.0.1:port/... instead of file:///....

Conceptually:

swift
// Pseudocode:
// 1. Start a small local server rooted at your bundled content directory.
// 2. Point WKWebView at http://127.0.0.1:8080/index.html

This was heavier, but it gave the web content a more normal browser environment and avoided some of the file-URL restrictions that made iOS 8 WKWebView painful.

If your page behaved more like a mini web app than a single static document, the local-server route was often the most reliable workaround on old systems.

The Decision Framework

If you are maintaining historical code or reading old answers, the practical guidance is:

  • for simple bundled pages, try loadHTMLString(_:baseURL:)
  • for complex local apps, use a local HTTP server
  • if you can drop iOS 8 support, move to loadFileURL(_:allowingReadAccessTo:)

That framing matters because many old snippets make it sound like there was one missing magic method for iOS 8. There was not.

Common Pitfalls

The biggest pitfall is assuming that loading the top-level HTML file means all local resources will work. On iOS 8 WKWebView, subresource access was often the real failure point.

Another mistake is comparing behavior directly with UIWebView and expecting identical file handling. WKWebView was not a drop-in match in this area.

Developers also sometimes forget that loadHTMLString(_:baseURL:) only helps if the HTML content itself is available and the relative references make sense from that base URL.

Finally, if you no longer support iOS 8, do not keep the old workaround architecture around out of habit. The modern file-loading API is simpler and clearer.

Summary

  • Early WKWebView on iOS 8 had real local-file limitations.
  • 'loadFileURL(_:allowingReadAccessTo:) is the modern fix, but it was not available on iOS 8.1.'
  • 'loadHTMLString(_:baseURL:) can work for simpler bundled content.'
  • A local HTTP server was often the most reliable workaround for complex local web apps.
  • If iOS 8 support is gone, use the newer file-loading API and remove legacy workarounds.

Course illustration
Course illustration

All Rights Reserved.