56

Unlike with UIWebView and previous versions of WKWebView (iOS 10 & macOS 10.12), the default load operation for local files has moved from Bundle.main.path to Bundle.main.url. Similarly, loadFileURL has also become the default function to load local resources in WKWebView.

I know that .path and .url are entirely different and have both worked in the past – .path historically being the default-chosen method; however, it seems that the latest versions of Swift have broken most, if not all, .path solutions. The .path solutions seem to now flatten the directory hierarchy, putting all of the CSS, JS, and any other sub-directory contents, into one big directory. This causes loading errors when WKWebView attempts to load index.html, for example, with a linked, sub-folder stylesheet (ie. /css/style.css).

After seeing numerous questions and countless uncertain/broken answers to match, is there a quick and painless solution for implementing a WKWebView that can load local resources (including linked CSS/JS files), without any workarounds?

jscs
  • 63,694
  • 13
  • 151
  • 195
JDev
  • 5,168
  • 6
  • 40
  • 61

5 Answers5

96

Updated for Swift 4, Xcode 9.3


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

Solution (Quick)

  1. Add the web folder to your project (File > Add Files to Project)
    • Copy items if needed
    • Create folder references *
    • Add to targets (that are applicable)
  2. Add the following code to the viewDidLoad and personalize it to your needs:

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

Solution (In-Depth)

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!

drWisdom
  • 502
  • 1
  • 4
  • 12
JDev
  • 5,168
  • 6
  • 40
  • 61
  • 1
    Thank you so much. Have been struggling to find a working solution! – Rhuari Glen Jun 29 '18 at 15:55
  • 1
    Would this also be the way to wrap a simple HTML5 (including several JS libs) app into a macOS application? – z80crew Jul 17 '18 at 10:18
  • what is this base url in url request method ?? – vivek Aug 07 '18 at 05:09
  • Read it twice and remember to: Create folder references (not "Create groups") – Shial Aug 31 '18 at 02:09
  • 30
    I'm confused why there's a call to `.load(request)` too. I think `loadFileURL()` is the preferred way to load local files, does the `load(request)` do anything extra? – Austin Oct 06 '18 at 17:57
  • for what it's worth, I've just spent the last couple of hours trying to get a simple MacOS app to read from a directory - the only way it would do it was through using load(request) and not loadFileURL() – Nostradamus Jan 02 '19 at 03:54
  • 7
    I get: Fatal error: Unexpectedly found nil while unwrapping an Optional value. The only way I can get it to work is if I put the index.html in the same folder as the xcodeproj file and use "/" as subdirectory – Curtis Feb 03 '19 at 21:09
  • For those who couldn't have solved the problem when using WKWebView, **you should add Webkit Library in your Xcode project.** Check this [post](https://stackoverflow.com/questions/54048414/wkwebview-is-not-getting-intialized-in-swift-4-xcode-10/54050167#comment98762031_54050167). – cadenzah May 10 '19 at 13:33
  • 6
    You do not need the call to `load`. Just call `loadFileURL`. The 2nd `load` is pointless. – rmaddy Oct 17 '19 at 16:02
  • In WKWebView to load local file it is taking 10 Sec But in UIwebview it is too quick – SM18 Jun 09 '20 at 07:32
  • Is there a way to load HTML code, created in memory, maybe held in a string, and passing that directly to the WKWebView? – Dominique Jun 08 '21 at 13:35
15

This work For Me:

        WKWebView *wkwebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
        wkwebView.navigationDelegate = self;
        wkwebView.UIDelegate = self;
        [wkwebView.configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];
        NSURL *url = [NSURL fileURLWithPath:YOURFILEPATH];
        [wkwebView loadFileURL:url allowingReadAccessToURL:url.URLByDeletingLastPathComponent];
        [self.view addSubview:wkwebView];
Zouhair Sassi
  • 1,403
  • 1
  • 13
  • 30
2

I was facing the same issue my condition was, I am downloading some HTML content from the server and save it to the Document directory and show it inside the application. The same controller uses the LIVE URL also I have to put the condition with url scheme. Tested on iOS 13 Xcode 11

  if Url.scheme == "file" as String {
         wkWebView.loadFileURL(Url, allowingReadAccessTo: Url)
    }
    else {
         let request = URLRequest.init(url: Url, cachePolicy:.reloadIgnoringLocalCacheData, timeoutInterval:60)
         wkWebView.load(request)
    }

it worked perfect for me

Mohammad Parvez
  • 409
  • 4
  • 12
  • i have downloaded the images in a local documents directory folder now i have a html string which contains the last path component of img , which are ideally saved in docDirectory , how can i load all the images in that string to wkwebview , can you guide me with steps please. – iMinion Sep 09 '20 at 22:05
  • in which form did u saved the html content , I have saved all the images separate – iMinion Sep 10 '20 at 18:23
2

1, Open project setting and got to Build Phases tab, open Link Binary with Libraries. Add Webkit framework.

2, Add the html to your project (Copy items if needed)

3, add this to your code: @IBOutlet weak var webView: WKWebView!

4, viewDidLoad viewDidLoad

  • I had to migrate ancient code. Adding Webkit framework was a very helpful hint for me. Many thanks. – CGN Jun 27 '22 at 08:59
0

It is possible to use resources from you project and even shared libraries inside html code with WKWebView.
For instance, I show how to load pic.png from bundle that contains ResourceContainingBundleClassNamed class.

NSSTring * htmlCode = @"<img src='pic.png'>";
NSBundle * bundle = [NSBundle bundleForClass:[ResourceContainingBundleClassNamed class]];
NSURL * base = bundle.resourceURL;
WKWebView * represent = [WKWebView new];
[represent loadHtmlString: htmlCode baseURL: base];

The magic is in resourceURL bundle property. If you just get path for desired file, then convert it to URL - no success.

ETech
  • 1,613
  • 16
  • 17