Asynchronous request running slowly - iOS

1.2k views Asked by At

I have an app which downloads a set of photos from a server. I am using an Asynchronous request because I don't want the UI to be blocked. However, I am finding that the request is very slow and takes ages to load.

I know you can set the queue type to [NSOperationQueue mainQueue] but that just puts the Asynchronous request back on the main thread which defeats the whole point of making the request Asynchronously in the first place.

Is there anyway to speed up the request or to tell iOS: "Run this request in the background, but do it ASAP, don't leave it till the end of the queue"???

Here is my code:

// Set up the photo request.
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:PHOTO_URL, pass_venue_ID, PHOTO_CLIENT_ID, PHOTO_CLIENT_SECRET]];
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // Begin the asynchromous image loading.
    [NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

        if (error == nil) {

            // Convert the response data to JSON.
            NSError *my_error = nil;
            NSDictionary *feed = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&my_error];

            // Check to see if any images exist
            // for this particular place.
            int images_check = [[NSString stringWithFormat:@"%@", [[[feed objectForKey:@"response"] valueForKey:@"photos"] valueForKey:@"count"]] intValue];

            if (images_check > 0) {

                // Download all the image link properties.
                images_prefix = [[[[feed objectForKey:@"response"] valueForKey:@"photos"] valueForKey:@"items"] valueForKey:@"prefix"];
                images_suffix = [[[[feed objectForKey:@"response"] valueForKey:@"photos"] valueForKey:@"items"] valueForKey:@"suffix"];
                images_width = [[[[feed objectForKey:@"response"] valueForKey:@"photos"] valueForKey:@"items"] valueForKey:@"width"];
                images_height = [[[[feed objectForKey:@"response"] valueForKey:@"photos"] valueForKey:@"items"] valueForKey:@"height"];

                // Set the image number label.
                number_label.text = [NSString stringWithFormat:@"1/%lu", (unsigned long)[images_prefix count]];

                // Download up to 5 images.
                images_downloaded = [[NSMutableArray alloc] init];

                // Set the download limit.
                loop_max = 0;

                if ([images_prefix count] > 5) {
                    loop_max = 5;
                }

                else {
                    loop_max = [images_prefix count];
                }

                for (NSUInteger loop = 0; loop < loop_max; loop++) {

                    // Create the image URL.
                    NSString *image_URL = [NSString stringWithFormat:@"%@%@x%@%@", images_prefix[loop], images_width[loop], images_height[loop], images_suffix[loop]];

                    // Download the image file.
                    NSData *image_data = [NSData dataWithContentsOfURL:[NSURL URLWithString:image_URL]];

                    // Store the image data in the array.
                    [images_downloaded addObject:image_data];
                }

                // Load the first image.
                [self load_image:image_num];
            }

            else if (images_check <= 0) {

                // error...
            }
        }

        else {

            // error
        }
    }];

Thanks for your time, Dan.

3

There are 3 answers

1
Fonix On BEST ANSWER

i think your problem isnt the request running slow, its that you are updating UI elements not on the main thread, surround any UI updates (like setting the text on labels) with

dispatch_sync(dispatch_get_main_queue(), ^{
        <#code#>
});
0
Saurav On

As Fonix said its not iOS that responding slow but dataWithContentsOfURL doesn't work in background thread. Apple's recommendation is that you should use NSURLConnection asynchronously with delegates - didReceiveResponse - didReceiveData

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:theURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:_mAuthenticationTimeoutInterval];

In these methods you can make use of chunks of data as well.

If you actually want these multiple downloads to be faster you should use parallel downloading using NSOperationQueue. You can refer enter link description here

1
loretoparisi On

I think a good solution could be using AFNetworking when combined with NSOperation, check this code I wrote to do more than one operation asynchronously

NSMutableArray *operations = [[NSMutableArray alloc] init];
for(NSObject *obj in caches) {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];        
//...set up your mutable request options here

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    operation.responseSerializer = [AFJSONResponseSerializer serializer];
    operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/json"];
    [operation  setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        NSInteger statusCode = operation.response.statusCode;
        if(statusCode==200) {
        }

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"API Call error:%@", error.localizedDescription);
    }];

    [[requestManager operationQueue] addOperation:operation];
    [operations addObject:operation];

    if([operations count] >= MAX_API_CALL) break;
}

[AFHTTPRequestOperation batchOfRequestOperations:operations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
} completionBlock:^(NSArray *operations) {

    NSError *error;
    for (AFHTTPRequestOperation *op in operations) {
        if (op.isCancelled){
        }
        if (op.responseObject){
            // process your responce here
        }
        if (op.error){
            error = op.error;
        }
    }
}];