WKWebView does load resources from local document folder

后端 未结 8 518
伪装坚强ぢ
伪装坚强ぢ 2020-11-28 09:38

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.

相关标签:
8条回答
  • 2020-11-28 10:09

    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! :)

    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.

    0 讨论(0)
  • 2020-11-28 10:13

    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:

    ☑️ 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. :)

    0 讨论(0)
  • 2020-11-28 10:14

    Constructing the URLs this way allowed me to load resources from the document directory with WKWebView:

    guard let docDir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) else {
        return
    }
    
    let resourceURL = docDir.appendingPathComponent("/Path/To/Your/Resource")
    self.wkWebView.loadFileURL(resourceURL, allowingReadAccessTo: docDir)
    
    0 讨论(0)
  • 2020-11-28 10:19

    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.")
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-28 10:22

    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)
    }
    
    0 讨论(0)
  • 2020-11-28 10:23

    The files must be in the document directory.

    I implemented the following to retrieve a document:

    let documentDirUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    let fileNameWithExtension = "IMG_0002.PNG"
    let indexFileUrl = documentDirUrl.appendingPathComponent(fileNameWithExtension)
    if FileManager.default.fileExists(atPath: indexFileUrl.path) {
        webView.loadFileURL(indexFileUrl, allowingReadAccessTo: documentDirUrl)
    }
    
    0 讨论(0)
提交回复
热议问题