If you are like me and love using blocks over delegates, then these code snippets will come in handy. After learning about blocks I have come to love to use them, especially using them for callbacks rather than using the old protocol/delegate methodology. These code snippets will allow you to use blocks as callbacks over delegates.
In a previous post Facebook SDK – Posting to User News Feed I showed how to post various types of status’s to the logged in users news feed. These code snippets have been added to a of the official Facebook for iOS SDK. A sample of how to use the new block callbacks can be found within the from the last post.
For more information on blocks, you can refer to the discussing blocks in more detail.
Step 1: Modifying FBRequest Class for new callbacks
First we will modify the FBRequest class. There are a few simple modifications that we need to create. Within the FBRequest.h file add the following:
@property (copy) void (^FBRequestCallback)(FBRequest*, id, NSError*); @property (nonatomic, assign) BOOL usesBlockCallback; + (FBRequest*) getRequestWithParams:(NSMutableDictionary *)params httpMethod:(NSString *)httpMethod callback:(void(^)(FBRequest *request, id result, NSError *error)) _block requestURL:(NSString *)url; |
In the FBRequest.m file add the following code:
@synthesize FBRequestCallback = _FBRequestCallback; @synthesize usesBlockCallback; + (FBRequest*) getRequestWithParams:(NSMutableDictionary *)params httpMethod:(NSString *)httpMethod callback:(void(^)(FBRequest *request, id result, NSError *error)) _block requestURL:(NSString *)url { FBRequest* request = [[[FBRequest alloc] init] autorelease]; request.delegate = nil; request.url = url; request.httpMethod = httpMethod; request.params = params; request.connection = nil; request.responseText = nil; request.FBRequestCallback = _block; return request; } |
In the FBRequest class we are duplicating what they already have in the provided method but swapping out the delegate with a block. Out of personal preference I went with a single callback versus having an error and result callback. If there is no error we will be returning nil for the error parameter.
I also added a boolean property that will be used to determine which callback to use when processing the data when it has been returned from the Facebook servers.
Moving onto the – (void)handleResponseData:(NSData *)data; Method we need to add logic to pass the result request data onto the block callback.
- (void)handleResponseData:(NSData *)data { if ([_delegate respondsToSelector: @selector(request:didLoadRawResponse:)]) { [_delegate request:self didLoadRawResponse:data]; } if (usesBlockCallback) { NSError* error = nil; id result = [self parseJsonResponse:data error:&error]; _FBRequestCallback(self, result, error); } else if ([_delegate respondsToSelector:@selector(request:didLoad:)] || [_delegate respondsToSelector:@selector(request:didFailWithError:)]) { NSError* error = nil; id result = [self parseJsonResponse:data error:&error]; if (error) { [self failWithError:error]; } else if ([_delegate respondsToSelector:@selector(request:didLoad:)]) { [_delegate request:self didLoad:(result == nil ? data : result)]; } } } |
We are using the same logic as the standard delegate methodology, except we do not need to make a separate delegate call if there is an error. Within the block we have included result and an NSError parameter. That way you can handle the error logic within the same callback.
Finally within the dealloc method we need to release the block. Since we are using the block in objective-c we can simply use [_FBRequestCallback release]; You can also use Block_release(_FBRequestCallback); but since we are using it within Objective-C we can use the standard way of releasing an object.
- (void)dealloc { [_FBRequestCallback release]; [_connection cancel]; [_connection release]; [_responseText release]; [_url release]; [_httpMethod release]; [_params release]; [super dealloc]; } |
Step 2: Adding request methods with block callbacks
We want to include two new methods to the Facebook.h class. One method is for graph API requests, and one is for the the old REST API.
In the Facebook.h add the following two methods:
- (void) requestWithGraphPath:(NSString *) _graphPath params:(NSMutableDictionary*) _params method:(NSString*) _method callback:(void(^)(FBRequest *request, id result, NSError *error)) _block; - (void) requestWithMethodName:(NSString *) _methodName andParams:(NSMutableDictionary *) _params andHttpMethod:(NSString *) _method callback:(void(^)(FBRequest *request, id result, NSError *error)) _block; |
In the Facebook.m file add the following two method implementations:
- (void) requestWithGraphPath:(NSString *) _graphPath params:(NSMutableDictionary*) _params method:(NSString*) _method callback:(void(^)(FBRequest *request, id result, NSError *error)) _block { NSString * fullURL = [kGraphBaseURL stringByAppendingString:_graphPath]; [_params setValue:@"json" forKey:@"format"]; [_params setValue:kSDK forKey:@"sdk"]; [_params setValue:kSDKVersion forKey:@"sdk_version"]; if ([self isSessionValid]) { [_params setValue:self.accessToken forKey:@"access_token"]; } [_request release]; //modify request to have block _request = [[FBRequest getRequestWithParams:_params httpMethod:_method callback:_block requestURL:fullURL] retain]; [_request connect]; } - (void) requestWithMethodName:(NSString *) _methodName andParams:(NSMutableDictionary *) _params andHttpMethod:(NSString *) _method callback:(void(^)(FBRequest *request, id result, NSError *error)) _block { NSString * fullURL = [kRestserverBaseURL stringByAppendingString:_methodName]; [_params setValue:@"json" forKey:@"format"]; [_params setValue:kSDK forKey:@"sdk"]; [_params setValue:kSDKVersion forKey:@"sdk_version"]; if ([self isSessionValid]) { [_params setValue:self.accessToken forKey:@"access_token"]; } [_request release]; //modify request to have block _request = [[FBRequest getRequestWithParams:_params httpMethod:_method callback:_block requestURL:fullURL] retain]; [_request connect]; } |
Once again we are duplicating the previously implemented methods by Facebook. The only difference is changing the request method. As you notice we are now using the methods we implemented in the FBRequest class.
Now that we have everything implemented that we need in the Facebook SDK we can use our awesome new block callbacks! The following snippet will return the logged in users friends!
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease]; [facebook requestWithGraphPath:@"me/friends" params:params method:@"GET" callback:^(FBRequest *request, id result, NSError *error) { NSLog(@"friends %@", result); if ([result isKindOfClass:[NSDictionary class]]) { friendsList = [[result objectForKey:@"data"] retain]; [table reloadData]; } }]; |