0

I'm creating a REST client class for my iPad app. So I created a BOOL method which does the login using an NSURLConnection subclass I created earlier.

This JWURLConnection has block type properties for the finishLoading and failWithError operations.

The Problem is that the URL connection most likely finishes (or fails) AFTER this method is completely executed. A cannot use an extra method to use performSelector:waitUntilDone: too because I have to wait for the connection.

Now I tried using plain C semaphores and an extra thread (so that the semaphore blocks only the RESTClient thread, not the URLConnections one), but I had no success; the method started waiting but the whole connection stuff was frozen, thus there where no NSLogs from the connection.

The JWURLConnection starts it's own thread by itself within the -start method:

- (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [super start]; }); }

Here is the code I tried it with (using semaphores):

- (BOOL)loginWithUsername:(NSString *)uName ansPassword:(NSString *)pWord {
    __block BOOL loginSucceeded = NO;

    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    JWURLConnection *connection = [JWURLConnection connectionWithPOSTRequestToURL:POSTData:];
    [connection setFinished^(NSData *data) {
        // validate server response and set login variable
        loginSucceeded = YES;

        dispatch_semaphore_signal(sema);
    }];
    [connection setFailed:^(NSError *error) {
        loginSucceeded = NO;
        NSLog(@"Login failed: %@", [error description]);

        dispatch_semaphore_signal(sema);
    }];
    [connection start];

    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

    // do some more stuff like error handling / reporting here

    return loginSucceeded;
}

I hope you can lead my the right direction...

Julian F. Weinert
  • 7,474
  • 7
  • 59
  • 107

1 Answers1

0

The JWURLConnection starts it's own thread by itself within the -start method:

- (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [super start]; }); }

You need to ensure that a NSURLConnection's delegate methods will be scheduled on a NSRunLoop or a NSOperationQueue. While the start method could actually take care of this - the given code and your comment indicate it does not ;) In short, dispatch_async does not guarantee that the underlaying thread has a run loop and a dispatch queue does not even guarantee that the underlaying thread is always the same.

The docs show how to schedule a connection.

I would suggest to schedule the connection on the main thread, and change this to a NSOperationQueue when required.

Your loginWithUsername:andPassword: method will simply return immediately since you call/invoke an asynchronous function/method.

Employing asynchronous patterns is kinda "infectious". Once you started using asynchronous programming style, you cant get "rid of" it unless you use synchronization primitives that block the current thread. I would suggest to keep the async style:

- (void) loginWithUsername:(NSString *)uName 
               andPassword:(NSString *)pWord 
                completion:(void(^)(id result))onCompletion;

And later:

[self loginWithUsername:@"Me" andPassword:@"secret" completion:^(id result) {
    if ([result != [isKindOfError class]]) {
        [self fetchImagesWithURL:url completion: ^(id result) {
             ...    
        }];
    }
}];
Community
  • 1
  • 1
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67