0
votes

I got confused while working with NSURLConnection and NSRunLoop. I’m trying to download a large file using NSURLConnection but it’s NOT working (Not even calling a single delegate method) as expected.

    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1:8080/"];
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url] autorelease];
    [request setHTTPBody:[@"Request Body Data" dataUsingEncoding:NSUTF8StringEncoding]];
    [request setHTTPMethod:@"POST"];

Running on main Thread.

Only working for small size of file. It didn’t work when I try to download an 18MB file.

  1. It didn't work for large image files.

    [NSURLConnection connectionWithRequest:request delegate:self];
    
  2. It didn't work for large image file, tried with 18MB.

    NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [con setDelegateQueue:[NSOperationQueue currentQueue]];
    [con start];
    
  3. It didn't work for large image file, tried with 18MB.

    NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [con scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; // Or NSRunLoopCommonModes
    [con start];
    
  4. It didn't work for large image file, tried with 18MB.

    NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    NSRunLoop *loop = [NSRunLoop currentRunLoop];
    [con scheduleInRunLoop:loop forMode:NSRunLoopCommonModes];
    [con start];
    [loop run];
    
  5. It didn't work for large image file, tried with 18MB.

    NSHTTPURLResponse *res = nil;
    NSError *err = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&res error:&err];
    NSLog(@"Res Code: %d, DataLen: %d", res.statusCode, data.length);
    
  6. It WORKED for large image as well

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        NSHTTPURLResponse *res = (NSHTTPURLResponse*)response;
        NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"/myImage.jpg"];
        NSLog(@"Res Code: %d, DataLen: %d, Path: %@", res.statusCode, data.length, imagePath);
        [data writeToFile:imagePath atomically:YES];
    }];
    

Running on NSOperation (I have attempted same above code in NSOperation's subclass, I have uploaded the code on github. Please find the link below.)

  1. Nothing worked (None of the delegate method get called.)
  2. It WORKED for all.
  3. It didn’t work for large file.
  4. It WORKED for all. (It worked for NSDefaultRunLoopMode mode as well, where apple document says this “The mode to deal with input sources other than NSConnection objects.”).
  5. It WORKED for all.
  6. It WORKED for all.

I just want to understand the basic logic behind NSRunLoop, when we use it with NSURLConnection object. How NSRunLoop works behind the scene? How does it work with NSURLConnections? What happens when we call any asynchronous request through NSURLConnection on main thread or any secondary thread (created by NSOperation)?

Sample Code on GitHub

I have read several blogs and apple documents related to NSRunLoop but still confused, so i have written a doc over my understanding about the same. NSRunLoop understanding Doc.

Many thanks in advance!

1
You need to say what results you got, not just "it didn't work". There's no reason you shouldn't be able to use the #1 method to download a 20 MB file. There's no particular reason to mess around with run loops either.rdelmar
It didn't work means it's not even calling delegate methods.Zeeshan
If it's not calling the delegate methods, then you set something up incorrectly (without seeing your code, it's impossible to tell what's wrong). That has nothing to do with run loops or the size of the file you're trying to download.rdelmar
The code is there sir, on github. I have not added that 20 mb file in project. You can any 20 mb or large image there.Zeeshan
That code has all kinds of errors when I try to compile it, so it's not much use. If I copy just the app delegate code, and delete the stuff having to do with not using ARC and all the proxy server stuff, your method #1 works fine to download a 20MB file. I don't know what you're doing with the ZKProxyServer, but that could be the source of the problem.rdelmar

1 Answers

0
votes

Confusion is good, it's a signal that you are missing answers and probably even questions. As far as I can see, the missing question here is: do we really need NSOperation and NSRunLoop? NSURLConnection sendAsynchronousRequest: and sendSynchronousRequest: already schedule operations in runloops -- adding NSOperation and NSRunLoop doesn't help and probably hinders. So, concentrate on the methods that you really need (sendAsynchronousRequest: or sendSynchronousRequest:) and make full use of them. Take this code:

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSHTTPURLResponse *res = (NSHTTPURLResponse*)response;
    NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"/myImage.jpg"];
    NSLog(@"Res Code: %d, DataLen: %d, Path: %@", res.statusCode, data.length, imagePath);
    [data writeToFile:imagePath atomically:YES];
}];

You do cast NSURLResponse to NSHTTPURLResponse, presumably to read out the status code, but instead you forge ahead and write the data object to file anyway. If the status code is anything but 200, there is no valid data object to write to file. The status code may be 206, in which case you will need to append the incoming data to an already existing file, or create one if it doesn't exist and store a reference to it, etc.