Josh Hunt
Josh Hunt

Reputation: 14521

Best way to create nested callbacks in ObjectiveC

I have a class that handles interaction with an external API, and I'm trying to figure out the best way to break them into descreat chunks.

At the moment, it looks something like this:

- (BOOL) testCredentialsWithUsername:(NSString *)username password:(NSString *)password {
    acUsername = username;
    acPassword = password;
    [self loadLoginPage];
    return YES;
}

- (void)loadLoginPage {
    [NSURLConnection sendAsynchronousRequest:...
                           completionHandler:^(...) {
                               [self logIntoMyAPI:response];
                           }];
}

- (void)logIntoMyAPI:(NSURLResponse *)response {
    [NSURLConnection sendAsynchronousRequest:...
                           completionHandler:^(...) {
                               [self recieveLoginResponse:response data:data];
                           }];
}

- (void)recieveLoginResponse:(NSURLResponse *) response data:(NSData *)data {
    if (...) {
        NSLog(@"Logged into MyAPI!");
    } else if (...) {
        NSLog(@"Invalid username or password");
    } else if (...) {
        NSLog(@"Locked account");
    } else {
        NSLog(@"An unexpected error");
    }
}

Basically, I want to call testCredtionalsWithUsername:password: from my View Controller and, probably using success/failure blocks, know the outcome.

What's the best way to do this without tieing the callbacks to the test testCredtionalsWithUsername:password: method?

Upvotes: 1

Views: 598

Answers (2)

Mikkel Selsøe
Mikkel Selsøe

Reputation: 1201

You need to wait for your server to respond before you have a result of your test, so there no real point in returning a BOOL from testCredentialsWithUsername:password: it seems.

An elegant solution is to have a completionBlock that is called whenever you have an answer. Like this:

- (void) testCredentialsWithUsername:(NSString *)username password:(NSString *)password completion:(void (^)(BOOL success, NSError *error))completion {
    acUsername = username;
    acPassword = password;
    _validationCompletion = completion; // Save this as an instance variable
    [self loadLoginPage];
}

When you get a result from the server:

- (void)recieveLoginResponse:(NSURLResponse *) response data:(NSData *)data {
    BOOL success = data != nil; //or whatever you criteria is.

    if (_completion != nil) {

        NSError = success ? nil : [NSError new]; // create an appropriate error object

        _completion(success, error)
        _completion = nil;
    }
}

Upvotes: 1

Greg
Greg

Reputation: 25459

You can add two properties for success block and failure block to that class and call it when the request is completed. In your view controller allocate instance of this class and pass two blocks to this parameters. You can also add initWithSuccessBlock:failureBlock: method to that class and in your view controller just call this method when you initialise this class.

Hope this help.

Upvotes: 0

Related Questions