iosfacebookasynchronousfacebook-sdk-4.0facebook-batch-request

FaceBook iOS SDK Batch Request & Single Completion Handler


I started to use Facebook SDK for iOS and i'm a little bit confused,

I'm seeing this little piece of code that shows how we can perform a batch request however having a completion handler per request is absurd for me. how can you make a completion handler per request when you make a single batch request ? maybe i didn't get the concept ? there is for sure an explanation for this but i don't see it right now

if ([[FBSDKAccessToken currentAccessToken] hasGranted:@"user_likes"]) {
  FBSDKGraphRequest *requestMe = [[FBSDKGraphRequest alloc]
    initWithGraphPath:@"me" parameters:nil];
  FBSDKGraphRequest *requestLikes = [[FBSDKGraphRequest alloc]
    initWithGraphPath:@"me/likes" parameters:nil];
  FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init];
  [connection addRequest:requestMe
           completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
   //TODO: process me information
}];
  [connection addRequest:requestLikes
            completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
    //TODO: process like information
  }];
  [connection start];
}

If it is the only way to do it how can i know when all my requests have been performed ?


Solution

  • I created an FBBatchHandler class to automatically combine results and return a single completion handler to the caller. Source and example usage below. It's currently designed specifically for retrieving events (i.e. the final result is an NSArray of events), but it should be easily to repurpose it for other tasks. Let me know if this works for you.

    FBBatchHandler.h

    #import <Foundation/Foundation.h>
    #import "FBSDKGraphRequestConnection.h"
    
    typedef void (^BatchFinishedHandler)(NSArray *result, NSError *error);
    
    @interface FBBatchHandler : NSObject
    
    - (instancetype)initWithConnection:(FBSDKGraphRequestConnection *)connection
                       finishedHandler:(BatchFinishedHandler)finishHandler;
    
    - (void)addRequestResult:(id)result;
    
    @end
    

    FBBatchHandler.m

    #import "FBBatchHandler.h"
    
    @interface FBBatchHandler () <FBSDKGraphRequestConnectionDelegate>
    
    @property (nonatomic, strong) FBSDKGraphRequestConnection *connection;
    @property (nonatomic, strong) BatchFinishedHandler finishHandler;
    @property (nonatomic, strong) NSMutableArray *batchResults;
    
    @end
    
    @implementation FBBatchHandler
    
    #pragma mark - Instance
    
    - (instancetype)initWithConnection:(FBSDKGraphRequestConnection *)connection
                       finishedHandler:(BatchFinishedHandler)finishHandler {
        if (self = [super init]) {
            _connection = connection;
            _connection.delegate = self;
            _finishHandler = finishHandler;
            _batchResults = [NSMutableArray new];
        }
    
        return self;
    }
    
    #pragma mark - Public methods
    
    - (void)addRequestResult:(id)result {
        if ([result isKindOfClass:[NSDictionary class]]) {
            [self.batchResults addObjectsFromArray:result[@"data"]];
        }
    }
    
    #pragma mark - FBSDKGraphRequestConnectionDelegate
    
    - (void)requestConnectionDidFinishLoading:(FBSDKGraphRequestConnection *)connection {
        self.finishHandler([self.batchResults copy], nil);
    }
    
    - (void)requestConnection:(FBSDKGraphRequestConnection *)connection
             didFailWithError:(NSError *)error {
        self.finishHandler([self.batchResults copy], error);
    }
    
    @end
    

    Example Usage

    - (void)retrieveEvents {
        NSDictionary *params = @{@"since": @"now", @"fields": @"id,name,description,start_time,end_time,location,picture"};
        FBSDKGraphRequest *attending = [[FBSDKGraphRequest alloc] initWithGraphPath:@"/me/events/attending" parameters:params];
        FBSDKGraphRequest *created = [[FBSDKGraphRequest alloc] initWithGraphPath:@"/me/events/created" parameters:params];
        FBSDKGraphRequest *maybe = [[FBSDKGraphRequest alloc] initWithGraphPath:@"/me/events/maybe" parameters:params];
        FBSDKGraphRequest *notReplied = [[FBSDKGraphRequest alloc] initWithGraphPath:@"/me/events/not_replied" parameters:params];
    
        FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init];
        FBBatchHandler *batchHandler = [[FBBatchHandler alloc] initWithConnection:connection
                                                                  finishedHandler:^(NSArray *result, NSError *error) {
                                                                      // Here's the "single" completion handler
                                                              }];
        [connection addRequest:attending
             completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
                 [batchHandler addRequestResult:result];
             }];
        [connection addRequest:created
             completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
                 [batchHandler addRequestResult:result];
             }];
        [connection addRequest:maybe
             completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
                 [batchHandler addRequestResult:result];
             }];
        [connection addRequest:notReplied
             completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
                 [batchHandler addRequestResult:result];
             }];
        [connection start];
    }