-3

I have a WKWebView. The webpage load most of its css file from CDN. But, there is this frequently-used large css file that I'd like to put it in the app Bundle so that webview load faster. Does anyone know how to load from bundle?

A simple example. A webpage page needs to load a file at https://cdn.example.com/styles.css. Instead of loading it from file servers, I want to redirect the request (https://cdn.example.com/styles.css) so that it can response with the content styles.css in the bundle, or even from disk cache.

I don't want to insert css file. I wanna redirect some css files request to my local file url instead.

A Swift version will be great.

Thank you.

wzso
  • 3,482
  • 5
  • 27
  • 48

3 Answers3

2

I have face same issue , struggle lot to fix issue, finally find solution and It worked like a charm! for me,

I want to apply CSS by using url not bundle CSS file location ,

Follow steps ,

  1. Give your CSS file link let say ,

       let cssURL "https://xyz/styles/style.css" 
    

2.Change JavaScript injection code style to link like below code ,

        let source = """
        var link = document.createElement('link');
        link.href = '\(url)';
        link.rel= 'stylesheet'
        document.head.appendChild(link);
        """

3. Now it's time to inject JS to webView like ,

       let userScript = WKUserScript(source: source,
                                      injectionTime: .atDocumentEnd,
                                      forMainFrameOnly: true)

        let userContentController = WKUserContentController()
        userContentController.addUserScript(userScript)

        let configuration = WKWebViewConfiguration()
        configuration.userContentController = userContentController

        self.webView = WKWebView(frame: self.documentDiscriptionView.bounds,
                                 configuration: configuration)
        self.webView.navigationDelegate = self
        self.webView.scrollView.isScrollEnabled = false
        self.webView.scrollView.bounces = false

        self.webView.isOpaque = false
        self.webView.backgroundColor = UIColor.clear
        self.webView.scrollView.backgroundColor = UIColor.clear

        self.self.webViewContainerView.addSubview( self.webView)
  1. I have added all above code in "override func viewDidAppear(_ animated: Bool)" method , just copy and paste code , it will work for you.

For Details description refer Detail Answer

Hope this solution will help for someone , Please let me know if required other details, I will happy to help .

Jaywant Khedkar
  • 5,941
  • 2
  • 44
  • 55
  • But u didn’t load the css file from bundle. What I want is to use the files in my bundle instead of requesting from remote, so the webview can hopefully load faster. I believe part of the solution will include how to capture the requests by webview to load css file, and how to twist the request to stop asking remote server for file and return the file from bundle. – wzso May 21 '20 at 23:28
  • In your case then use @Pranay solution , I thought you want to load CSS file from remote server – Jaywant Khedkar May 23 '20 at 04:13
1

Create style.css file as below and drag & drop to your project directory

@font-face {
    font-family: "Titillium Web";
    font-weight: normal;
    src: url("TitilliumWeb-Regular.ttf")
}

@font-face {
    font-family: "Titillium Web";
    font-style: italic;
    src: url("TitilliumWeb-Italic.ttf")
}

@font-face {
    font-family: "Titillium Web";
    font-weight: bold;
    src: url("TitilliumWeb-Bold.ttf")
}
body {
    margin: 0;
    font-family: "Titillium Web";
    font-weight: normal;
    font-size: 13pt;
}

Add below source code in UIViewController's viewDidLoad()

override func viewDidLoad() {
    super.viewDidLoad()

    let html = """
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. <b>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</b>
    <br>
    <i>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</i>
    <br>
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
"""

    let htmlStart = "<HTML><HEAD><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, shrink-to-fit=no\"></HEAD><BODY>"
    let htmlEnd = "</BODY></HTML>"
    let htmlString = "\(htmlStart)\(html)\(htmlEnd)"
    webView.loadHTMLString(htmlString, baseURL: Bundle.main.bundleURL)
    self.view.addSubview(webView)
  }

Create WKWebView object as below and run the app, It will load the content.

  lazy var webView: WKWebView = {
    guard
      let path = Bundle.main.path(forResource: "style", ofType: "css"),
      let cssString = try? String(contentsOfFile: path).components(separatedBy: .newlines).joined()
      else {
        return WKWebView()
    }

    let source = """
    var style = document.createElement('style');
    style.innerHTML = '\(cssString)';
    document.head.appendChild(style);
    """

    let userScript = WKUserScript(source: source,
                                  injectionTime: .atDocumentEnd,
                                  forMainFrameOnly: true)

    let userContentController = WKUserContentController()
    userContentController.addUserScript(userScript)

    let configuration = WKWebViewConfiguration()
    configuration.userContentController = userContentController

    let webView = WKWebView(frame: self.view.frame,
                            configuration: configuration)
    return webView
  }()

enter image description here

Pranay
  • 372
  • 1
  • 10
0

You could preload your html to a String, inject the content of your local CSS inside of some style-tags and finally show that modified string in your WKWebView.

Below you will see a working solution, you just need to drag a file called inject.css inside of your project. This file contains the style you want to apply.

Make sure that you have a WKWebView called webView and a button connected to the outlets to make the following solution work (This solution probably needs some refactoring):

import UIKit
import WebKit

class ViewController: UIViewController {
  @IBOutlet weak var webView: WKWebView!

  @IBAction func loadStuffTapped(_ sender: Any) {

    injectCssAndShow(url: "https://www.wikipedia.org/")

  }

  // this one loads the css from your bundle
  func loadCss()->String? {
    if let filepath = Bundle.main.path(forResource: "inject", ofType: "css") {
      do {
        let contents = try String(contentsOfFile: filepath)
        print(contents)
        return contents
      } catch {
        // contents could not be loaded
      }
    } else {
      // inject.css not found!
    }
    return nil
  }

// this (kind of bloated...) function loads the orignal html, injects your css and shows the modified version inside of your WKWebview
  func injectCssAndShow(url: String) {

    let request = NSMutableURLRequest(url: NSURL(string: url)! as URL,
                                      cachePolicy: .useProtocolCachePolicy,
                                      timeoutInterval: 10.0)
    request.httpMethod = "GET"

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
      if (error != nil) {
        print(error as Any)
      } else {
        let htmlString = String(decoding: data ?? Data(), as: UTF8.self)

        DispatchQueue.main.async {

          let result = htmlString.range(of: "<title>",
                                        options: NSString.CompareOptions.literal,
                                        range: htmlString.startIndex..<htmlString.endIndex,
                                        locale: nil)

          if let range = result {
            let start = range.lowerBound
            let injection = self.loadCss() ?? ""
            let modifiedHtml = "\(htmlString[htmlString.startIndex..<start]) <style>\(injection)</style> \(htmlString[start..<htmlString.endIndex])"
            self.webView.loadHTMLString(modifiedHtml, baseURL: URL.init(string: url))
          }
        }
      }
    })

    dataTask.resume()

  }
}

Animation of posted solution

laka
  • 644
  • 8
  • 23