skip to Main Content

Using Facebook Graph API, I retrieved a string URL to a 200x200 profile picture that I want to display in a UIImageView. I’m successfully able to do this, but I notice that it can take as long as 10 seconds for the image to display on the screen. Can anyone give me some pointers (no pun intended) on how to optimize it?

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: self.profilePictureUrl)!, completionHandler: { (data, response, error) ->
        Void in
        self.profilePictureImageView.image = UIImage(data: data!)
        self.profilePictureImageView.layer.cornerRadius = self.profilePictureImageView.frame.size.width / 2;
        self.profilePictureImageView.clipsToBounds      = true

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.view.addSubview(self.profilePictureImageView)
        })

    }).resume()
}

2

Answers


  1. You should move all UIView calls (so anything you set on the UIImageView) onto the main thread as UIKit for the most part isn’t thread-safe. You can instantiate the UIImage on the background thread though for performance optimization, so try this:

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
    
        let url = NSURL(string: self.profilePictureUrl)!
    
        NSURLSession.sharedSession().dataTaskWithURL(
            url,
            completionHandler: { [weak self] (data, response, error) -> Void in
                guard let strongSelf = self else { return }
    
                // create the UIImage on the background thread
                let image = UIImage(data: data!)
    
                // then jump to the main thread to modify your UIImageView
                dispatch_async(dispatch_get_main_queue(), { [weak self] () -> Void in
                    guard let strongSelf = self else { return }
    
                    let profilePictureImageView = strongSelf.profilePictureImageView
    
                    profilePictureImageView.image = image
                    profilePictureImageView.layer.cornerRadius = profilePictureImageView.frame.size.width / 2;
                    profilePictureImageView.clipsToBounds = true
    
                    strongSelf.view.addSubview(profilePictureImageView)
                })
            }
        ).resume()
    }
    

    Note also that I’ve weak-ified your references to self. There is no guarantee the user hasn’t dismissed the view controller that is initiating this code by the time the completion routines get called so you want to make sure you’re not keeping a strong reference to self. This allows the view controller to deallocate if the user dismisses it and the completion routines then return early without doing any unnecessary work.

    Login or Signup to reply.
  2. This code is illegal:

    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: self.profilePictureUrl)!, completionHandler: { (data, response, error) ->
        Void in
        self.profilePictureImageView.image = UIImage(data: data!)
    

    Stop! you are setting a UIImageView’s image on a background thread. No no no. UIKit is not thread-safe. You must get onto the main thread to do this. (You do eventually get onto the main thread in your code, but you are doing it too late.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search