-1

At one point in my app, I call a method in a superclass to authenticate with the server. At the end of this method, I would like to run some code specific to the class that calls it. Is this the best way to wait for the response from the superclass before calling the additional code?

     [super authenticateWithServer^(BOOL success, NSError *error) {
             dispatch_async(dispatch_get_main_queue(), ^{
             NSLog(@"heard back from method");
if (success==YES) {
//RUN MY CODE HERE
}
             });
             }];

If so, what would the method look like? Something like the following?

-(BOOL)authenticateWithServer (
//if fail {
return NO;
}
else {
return YES;
}
}
user6631314
  • 1,751
  • 1
  • 13
  • 44

1 Answers1

1

BOOL return types don't mix with async operations. Instead you'll want to pass the result of authenticating with the server into the completion block and have your caller inspect it. I would highly recommend having a look at http://goshdarnblocksyntax.com for the correct block syntax.

I'm pretty sure this is close to what you're attempting to do (added a fake delay in here to simulate the server request):

@interface ViewController ()
@property (strong, nullable) IBOutlet UILabel *resultLabel;
- (void)authenticateWithServer:(void (^_Nullable)(BOOL success, NSError *_Nullable error))completion;
- (void)_fakeUrlRequestToServerWithCompletion:(void(^_Nullable)(BOOL successFromServer, NSError *_Nullable errorFromServer))serverCompletion;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (IBAction)authWithServerButton:(id)sender {
    // call to authenticate with the results in the callback (not returning BOOL)
    [self authenticateWithServer:^(BOOL success, NSError * _Nullable error) {
        // callback will probably come in off the main queue so if you're doing some UI updates jump back on the main queue
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.resultLabel.text = [NSString stringWithFormat:@"Result = %@\nError = %@",success == 1 ? @"SUCCESS" : @"FAILURE", error == nil ? @"No Error" : error];
        }];
    }];
}

- (void)authenticateWithServer:(void (^_Nullable)(BOOL success, NSError *error))completion {
    // put this on the background queue so it doesn't hug
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        // call the fakeUrlRequestToServer method to simulate your async request
        [self _fakeUrlRequestToServerWithCompletion:^(BOOL successFromServer, NSError * _Nullable errorFromServer) {
            // completion block that was passed into the method, callback and pass along the success and error results
            completion(successFromServer, errorFromServer);
        }];
    });
}

- (void)_fakeUrlRequestToServerWithCompletion:(void(^_Nullable)(BOOL successFromServer, NSError *errorFromServer))serverCompletion {
    // fake sleep here for 2 seconds just to simulate waiting for the callback
    // never call sleep in your own code
    sleep(2);
    NSError *fakeError = nil;
    // just a fake auth success or failure
    BOOL fakeSuccess = arc4random() % 2 == 1 ? YES : NO;
    if (fakeSuccess == NO) {
        // fake error
        fakeError = [NSError errorWithDomain:@"FakeErrorDomain" code:22 userInfo:nil];
    }
    // completion block that was passed into the method, call back with the success and error params passed in
    serverCompletion(fakeSuccess, fakeError);
}


@end

Here's an example if it in action:

Fake Async Callback

Edit: because the completion blocks are nullable in the example I gave, you'll want to check whether one was passed in first.

i.e.

  if (completion) {
    completion(success, error);
  }
R4N
  • 2,455
  • 1
  • 7
  • 9