Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I cancel an asynchronous call through NSURLConnection sendAsynchronousRequest?

I've got a web service call performing some validation on user input in real time. I'd like to use [NSURLConnection sendAsynchronousRequest] on the validation (which was introduced in iOS 5), but cancel it if the user changes the input field content in the mean time. What is the best way to cancel a current request?

like image 763
Micah Hainline Avatar asked Nov 09 '11 22:11

Micah Hainline


2 Answers

It doesn't appear that there is a good way to do this. The solution seems to be to not use the new [NSURLConnection sendAsynchronousRequest] in situations in which you need to cancel the request.

like image 111
Micah Hainline Avatar answered Nov 10 '22 05:11

Micah Hainline


I've managed to do this by placing the sendAsynchronousRequest method in a separate DownloadWrapper class, as follows:

//
//  DownloadWrapper.h
//
//  Created by Ahmed Khalaf on 16/12/11.
//  Copyright (c) 2011 arkuana. All rights reserved.
//

#import <Foundation/Foundation.h>

@protocol DownloadWrapperDelegate
- (void)receivedData:(NSData *)data;
- (void)emptyReply;
- (void)timedOut;
- (void)downloadError:(NSError *)error;
@end

@interface DownloadWrapper : NSObject {
    id<DownloadWrapperDelegate> delegate;
}
@property(nonatomic, retain) id<DownloadWrapperDelegate> delegate;
- (void)downloadContentsOfURL:(NSString *)urlString;
@end

@implementation DownloadWrapper
@synthesize delegate;

- (void)downloadContentsOfURL:(NSString *)urlString
{
    NSURL *url = [NSURL URLWithString:urlString];

    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:TIMEOUT_INTERVAL];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    [NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
    {
        if ([data length] > 0 && error == nil)
            [delegate receivedData:data];
        else if ([data length] == 0 && error == nil)
            [delegate emptyReply];
        else if (error != nil && error.code == ERROR_CODE_TIMEOUT)
            [delegate timedOut];
        else if (error != nil)
            [delegate downloadError:error];
    }];
}
@end

To utilise this class, I do the following, in addition to declaring the DownloadWrapper *downloadWrapper variable (in the interface declaration) and implementing the protocol methods which handles the response or a lack of one:

NSString *urlString = @"http://yoursite.com/page/to/download.html";
downloadWrapper = [DownloadWrapper alloc];
downloadWrapper.delegate = self;
[downloadWrapper downloadContentsOfURL:urlString];

Then I simply do the following to 'cancel' the connection when the view is about to disappear:

- (void)viewDidUnload
{
    [super viewDidUnload];
    downloadWrapper = nil;
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [downloadWrapper setDelegate:nil];
}

It's as simple as that. This would hopefully mimic the documented cancel method, which states that it does the following:

Once this method is called, the receiver’s delegate will no longer receive any messages for this NSURLConnection.

I was concerned that this (somewhat naive) method means that the packets of data would still come through in response to our URL request - only that we're no longer 'listening in' as the delegate. But then I realised that once the URL request was sent through, there's really no way of stopping the response from coming back to us - we can only disregard it (if not at this level, then still at some lower level in the network hierarchy). Please correct me if I'm wrong.

Either way, hope this helps.

like image 44
pxlshpr Avatar answered Nov 10 '22 05:11

pxlshpr