Archive for the 'Asynchronous' Category

27 MayGrand Central Dispatch magic – async image demo

The Grand Central Dispatch (GCD) is a big topic. The first thing I wanted to try when I started to learn it a few months ago is set of asynchronous operations like image downloads. Check out my old sample how to get Asynchronous UIImage from web or this one if you need to download a really big file in the background on the app launch.

Current sample is for situation when you need to download a set of files, so you must have a queue. The magic of GCD is that it manages queues automatically depending of available resources and their priorities. GCD does that efficiently because of low level implementation and Bloc

Demo

  • has a predefined set of urls to download
  • creates a GCD queue and starts to download images
  • shows each image as soon as it’s available with appearing–dissappearing animation

Implementation

Тhe demo result is nice Category UIImageView+DispatchLoad:

Note, this is just a sample how GCD works with queues and asynchronous operations. Don’t use it in production because it’s usually need to cancel and re-prioritize network downloads. GCD approach is nice for local operations like saving a big file to disk. GCD lets us to keep UI smooth while heavy operations – that’s the main point.

21 JanBackground asynchronous downloads or other jobs

In current development I need to synchronize tons of data with a server. The big “Synchronize” button in the settings or somewhere else isn’t a good solution, because user starts the application to use, but wait any system jobs. So the background synchronization is more better. I found the easiest way to do it and want to share a code snippet with you.


Run method in backgorund right from UIApplicationDelegate:

- (void) startBackgroundSynch{ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
BackgroundSynch *synch = [[[BackgroundSynch alloc] init] autorelease]; [synch startDownloading];
 [pool release];} - (void)applicationDidFinishLaunching:(UIApplication *)application 
{ [self performSelectorInBackground:@selector(startBackgroundSynch) withObject:nil];
 // Override point for customization after app launch
    [window addSubview:viewController.view];    [window makeKeyAndVisible];}

The BackgroundSynch must create NSURLConnection and receives NSURLResponse(s). But the problem you’d face with is NSURLConnection’s events (didReceiveResponse, didReceiveData, connectionDidFinishLoading) are never called. And that’s a small trick.

@interface BackgroundSynch : NSObject{ NSMutableData *responseData; BOOL finished;}
 - (void) startDownloading;@end
#import "BackgroundSynch.h" @implementation BackgroundSynch 
- (void) startDownloadingWithUrl:(NSString*)urlString{ NSLog(urlString); NSURL *url
= [NSURL URLWithString:urlString]; NSMutableURLRequest *request =  [NSMutableURLRequest
requestWithURL:url                cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30.0][request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]} -
(void) startDownloading{ [UIApplication sharedApplication].networkActivityIndicatorVisible =
 YES; NSString *urlString = @"http://www.archive.org/download/BettyBoopCartoons/Betty_Boop_More_
Pep_1936_512kb.mp4"; [self startDownloadingWithUrl:urlString]// THAT'S A TRICK >> finished =
 NO; while(!finished) {  [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:
[NSDate distantFuture]]; }} ////// RECEIVING DATE FROM SERVER/// - (void) connection:(NSURLConnection
 *)connection didReceiveResponse:(NSURLResponse *)response{ responseData = [[NSMutableData alloc] init];}
 - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)aData{ [responseData
appendData:aData]; NSLog(@"DATA: %d", [responseData length]);} - (void) connectionDidFinishLoading:
 (NSURLConnection*) connection { [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 [responseData release], responseData = nil; finished = YES;} -(void) connection:(NSURLConnection *)
connection didFailWithError:(NSError *)error { [responseData release], responseData = nil;
 [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; finished = YES;} @end

Hope you were careful enough to find an while-loop depending at the end of “startDownloading”. It depends on boolean variable which tells if data is downloading or downloaded(failed).

Now user can normally work in GUI and data automatically synchronizes on background.

22 OctAsynchronous UIImage from web

Everybody needed or will need to load images from web. The easiest way to do it is to get NSData from NSURL and create UIImage:

UIImage *myImage = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:[NSURL URLWithString:strImageUrl]]];

But it’d look strange for customers to hover while image loading from server.

Asynchronous loading is a de-facto standard. Let’s make an elegant solution for future need.

Usually we show UIImage with UIImageView on views, so there is a reason to inherit UIImageView. The task is easy: download NSData in background and show it. We need only one public method which actually starts background working:

@interface AsynchronousImageView : UIImageView {
NSURLConnection *connection;    NSMutableData *data;}                           

- (void)loadImageFromURLString:(NSString *)theUrlString;

@end

Asynchronous download from web requires the use of a NSURLRequest and NSURLConnection. Don’t forget about caching and select necessary NSURLRequest’s cachePolicy:

- (void)loadImageFromURLString
 :( NSString *)theUrlString{ [self.image release],
self.image = nil; NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:theUrlString]
cachePolicy:NSURLRequestReturnCacheDataElseLoad
 timeoutInterval:30.0];                          

 connection = [[NSURLConnection alloc]
 initWithRequest:request delegate:self];}

Then we need to process data receiving:

- (void)connection:(NSURLConnection *)theConnection
 didReceiveData:(NSData *)incrementalData {    if (data == nil)
 data = [[NSMutableData alloc] initWithCapacity:2048];                 

    [data appendData:incrementalData];}

- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
{    self.image = [UIImage imageWithData:data];
  [data release], data = nil; [connection release],
connection = nil;}

To apply brand new AsynchronousImageView we need just add it

to a UIVIew. Open InterfaceBuilder,

add UIImageView to the view and change its class to AsynchronousImageView:

Now create and connect an IBOutlet with AsynchronousImageView and call loading a test image.

IBOutlet AsynchronousImageView *asynchronousImageView;
[asynchronousImageView
loadImageFromURLString:@"http://www.iphone-codes.com/wp-content/uploads/2008/03
/apple-iphone-sdk-beta-2.jpg"];

If you’ve made all right it’d download and show your image.

Look sample sources or directly git “git clone git://github.com/slatvick/Alterplay-iPhone-dev-tips.git“.

PS.
It’s not depend of where you want to show web image. It can be a Multiline UITableViewCellor UITableView header as I needed in one of my project:

AsynchronousImageView *imageView = [[AsynchronousImageView alloc] init];
 [imageView loadImageFromURLString: @"..." ];
 self.tableView.tableHeaderView = imageView;