connectionDidFinishLoading calls before image has downloaded?

416 views Asked by At

I am trying to retrieve a Facebook profile picture, however I am having trouble being able to check when the image has been downloaded?

First I create a variable.

@property (strong, nonatomic) NSMutableData *imageData;

Than I start the connection.

-(void)getUserPicture {

//Grab user profile picture
imageData = [[NSMutableData alloc] init]; // the image will be loaded in here
NSString *urlString = [NSString stringWithFormat:@"http://graph.facebook.com/%@/picture?type=large", userId];
NSMutableURLRequest *urlRequest =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];

NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest
                                                                 delegate:self];
    if (!urlConnection) NSLog(@"Failed to download picture");


}

After that I try to check when it is done so I can upload the file to my backend, however my problem is connectionDidFinishLoading calls almost instantly before the image has downloaded.

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    imageData = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [imageData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    userPicture = [UIImage imageWithData:imageData];

        NSLog(@"%@",userPicture); //this returns null :(

    }

The weird thing is if I call this method twice, the NSLog doesn't return null, it actually returns the photo. So why is connectionDidFinishedLoading calling before the image has downloaded from Facebook?

2

There are 2 answers

0
Rob On BEST ANSWER

The problem is almost certainly neither NSURLConnection nor the Facebook API, but rather how you're calling it. But, your question doesn't include enough information for us to diagnose it.

So, first, expand your methods to include more diagnostic information, for example:

// check the response header when we receive the response

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    imageData = [NSMutableData data];

    // if `statusCode` is not 200, show us what it was ...

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        int statusCode = [(NSHTTPURLResponse *)response statusCode];
        if (statusCode != 200) {
            NSLog(@"Status code was %ld, but should be 200.", (long)statusCode);
            NSLog(@"response = %@", response);
        }
    }
}

// make sure to detect and report any errors

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"didFailWithError: %@", error);
}

// when you're done, if we fail to create `UIImage`, then it obviously
// wasn't an image, so let's see what it was.

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    userPicture = [UIImage imageWithData:imageData];

    // if not an image, then ...

    if (!userPicture) {
        NSString *responseString = [[NSString alloc] initWithData:imageData encoding:NSUTF8StringEncoding];
        if (responseString) {
            // if it was a string, show it to us ...

            NSLog(@"did not receive image: responseString = %@", responseString);
        } else {
            // if neither a string nor image data, then what was it ...

            NSLog(@"did not receive image: imageData = %@", imageData);
        }
    }

    // By the way, I'd expect to see you do something here to update the UI with the image;
    // all of these delegate methods were called asynchronously, so you have
    // to do something here that triggers the update of the UI, e.g.
    //
    // self.imageView.image = userPicture
}

By the way, I typed the above without the benefit of Xcode's syntax checking and the like, so don't be surprised if there are some errors there. But worry less about the actual code and focus on the the three diagnostic pillars this illustrates: 1. Look at the response headers and make sure they're ok, not reporting some non-200 status code; 2. Implement delegate that will report networking errors; and 3. If image conversion failed, then you obviously didn't receive an image, so stop and figure out what you actually received. (Often if the server had trouble fulfilling your request, the response is actually HTML or something like that which tells you why it had problems. If you don't look at it, you're flying blind.)

Second, you can watch the network connection by using Charles (or something like that). Run the app on the simulator and then watch the network connection as the app runs.

Third, if you're still having problems, create a MCVE. Namely, we don't want to see all of your code, but you should instead create the simplest possible example that manifests the problem you describe. Don't ask us to pour through tons of code, but rather make it as absolutely bare-bones as possible.

1
Tyler McKay On

So I'm not sure why connectionDidFinishLoading is getting called instantly after you set the connection, but I may be able to help you work around the issue.

Try this:

-(UIImage *) getImageFromURL:(NSString *)fileURL {

    UIImage * result;

    NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
    result = [UIImage imageWithData:data];

    return result;
}

Where fileURL is the a string with the url.

If you want to perform an action after the request is sent try this instead:

-(UIImage *) getImageFromURL:(NSString *)fileURL {

    UIImage * result;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

        NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
        result = [UIImage imageWithData:data];

        return result;

        dispatch_async(dispatch_get_main_queue(), ^{
            //code for operation after download
        });

    });
}

Let me know how it goes