Question

.h
@property (strong) NSString *reply;

I have the following method:

.m
@synthesize reply;

- (void)send
 {
 [NSURLConnection sendAsynchronousRequest:[self request] 
                 queue:[[NSOperationQueue alloc] init] 
                 completionHandler:
                        ^(NSURLResponse *response, NSData *data, NSError *error)       
                        {
                            if (error)
                            {
                                //NSLog(@"Error,%@", [error localizedDescription]);
                                [self setReply: [[NSString alloc] initWithString:[error localizedDescription]]];
                            }
                            else 
                            {
                                //NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
                                [self setReply: [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]];
                            } 
                        }];

}

Now, I have tried to return a value from the block/handler, but apparently this is not possible ( or I haven't figured out the syntax yet ).

I have tried to set a local variable to get the value of the reply (Data) but it produces errors.

The only way that errors were not produced is by using a class property. But when I try to read the value of [self reply] it is null, which makes me think that it is never set.

I understand that the sendAsync function is threaded and asynchronous, so is that the reason that the value of [self reply] is null, while when I use a sendSynchronousRequest, I always get a reply ???

What am I doing wrong, and how can I return a value from within the completionHandler ???

Edit#1: It appears to me that Im doing something very wrong. I was tracing into the code, and when I used the synchronous send, everything worked fine. Using the asynchronous one, the actual call to sendAsynchronousRequest was never executed, making me think that the thread is not being invoked, and hence the empty value.

Edit#2: I have found a way around this, by adding the following:

[NSURLConnection sendAsynchronousRequest:[self request] 
                 queue:[NSOperationQueue alloc] init
                 completionHandler:
                        ^(NSURLResponse *response, NSData *data, NSError *error)       
                        {
                            if (error)
                            {
                                //NSLog(@"Error,%@", [error localizedDescription]);
                                [self setReply: [[NSString alloc] initWithString:[error localizedDescription]]];
                            }
                            else 
                            {
                                //NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
                                [self setReply: [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]];
                            } 
                        }];

[[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 2]];

However, I am not entirely sure I understand how it works. I imagine that RunLoop tells the async operation to run for 2 seconds ( I can verify because when setting the date interval to a low value, I get a null response, due to testing with a live server ). What I do not understand, is when I just tell the RunLoop to run, it keeps blocking everything forever, as if the current thread never terminated.

Was it helpful?

Solution 2

Almost a month later, I found the solution to my problem:

    NSURLConnection* _connection = [[NSURLConnection alloc] initWithRequest:[self request] delegate:self startImmediately:NO];
    self.port = [NSPort port];                                                                                                       
    self.runloop = [NSRunLoop currentRunLoop];                                                                                       
    [self.runloop addPort:self.port forMode:NSDefaultRunLoopMode];
    [_connection scheduleInRunLoop:self.runloop forMode:NSDefaultRunLoopMode];
    [_connection start];
    while (self.finished != YES ) {
        [self.runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]];                                                                    
    }
    [self.runloop removePort:[self port] forMode:NSDefaultRunLoopMode];
    [_connection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

This did the trick for me. Bear in mind I had to properly implement NSConnection's delegate methods (didReceiveResponse,didReceiveData,didFailWithError,connectionDidFinishLoading). However it is now working asynchronously, does not freeze the UI, and has proper error reporting. Hope it may help someone stuck at the same issue

OTHER TIPS

Get rid of that runUntilDate and try using [NSOperationQueue mainQueue], see if that changes anything.

[NSURLConnection sendAsynchronousRequest:[self request] 
             queue:[NSOperationQueue mainQueue]
             completionHandler:
                    ^(NSURLResponse *response, NSData *data, NSError *error)...     

You should try to use the __block keyword for the declaration of your property reply

@property (strong) __block NSString *reply;

This keyword makes your property writable from inside a block.

EDIT

Proposal for a method that blocks the main thread until a response is received or until the request times out:

//this block is used to define a waiting condition
typedef BOOL (^ CheckBlock)();

- (BOOL)waitUntilBlockTrue:(CheckBlock)theBlock orTimeout:(int)timeout {
    NSDate *waitingStartPoint = [NSDate date];
    BOOL stillNeedToWait = YES;
    while (stillNeedToWait && theBlock()) {
        stillNeedToWait = ([waitingStartPoint timeIntervalSinceNow]*(-1.0)) < timeout;
    }
    return stillNeedToWait;
}

For your example you would call this method with:

[self waitUntilBlockTrue:^{
        //returns YES as long as the method should block
        return (BOOL)(self.reply == nil);
    } orTimeout:10];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top