NSURLConnection Download multiple images

I am trying to download multiple images from a URL stored in an XML feed. Getting the image urls from the XML is working correctly. However, the NSURLConnection is creating empty files, but the data is received as noted in NSLog. In connectionDidFinishLoading:(NSURLConnection *)connection, the data and correct bytes are received, the problem is how do I make the receivedData write to the correct file.

Semi-working code:

-(void)parsingComplete:(XMLDataSource*)theParser 
{
    /*  iterate through the Categories and create the 
        sub-directory if it does not exist  
     */
    for (int i = 0; i < [categories count]; i++) {
        NSString *cat      = [NSString stringWithFormat:@"%@/%@",BASE_DIR,[[categories objectAtIndex:i] objectForKey:@"name"]];
        NSString *catName  = [[categories objectAtIndex:i] objectForKey:@"name"];
        NSArray  *catArray = [[categories objectAtIndex:i] objectForKey:@"images"];

        /*  create the sub-direcotry naming it the #category# key  */
        if (![FILEMANAGER fileExistsAtPath:cat]) {
            [FILEMANAGER createDirectoryAtPath:cat withIntermediateDirectories:NO attributes:nil error:nil];
        }

        //NSLog(@"\n\nCategory: %@",cat);
        for (int x = 0; x < [catArray count]; x++) {
            //NSLog(@"Image: %@",[[catArray objectAtIndex:x] objectForKey:@"imageUrl"]);   
            /*  download each file to the corresponding category sub-directory  */
            fileOut = [NSString stringWithFormat:@"%@/%@_0%i.jpg",cat,catName,x];

            NSURLRequest *imageRequest = 
            [NSURLRequest requestWithURL:[NSURL URLWithString:[[catArray objectAtIndex:x] objectForKey:@"imageUrl"]]
                             cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0];
            NSURLConnection *imageConnection = [[NSURLConnection alloc] initWithRequest:imageRequest delegate:self];

            int counter = 0;
            //BOOL result = NO;
            if(imageConnection)
            {
                NSLog(@"Counter: %i",counter++);
                receivedData = [[NSMutableData data] retain];
                /*result = */[receivedData writeToFile:fileOut atomically:YES];
            }
            /*
                if (!result) NSLog(@"Failed"); else NSLog(@"Successful");
             */
        }
    }
}

#pragma mark NSURLConenction

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response  {  
    [receivedData setLength:0];  
} 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data  {  
    [receivedData appendData:data];
}  
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    // release the connection, and the data object
    [connection release];
    // receivedData is declared as a method instance elsewhere
    [receivedData release];
    // inform the user
    NSLog(@"Connection failed! Error - %@ %@",
          [error localizedDescription],
          [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection  
{  
    // do something with the data  
    // receivedData is declared as a method instance elsewhere  
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);  
    NSString *aStr = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];  
    // release the connection, and the data object  
    //[receivedData release];  
} 

  • Converting NSDictionary to XML
  • Can't search music using iTunes Scripting Bridge and Sandboxing
  • Objective-C Library for RSS feed parsing?
  • Choosing the right IOS XML parser
  • How to generate all of the numbers of pi in Objective-C
  • Extracting an href's text from an html document
  • 2 Solutions Collect From Internet About “NSURLConnection Download multiple images”

    You have to wait until the connection tells you it has finished before you can write the data. The connection is handled on another thread; if you try to access the data immediately on the original thread as you’re doing, there won’t be anything in it.

    You should move the writeToFile: call to the end of connectionDidFinishLoading:, or to another method that you call from there. That’s the first point where you know that the data has all been collected.

    I’d also suggest creating the NSMutableData instance in didRecieveResponse:, so that you know that it is available at the correct time. That will be more readable/understandable. You can think of the delegate methods as a collective “scope” — the data is only used inside of them, so it should be created inside of one of them.

    In reply to your comment:

    One possibility, since you have so much that needs to be done around this one download, and don’t seem to be touching the GUI, is to run the whole parsingComplete: method on a background thread, and using +[NSURLConnection sendSynchronousRequest:returningResponse:error:]. This way your code will just wait until the data comes back, in one piece, and you can write it immediately after the sendSynchronous... call returns.

    NSError * err;
    NSURLResponse * response;
    NSData * receivedData = [NSURLConnection sendSynchronousRequest:imageRequest
                                                  returningResponse:&response
                                                              error:&err];
    if( !receivedData ){
        /* Handle error */
    }
    /* Check response */
    
    BOOL result = [receivedData writeToFile:fileOut atomically:YES];
    /* check result, etc. */
    

    You can use a CustomURLConnection with Tag to name de images before they download.

    With this code you can make a customURLConnection, name it when you make the request, and ask for the name of the image in the connectionDidFinishLoading:

    CustomURLConnection.h

    #import <Foundation/Foundation.h>
    
    @interface CustomURLConnection : NSURLConnection 
    {
    NSString *tag;
    }
    
    @property (nonatomic, retain) NSString *tag;
    
    - (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)aTag;
    
    @end
    

    CustomURLConnection.m

    #import "CustomURLConnection.h"
    
    @implementation CustomURLConnection
    
    @synthesize tag;
    
    - (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)aTag 
    {
    self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
    
        if (self) {
            self.tag = aTag;
        }
        return self;
    }
    
    - (void)dealloc 
    {
        [tag release];
        [super dealloc];
    }
    
    @end
    

    Then make the connection, a custom url connection in your parsingComplete with:

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:yourURL];
    [request setTimeoutInterval:3000.0];
    
    CustomURLConnection *connection = [[CustomURLConnection alloc]     initWithRequest:request delegate:self startImmediately:YES tag:imageTag];
    

    Now you can take the imageName with the CustomURLConnection tag, and save it in the connectionDidFinishLoading:

    CustomURLConnection *urlConec = (CustomURLConnection*)connection;
    
    NSMutableData *dataFromConnection = [self dataForConnection:urlConec];
    

    and this is the code for the function dataForConnection:

    - (NSMutableData*)dataForConnection:(CustomURLConnection*)connection 
    {
        NSMutableData *data = [receivedData objectForKey:connection.tag];
        return data;
    }
    

    Hope that helps.