Completion block after web service finishes?

2019-07-29 10:11发布

问题:

I have an implementation of a RESTful API in a class called WebServices. I call methods in this class from other classes within my app and would like to perform actions upon successful completion of a web service. I have a completion block as a part of my method headers, but am not sure if I am using them correctly as the app never seems to reach inside the completion part of the method call. This is how I am calling my method:

  [services callUnregisterForPushNotifications:savedAccessToken pushToken:savedPushToken completionBlock:^(NSMutableArray *resultsArray) {

        NSLog(@">>> COMPLETE! <<<");

        [self.loadingView removeFromSuperview];
    }];

And the method I am calling looks like this:

- (void)callUnregisterForPushNotifications:(NSString *)accessToken
                               pushToken:(NSString *)pushToken
                         completionBlock:(void (^)(NSMutableArray *resultsArray))completion{

    NSLog(@"UNREGISTER FOR PUSH CALLED!");

    NSLog(@"PUSH TOKEN %@", pushToken);

    NSString *appendUrl = @"alerts/unregisterpush/";

    NSLog(@"APPEND URL %@",appendUrl);

    NSURL *unregisterUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", BaseURLString, appendUrl]];
    NSLog(@"UNREGISTER URL: %@",unregisterUrl);

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:unregisterUrl
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:30.0];

    [request setHTTPMethod:@"PUT"];

    NSString *appendToken = [NSString stringWithFormat:@"Bearer %@", accessToken];
    NSLog(@"TOKEN: %@",appendToken);

    [request addValue:appendToken forHTTPHeaderField:@"Authorization"];
    [request addValue:@"application/json, text/plain, */*" forHTTPHeaderField:@"Accept"];
    [request addValue:@"application/json;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];


    NSString *postString = [NSString stringWithFormat:@"{\"Guid\":\"%@\"}",pushToken];
    NSLog(@"POST STRING: %@",postString);
    [request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

    NSLog(@"REQUEST %@",request);

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

                                                              NSLog(@"UNREGISTER PUSH NOTIFICATIONS RESPONSE: %@", response);
                                                              NSLog(@"UNREGISTER PUSH NOTIFICATIONS ERROR: %@", error);
                                                              NSLog(@"UNREGISTER PUSH NOTIFICATIONS DATA: %@", data);

                               NSData *_data = data;// ... whatever
                               NSMutableString *_string = [NSMutableString stringWithString:@""];
                               for (int i = 0; i < _data.length; i++) {
                                   unsigned char _byte;
                                   [_data getBytes:&_byte range:NSMakeRange(i, 1)];
                                   if (_byte >= 32 && _byte < 127) {
                                       [_string appendFormat:@"%c", _byte];
                                   } else {
                                       [_string appendFormat:@"[%d]", _byte];
                                   }
                               }
                               NSLog(@"UNREGISTER PUSH RESPONSE: %@", _string);

                               id obj= [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                               if (!obj) {
                                    //NSLog(@"REGISTER PUSH NOTIFICATIONS ERROR: %@", error);

                               } else {
                                    //NSLog(@"REGISTER PUSH NOTIFICATIONS DATA: %@", obj);

                                   //self.accessToken = [obj valueForKey:@"access_token"];
                                   //NSLog(@"ACCESS TOKEN: %@",self.accessToken);
                               }

                           }];
}

Any advice / input would be appreciated, thanks in advance!

回答1:

In your else block you should call the completion handler like so:

// [code omitted for brevity]

                       NSLog(@"UNREGISTER PUSH RESPONSE: %@", _string);

                       id obj= [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                       if (!obj) {
                            //NSLog(@"REGISTER PUSH NOTIFICATIONS ERROR: %@", error);

                       } else {
                            completionHandler(resultsArray); // add this line to actually call the completion block passed in
                            //NSLog(@"REGISTER PUSH NOTIFICATIONS DATA: %@", obj);

                           //self.accessToken = [obj valueForKey:@"access_token"];
                           //NSLog(@"ACCESS TOKEN: %@",self.accessToken);
                       }


                   }];

Also make sure to pass in the actual array instead of the resultsArray parameter I passed.

What you are basically doing is passing in a function (or "block") which does not know when to execute (you'll have to do that yourself after any asynchronous tasks have finished). So you passed in the block from your calling method:

[services callUnregisterForPushNotifications:savedAccessToken pushToken:savedPushToken completionBlock:^(NSMutableArray *resultsArray) {

        NSLog(@">>> COMPLETE! <<<");

        [self.loadingView removeFromSuperview];
    }];

The block of code contained in the curly braces is passed on to the function callUnregisterForPushNotifications:pushToken:completionHandler: and assigned to the completionHandler parameter which now contains the block of code you passed in when initially calling the method. The method that receives the completionHandler block is responsible for calling it once the asynchronous tasks (network requests) are finished (as you can see in the first snippet I posted).

completionHandler(resultsArray);

That way, once your request has finished (the else block) you are ready to execute the completion block that was passed in. That will effectively mean "Execute the block of code I passed in earlier because now we have the data from the network operation."