Oleg
Oleg

Reputation: 3014

iOS - multiple NSURLConnection downloads

I'm trying to download some files using NSURLConnection:

- (void)startDownload {
  NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
  [req addValue:@"Basic XXXXXXXXXXXXXX=" forHTTPHeaderField:@"Authorization"];
  connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response {
  filepath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:save_name];
  [[NSFileManager defaultManager] createFileAtPath:filepath contents:nil attributes:nil];
  file = [[NSFileHandle fileHandleForUpdatingAtPath:filepath] retain];
  [file seekToEndOfFile];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
  [file seekToEndOfFile];
  [file writeData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
  [file closeFile];
 }

My files are quite big, so I have to save every piece I download to a disk, can't store them in RAM. The question is, how can I handle multiple downloads?

I was thinking to start the next download after the previous one is finished in - (void)connectionDidFinishLoading:, but I guess it is not the best way to do it. Any suggestions?

Upvotes: 1

Views: 2000

Answers (3)

Bartu
Bartu

Reputation: 2210

this is what I come up with:

  • Create a mutable array named downloadQueue
  • Check if your download connection is running at the moment and queue the connection if so
  • When your connection is ended, check the queue, if queue has members, run the queue

Implementation: (Im going to mark the important parts of it, please check each step for additional controls)

as private members;

...
@property (nonatomic, retain) NSURLConnection *downloadConnection;
@property (nonatomic, retain) NSMutableArray *downloadQueue;
...

modify your functions such as;

- (void)startDownload {
  NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
  [req addValue:@"Basic XXXXXXXXXXXXXX=" forHTTPHeaderField:@"Authorization"];

  if (self.downloadConnection)
  {
       if (!self.downloadQueue) self.downloadQueue = [[NSMutableArray alloc] init];
       [self.downloadQueue addObject:request];
       return;
  }
  // else just run your connection
  self.downloadConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}

    - (void)connectionDidFinishLoading:(NSURLConnection*)connection {
  [file closeFile];
  // nil your connection
  self.downloadConnection = nil;
  // check the state of your queue
  [self checkQueue];
 }


 -(void)checkQueue
{
    if (self.downloadQueue)
    {
          if (self.downloadQueue.count > 0)
          {
                NSURLRequest *queueLastObject = [self.downloadQueue lastObject];
                // call the request with downloadConnection and remove it from the queue...
          }
    }
}

NOTE: This code was only meant to specify the idea of implemetation and should be edited in order to be used. If you have any issues at any points, just let me know and I will update the code.

Upvotes: 1

deanWombourne
deanWombourne

Reputation: 38475

If you want to use NSURLConnection I would recommend making your own download object that wraps the connection and deals with writing it to disk.

You would initialise your object with a filename to put the finished file in and the URL to get the file from, something like :

MyDownloadObject *o = [[MyDownloadObject alloc] initWithFilename:filename URL:url];
o.delegate = self;

Each download object would deal with it's own NSURLRequest and NSURLConnection, using pretty much the same code as in your question :)

When it's finished it would tell your view controller that it's downloaded (probably via a delegate method but a notification would work just as well). You can then tell from the delegate method which file has finished.

- (void)myDownloadObjectFinishedDownload:(MyDownloadObject *)o {
    NSLog(@"File from %@ has been stored at %@", o.URL, o.filename);
}

If you made your MyDownloadObject a subclass of NSOperation then you could use an NSOperationQueue to limit the concurrent downloads and monitor overall progress etc.


If you didn't mind using a third party library then ASIHTTPRequest is a good choice but be warned, it's not being developed anymore. I suspect that other people on stack overflow will recommend better, more up to date, libraries you could use instead.

Upvotes: 2

Amy Worrall
Amy Worrall

Reputation: 16337

For multiple downloads, use multiple NSURLConnections.

Note that in each delegate method you get passed the connection to which it refers. You just need to store all the connections you create (as properties, or in an array, or whatever, depends on your design). Then when implementing connection: didReceiveData:, you can check which connection has received some data by comparing (using ==) the parameter with each of your stored connections, and you can act on the data accordingly.

Upvotes: 2

Related Questions