Search code examples
iosios9nsurlsessionrubymotion

NSURLSession dataTaskWithURL very "slow"


I mean by "slow", that the callback kind of wait for the remote server to timeout to effectively fire (calling a vimeo feed, parsing it, and then displaying uiviews on the scene)

I mostly don't get how it works. I'd like my view to be populated from within the callback as soon as the response is returned

Having the code below (rubymotion, but you might get the idea):

session = NSURLSession.sharedSession
url = NSURL.URLWithString(ALBUMS_URL)
downloadTask = session.dataTaskWithURL( url, completionHandler: lambda { |data, response, error|
    # 'puts' prints the result in the console, you get it as soon as the response arrives
    puts data
    # testing with a simple view 
    v = UIView.alloc.initWithFrame(CGRectMake(0,0,@width/2,200))
    v.backgroundColor = UIColor.blackColor
    self.view.addSubview v # ==> takes forever to effectively appear on the scene
})

I ended up setting it in the main thread with the following

NSURLSession.sessionWithConfiguration(
    NSURLSessionConfiguration.defaultSessionConfiguration,
    delegate:nil,
    delegateQueue: NSOperationQueue.mainQueue
)

Something else should be used to do this kind of tasks? Is there a way to 'force' the view to be updated?


Solution

  • The reason your UI is taking so long to update is not because the operation took so long, but it's because the NSURLSessionDataTask finishes in a background thread. As you may know, you are not supposed to be making UI changes from a background thread, only the main thread.

    Your solution to put the entire URL session's callback in the main queue "solves" this problem, but not the correct way, since you are now doing network operation in the main queue, which should be little as possible for things that can be done in the background (like network operations).

    To fix this, you need to do the network operations in the background thread, and then just finally call the UI-changing logic in the main thread. You can do a simple dispatch_async() call to make a block that updates the UI, like this (sorry, I'm not familiar with RubyMotion, so I'm writing this in Objective-C):

    // in the callback for NSURLSessionDataTask:
    NSLog(@"%@", data);
    
    // Dispatch the UI-related logic as a block on the main-thread
    dispatch_async(dispatch_get_main_queue(), ^{
        UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0,0,width/2,200)];
        v.backgroundColor = [UIColor blackColor];
        [self.view addSubview:v];
    });