Let's explore different ways of accessing your Facebook wall.
Let's start with ...
Using Social.framework
Since iOS 6, Apple added the support of Facebook in its Social.framework. With the built-in support, end user has to register their Facebook account into iOS settings and grant access.- (IBAction)postToFacebook:(id)sender { ACAccountStore *accountStore = [[ACAccountStore alloc] init]; ACAccountType *facebookAccountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; __block ACAccount *facebookAccount; // Specify App ID and permissions NSDictionary *options = @{ACFacebookAppIdKey: @"xxxxx", // [1] ACFacebookPermissionsKey: @[@"publish_actions"], // [2] @"ACFacebookAudienceKey": ACFacebookAudienceFriends}; [accountStore requestAccessToAccountsWithType:facebookAccountType options:options completion:^(BOOL granted, NSError *e) { if (granted) { // [3] NSArray *accounts = [accountStore accountsWithAccountType:facebookAccountType]; facebookAccount = [accounts lastObject]; // Get the access token, could be used in other scenarios ACAccountCredential *fbCredential = [facebookAccount credential]; NSString *accessToken = [fbCredential oauthToken]; NSLog(@"...Facebook Access Token: %@", accessToken); } else { // Handle Failure } }]; if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) { // [4] SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook]; [controller setInitialText:@"First post from my iPhone app"]; [self presentViewController:controller animated:YES completion:Nil]; } }
In [1], you specify your Facebook app id and you specify the permissions needed [2].
In [3], you're in the callback from the authorization. As there is only one callback you need to test for successful grant or not. You can then embed your logic in it. As an exemple I show how you can retrieve access token and just logged them.
In [4], you can wait until the asynch authorization request has ended and do you Facebook post using SLComposeViewController. Note when doing you POST request it's the framework itself that pass the access token into the HTTP header. Easy peasy nothing to do on your side.
Pro and cons:
As a pre-requisite, the end user need to be registered in Setting
Facebook credentials are stored in your phone
Only build-in support provider like Twitter, Facebook (for now)
Note all Facebook actions are supported, for more advanced features you will need to go the 2 other ways.
Using Facebook iOS SDK
The good part of Facebook SDK is that it comes bundled with a bunch of good exemples, install it and follow the instruction as described here. For the purpose of our comparison exercice I've chosen to extract some code snipet from the HelloFacebookSample which shows you how handle OAuth2 authenticate and authorize using external browser. To deal with going back to main app from external browser, you work with URL schema and you need the following setting in your property file:The Facebook SDK provides you predefined view and controller to achieve the different actions. For example with FBLoginView you can create a login button. Like shown below:CFBundleURLTypes CFBundleURLSchemes fbCLIENT_APP_ID
- (void)viewDidLoad { [super viewDidLoad]; // Create Login View so that the app will be granted "status_update" permission. FBLoginView *loginview = [[FBLoginView alloc] init]; loginview.frame = CGRectOffset(loginview.frame, 5, 5); loginview.delegate = self; [self.view addSubview:loginview]; [loginview sizeToFit]; }Implementing the FBLoginViewDelegate delegate, you can implement the callback method as required. Notice here we use a delegate vs a callback block approach for async call. My preference goes toward block if you remember my previous blog post, but yeah just a matter of preferences ;)
@protocol FBLoginViewDelegatePro and cons:- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView; - (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id )user; - (void)loginViewShowingLoggedOutUser:(FBLoginView *)loginView; - (void)loginView:(FBLoginView *)loginView handleError:(NSError *)error; @end
Another SDK to learn,
a Facebook specific dependency to add
Using AeroGear
Using AeroGear iOS OAuth2 adapter, you can log in to any OAuth2 provider, don't need to include Facebook iOS sdk or Social.framework. The main advantage is if your Shoot'nShare app wants to share a photo to different providers like Google+, Facebook you don't need to work with each sdk.-(void)oauthFacebook { // start up the authorization process AGAuthorizer* authorizer = [AGAuthorizer authorizer]; // TODO replace XXX -> secret and YYY -> your app id in this file + plist file idIn [1], you configure your OAuth2 provider. In [2], you request the grant access. If this is the first time, the app open an external browser to start OAuth2 danse. Enter login/password if not already logged and grant access. Once access is granted you will be forwarded back to Shoot'nshare app using URL schema. The main advantage of using external browser rather than embedded view is that it's keep your login/password safe. No code in between embedded view and client app that you can't control._facebookAuthzModule = [authorizer authz:^(id config) { // [1] config.name = @"restAuthMod"; config.baseURL = [[NSURL alloc] init]; config.authzEndpoint = @"https://www.facebook.com/dialog/oauth"; config.accessTokenEndpoint = @"https://graph.facebook.com/oauth/access_token"; config.clientId = @"YYY"; config.clientSecret = @"XXX"; config.redirectURL = @"fbYYY://authorize/"; config.scopes = @[@"user_friends, publish_actions"]; config.type = @"AG_OAUTH2_FACEBOOK"; }]; [_facebookAuthzModule requestAccessSuccess:^(id response) { // [2] [self shareWithFacebook]; NSLog(@"Success to authorize %@", response); } failure:^(NSError *error) { NSLog(@"Failure to authorize"); }]; }
Some configuration is needed in your Shoot-Info.plist for URL schema:
Ready to actually work with Pipe objects, also part of AeroGear iOS, Pipe is a connection abstraction that let you do CRUD operation asynchronously over the network:CFBundleURLTypes CFBundleURLName fb240176532852375 CFBundleURLSchemes fb240176532852375
-(void)shareWithFacebook { // extract the image filename NSString *filename = ...; // the Facebook API base URL, you need to NSURL *gUrl = [NSURL URLWithString:@"https://graph.facebook.com/me/"]; AGPipeline* gPipeline = [AGPipeline pipelineWithBaseURL:gUrl]; // set up upload pipe idIn [1] I just instantiate a Pipe that I can use to save my picture [2].uploadPipe = [gPipeline pipe:^(id config) { // [1] [config setName:@"photos"]; [config setAuthzModule:_facebookAuthzModule]; }]; // Get currently displayed image NSData *imageData = ...; // set up payload with the image AGFileDataPart *dataPart = [[AGFileDataPart alloc] initWithFileData:imageData name:@"image" fileName:filename mimeType:@"image/jpeg"]; NSDictionary *dict = @{@"data:": dataPart}; // show a progress indicator [uploadPipe setUploadProgressBlock:^(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { dispatch_async(dispatch_get_main_queue(), ^{ [SVProgressHUD showProgress:(totalBytesSent/(float)totalBytesExpectedToSend) status:@"uploading, please wait"]; }); }]; // upload file [uploadPipe save:dict success:^(id responseObject) { // [2] [SVProgressHUD showSuccessWithStatus:@"Successfully uploaded!"]; } failure:^(NSError *error) { [SVProgressHUD showErrorWithStatus:@"Failed to upload!"]; }]; }
Pros and cons:
AeroGear OAuth2 dependency to add but cross providers
More verbose in term of configuration but you can do whatever you want once you get the access token, it's just plain HTTP REST calls.
You can find the complete source code in aerogear-ios-cookbook git repo, going to the Shoot recipe.
Conclusion
Because I'm core committer on the AeroGear project, my view is biased of course. I'd go for the AeroGear way :) the main reason being it gives you the most flexibility without having to link to several SDKs. The built-in solution is also very tempting but limited besides the fact it's required the end user a previous registration.Many ways to achieve the same end result, and looking deeper you may find other ways. I'm curious to hear about your discoveries and how we could improve AeroGear iOS libs. Share your thoughts, email AeroGear.
Tweet
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.