2

I'm trying to create multiple WKWebView views inside of a background process and then add them to a view on the main thread once they are all done loading.

Each of the WKWebView's contains a chart rendered via javascript so the load time takes about a second per WKWebView so I'm trying to offload the processing to the background so the UI isn't blocked.

This works fine when dispatch_get_main_queue is commented out, however the ui is blocked for 5-10 seconds. Only the brown background of the WKWebView shows up, none of the contents from the webpage.

 var webViews : [WKWebView] = []   

 var myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
 dispatch_async(myQueue, {

 for i in 0...10
 {
      var url : NSURL? = NSURL(string:"http://google.com")
      var req = NSURLRequest(URL:url!)

      var webview = WKWebView(frame:CGRectMake(0, height * CGFloat(i), width, height))         
      webview.loadRequest(req)
      webview.backgroundColor = UIColor.brownColor()

      self.webViews.append(webview)
  }

    dispatch_async(dispatch_get_main_queue(),{

      for item in self.webViews
      {
         self.view.addSubview(item)
      }

     });
  });
TWilly
  • 4,863
  • 3
  • 43
  • 73

2 Answers2

1

If I change the WKWebView to UIWebView, there is a crash.

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread.

Calling a UIKit method from a thread other than main thread is not allowed. WKWebView is also a UIView subclass. So I suggest you move setting frame and addSubView method out of the block, and put it before you call dispatch_get_global_queue, and inside dispatch_get_global_queue block, you load the request one by one.

Edit

To monitor if a request has finished loading, you can implement WKNavigationDelegate's didFinishNavigation function. You can set a counter, make the counter increase by 1 when the function is called, when the counter value is equal to 10, all webviews are fully loaded.

var counter = 0
var globalStart : NSDate?
var globalEnd : NSDate?

And in viewDidLoad.

var start = NSDate()
for i in 0...9
{
    var item = WKWebView()
    item.frame = CGRectMake(0, self.view.bounds.height * CGFloat(i),
        self.view.bounds.width, self.view.bounds.height)
    item.navigationDelegate = self
    self.scrollView.addSubview(item)
    self.webViews.append(item)
}
self.scrollView.contentSize = CGSizeMake(self.view.bounds.width, (self.view.bounds.height - 50.0) * CGFloat(11))
let end = NSDate();
NSLog("creating  webviews  \(end.timeIntervalSinceDate(start))")


dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),{

    self.globalStart = NSDate()

    for item in self.webViews
    {
        var url : NSURL? = NSURL(string:"http://google.com")
        var req = NSURLRequest(URL:url!)
        item.loadRequest(req)
    }
});


func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
    counter++
    println("\(counter) \(webView)")
    if counter == 10 {
        globalEnd = NSDate()
        println(globalEnd!.timeIntervalSinceDate(globalStart!))
    }
}

The result is creating webviews 1.85267299413681, while time of loading all requests is more than 8 seconds for me. I didn't find a way to decrease the time of creating webviews, I think it takes that much of time to create those views.

gabbler
  • 13,626
  • 4
  • 32
  • 44
  • Thanks for the response. I moved creating the WKWebView onto the main thread and that fixed the issue with the webpage not showing up. However the bulk of the work creating a WKWebView is done on the main thread now and it takes 250ms to create a WKWebView, thus if you create 10 WKWebView's the main thread is blocked for 2.5 seconds which isn't acceptable. – TWilly Nov 14 '14 at 15:22
  • Are you sure the time is for creating WKWebView not for loading the request? I think loading request takes more time, since you are loading the request with the same url, try to take the request out and load that specific request for all WKWebViews. – gabbler Nov 14 '14 at 15:39
  • I made a gist of my updated solution. https://gist.github.com/twilly86/8231c483d3cbd315796c Here is the timing of each block of code... - creating webviews 1.83809697628021 - done loading request 0.00213700532913208 - done adding webview to frame 4.0910450220108 Most of the work is being done to create the view and the add the webview to the main view and this all required on the main thread. – TWilly Nov 14 '14 at 20:55
  • Thanks for helping look into this. I beleive the loadRequest call is already async as it doesn't block scrolling or rendering of the page so I'm not sure the above helps. I think for now I'll just need to live with creating the webviews on startup and living with the delay. – TWilly Nov 21 '14 at 16:12
0

Why don't you a webview to the container view immediately after creating it and fire it's request immediately so data starts showing up even while you are still creating the views? All of that in the for loop with corresponding dispatches to the appropriate quque. Just try making the process more async

i-konov
  • 842
  • 1
  • 7
  • 19