2

I'm trying to download an image from a website and save it as a UIImage but if the user has low connection this can take forever... how can I download it in the background so the user can keep using the app in the meantime?

here is the code:

theIcon.image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://myWebsite.com/Icon.png"]]];
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
  • 1
    http://stackoverflow.com/questions/7502073/loading-images-from-a-background-thread-using-blocks – Vjy Jul 17 '13 at 19:39
  • @Vjy Which answer do you refer to? – CouchDeveloper Jul 17 '13 at 21:45
  • `dataWithContentsOfURL:` will not work reliable (if at all) for accessing remote resources. You should use either the asynchronous convenient method from class `NSURLConnection` or use it in asynchronous style and implement the delegates. – CouchDeveloper Jul 17 '13 at 21:49
  • @CouchDeveloper What's wrong with dataWithContentsOfURL? It's worked fine on all my apps in the past, I'd hate to have to go update them all haha! /: – Albert Renshaw Jul 17 '13 at 22:02
  • It's for use with _file_ URLs. That it works for remote URLs may be by accident, bad luck, or by mistake. It's unreliable and not appropriate for remote resources. Be careful! – CouchDeveloper Jul 17 '13 at 22:15

4 Answers4

1

Use GCD.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    // do my background code
    dispatch_async(dispatch_get_main_queue(), ^{
        // do handling on main thread when done!
    });
});
Nicholas Hart
  • 1,734
  • 13
  • 22
  • This worked amazingly for the image, but when I tried to do it with text the app crashed... Here is the code I used (It works in the main thread, just not in your GCD code area) `advertisementTitle.text = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.myWebsite.com/OneParagraph.html"] encoding:NSUTF8StringEncoding error:nil];` – Albert Renshaw Jul 17 '13 at 20:11
  • This was the error log: `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. Crashing now...` – Albert Renshaw Jul 17 '13 at 20:12
  • (*Note: I'm using ARC) – Albert Renshaw Jul 17 '13 at 20:16
  • It worked when I stored *NSStrings in the "background code" region and then in the "main thread when done" region I set the IBOutlet UI stuff equal to those NSStrings! God bless :) – Albert Renshaw Jul 17 '13 at 20:23
  • @Nicholas Hart The question deserves more details. This answer is simply too vague, and naively applying this idiom to the OPs problem would produce incorrect, or at least suboptimal code. – CouchDeveloper Jul 17 '13 at 21:51
  • The original code was a vaguely worded "how do I run code in the background." It has since been edited to be specific to loading images. But thanks for the downvote. – Nicholas Hart Jul 17 '13 at 21:58
  • @Albert: you can only call UIKit methods from the main thread. – Nicholas Hart Jul 17 '13 at 22:12
  • @Nicholas Hart, I feel bad -- really. But you can edit your answer, too. I'm sure you get upvotes when you show how to use `NSURLConnection` in asynchronous mode and demonstrate the implementation of the principal delegates. – CouchDeveloper Jul 17 '13 at 22:26
  • LOL, it's ok. Cheers! – Nicholas Hart Jul 17 '13 at 23:46
1

Use AFNetworking.

[imageView setImageWithURL:
                       [NSURL URLWithString:@"http://i.imgur.com/r4uwx.jpg"] 
                       placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]];
Samet DEDE
  • 1,621
  • 19
  • 28
0

You can perform the selector in background thread while the main foreground thread runs.

 [self performSelectorInBackground:@selector(downloadFile) withObject:nil];

 - (void) downloadFile {
   //download file 
   //you can show UIAlertView when done
    }

In your - (void) downloadFile you can download this big file. and have an activity indicator show (or not). You can have the activity Indicator become not hidden or hidden and have it startAnimating and stopAnimating will make it spin and stop. This can be referenced from the foreground and background processes.

apollosoftware.org
  • 12,161
  • 4
  • 48
  • 69
0

The quick and dirty way:

NSMutableRequest* request = ... ;
[NSURLConnection sendAsynchronousRequest:request 
                                   queue:[NSOperationQueue mainQueue] 
                       completionHandler:^(NSURLResponse* response, NSData* data, NSError* error) {
    if (!error) {
        // do something with the response data.    
    }
}];

This approach is sufficient for "a prove of concept", toy programs with simplistic insecure connections, Apple samples, and for hobbyists learning iOS for fun, and for samples which demonstrate anti-patterns ("How you should do it, not!").

If you want a solid approach you need to use NSURLConnection in asynchronous mode and implement the delegates - or use a third party library. ;)

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67