views:

1018

answers:

2

Hello all,

I am trying to use NSURLConnection in asynchonous mode and wait for completion. During the wait, I use NSRUNLOOP to handle events. Its works in most cases (3G and WIFI) but the application hangup randomly in GSM DATA and EDGE environnement.

Anyone can help me ?

My code:

(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
_requestCompleted = TRUE;
CFRunLoopStop(CFRunLoopGetCurrent());
}

(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{

_response = (NSHTTPURLResponse *)response;

[ _response retain];

}
(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
/* Append the new data to the received data. */
[_receivedData appendData:data];
}


(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
_requestError = error;
NSLog(@"request error - cause : %@", [_requestError localizedDescription ]);
_requestCompleted = TRUE;
[_requestError retain];
CFRunLoopStop(CFRunLoopGetCurrent());
}

(NSString*)downloadFileFromURL:(NSString*)url
{       
NSURLRequest *request = nil;

NSString *contents = nil;
NSData *response = nil;
int tries;
NSURLConnection *URLConnection = nil;
int i = 0;
NSDate* dateLimit;

[_mutex lock];
NSLog(@"url = %@", url);
NSString *requestWithEscaping = [ url    stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding ];


request = [NSURLRequest requestWithURL:[NSURL URLWithString:requestWithEscaping] 
                        cachePolicy: NSURLRequestReloadIgnoringLocalCacheData 
                        timeoutInterval:30];


for (tries = 0; tries < HTTP_REQUESTS_TRIES && response == nil ; tries ++) {
    [_receivedData setLength:0];
    _requestCompleted = FALSE;
    _requestError = nil;
    _requestTimeout = FALSE;

    if (URLConnection == nil)
        URLConnection = [ NSURLConnection connectionWithRequest:request delegate:self];
    else {
        [URLConnection cancel ];
        [URLConnection start];
    }

    if (URLConnection == nil) {
        NSLog(@"No connection ! maybe mistake in Request Format ?");
        continue;
    }

    for (i = 0; ; i++) {
        dateLimit = [[NSDate date] addTimeInterval:INTERVALL_LOOP];

        [ [NSRunLoop currentRunLoop] runUntilDate:dateLimit];

        if (_requestCompleted == TRUE)
            break;

        if (i > (int)(HTTP_TIMEOUT_FIRE * (1 / INTERVALL_LOOP))) {
            _requestTimeout = TRUE;
            break;
        }
    }

    if (_requestTimeout) {
        [ URLConnection cancel];
        NSLog(@"request timeout");
        continue;
    }
    if (_requestError) {
        NSLog(@"request error");
        continue;
    }

    NSLog(@"Request success");
    break;
}

if (_requestTimeout) {  
    _lastErrorType = ServicesRequestManager_error_Network;
    [ _lastErrorString setString:@"Délai de connexion depassé"];
    [ _mutex unlock];
    return nil;
}

if (_requestError) {    
    NSLog(@"ERROR Cannot get %@ - cause : %@",url, [ _requestError localizedDescription ]);
    _lastErrorType = ServicesRequestManager_error_Network;
    [_lastErrorString setString:@"Impossible de se connecter" ];
    [ _mutex unlock];
    return nil;
}

if ([ _response statusCode] != 200 ) {
    NSLog(@"ERROR Download file %@ HTTP response - code : %d", url, [ _response statusCode]);
    [_lastErrorString setString:[ NSString stringWithFormat:@"Error http code : %d", [_response statusCode]]];
    _lastErrorType = ServicesRequestManager_error_Service;
    [_lastErrorString setString:@"Le service n'est pas disponible actuellement" ];
    [ _mutex unlock];
    return nil;
}

contents = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding]; 
[ _mutex unlock];
return contents;

}

+2  A: 

NSURLConnection is already asynchronous. Simply implement the delegate methods. If you want to update the UI on the MainThread (e.g. a progress bar), you can do so in didReceiveData.

Felix
Ok but I want to wait for completion. I need to handle timeout too.
mneveren
If you want to block the UI, simply show an `UIAlerView` when starting the request and dismiss it in `connectionDidFinishLoading`.
Felix
A: 

I use NSNotificationor that puspose. In DidFinishLoading method post a notification,

[[NSNotificationCenter defaultCenter] postNotificationName:@"Name" object:someObject];

and handle this notification in anyclass by

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMethod:) notificationName:@"Name" object:nil];

and implement

-(void)handleMethod:(NSNotification *)ntf
EEE