Posting Links to Facebook Profile from iPhone Code

Posted by Markus Piipari Wed, 19 Aug 2009 18:56:00 GMT

Recently, we needed to implement Facebook link sharing in one of our applications, to let users of the app who really like it let others now about it, too. Read on to find out how this can be done!

(And if you have the stamina, see the very end for a little discussion about how it is often too easy to overdo simple things...)

The Implementation

While the code is finally relatively simple, there are quite a number of steps to get there.

First, you need to sidetrack to Dan Grigsby's tutorial on Mobile Orchard about setting a Facebook users's status from iPhone code. The tutorial covers all necessary pre-requisites:

  • Setting up a Facebook application
  • Downloading the Facebook Connect SDK for iPhone
  • Setting up your Xcode project for it
  • Helper classes and examples you can base your own work on, as we did in the following.

Note that to succesfully follow the tutorial, you'll need to have a Git client installed, as the tutorial's helper classes are downloaded from GitHub.

Next, coding time!

Quick Glance at the Facebook Connect API and MOFBHelper

Before our first Objective-C line, here's the Facebook API we're going to use: Links.post

Don't be alarmed by the number of request parameters. Everything in the request except for the link's URL and comment text will be handled by the Facebook Connect package and MOFBHelper (and the other original helper classes).

Step 1: Posting the Link

Let's start from the bottom - the code doing the actual work - and work our way towards the top, the user interface.

Because we don't want to modify the Mobile Orchard code in any way, let's create a set of helper classes of our own.

Here is the interface for PCFacebookLinks, a class that posts links to the Facebook profile. The structure of the code is quite similar to MOFBStatus, the original helper class that does the same thing with setting a user's Facebook status.

	#import <Foundation/Foundation.h>
	#import "FBConnect/FBConnect.h"

	#import "MOFBPermission.h"

	@class PCFacebookLinks;

	@protocol PCFacebookLinksDelegate <NSObject>

	@optional
	-(void)linkWasPosted:(id)links;
	-(void)linkPosting:(PCFacebookLinks*)links didFailWithError:(NSError*)error;
	@end

	@interface PCFacebookLinks : NSObject <FBRequestDelegate, MOFBPermissionDelegate> {
		id _delegate;
		NSString *_comment;
		NSString *_url;
	}

	@property (nonatomic,assign) id <PCFacebookLinksDelegate> delegate;
	@property (nonatomic,retain) NSString *url;
	@property (nonatomic,retain) NSString *comment;

	- (void)postWithURL: (NSString *)url comment: (NSString *)comment;

	@end

And here's the implementation of PCFacebookLinks.

	#import "PCFacebookLinks.h"
	#import "MOFBErrors.h"

	@implementation PCFacebookLinks

	@synthesize delegate=_delegate, comment=_comment, url=_url;

	- (void)postWithURL: (NSString *)url
				comment: (NSString *)comment
	{
		_url = url;
		_comment = comment;
		MOFBPermission *permission = [[[MOFBPermission alloc] init] retain];
		permission.delegate = self;
		[permission obtain:@"share_item"];
	}

	- (void)permissionGranted:(MOFBPermission*)permission {
		[permission release];
		NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys: self.comment, @"comment", self.url, @"url", nil];
		[[FBRequest requestWithDelegate:self] call:@"facebook.Links.post" params:params];
	}

	- (void)permissionDenied:(MOFBPermission*)permission {
		[permission release];
		NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: @"Permission denied", NSLocalizedDescriptionKey, nil];
		NSError *error = [NSError errorWithDomain:@"PCFacebookErrorDomain" code:PERMISSION_DENIED userInfo:userInfo];
		[_delegate linkPosting:self didFailWithError:error];
	}

	- (void)request:(FBRequest*)request didLoad:(NSString *)result {
		[_delegate linkWasPosted:self];
	}

	- (void)request:(FBRequest*)request didFailWithError:(NSError*)error {
		[_delegate linkPosting:self didFailWithError:error]; 
	}

	- (void)dealloc {
		[_url release];
		[_comment release];
		[super dealloc];
	}

	@end

Notice the asynchronouus nature of the code: first we ask for the share_item permission, and only in response to the permission being granted try and post the link.

Step 2: Hiding the Ugly Details

Next, we need to extend the original MOFBHelper to support links sharing in addition to status updates. Again, we don't want to touch the original helper classes, in order to keep things tidy, and our own code separate. Here's the interface, PCFacebookHelper.

    #import <Foundation/Foundation.h>
	#import "MOFBHelper.h"

	#import "PCFacebookLinks.h"

	@class PCFacebookHelper;

	@protocol PCFacebookHelperDelegate <MOFBHelperDelegate>

	-(void)linkWasPosted:(id)helper url:(NSString*)url comment:(NSString*)comment;
	-(void)linkPosting:(id)helper didFailWithError:(NSError*)error;
	@end

	@interface PCFacebookHelper : MOFBHelper <PCFacebookLinksDelegate> {
		PCFacebookLinks *_links;
	}

	- (void)postLinkWithURL:(NSString *)url comment:(NSString *)comment;

	@end

...and its implementation:

    #import "PCFacebookHelper.h"

	@implementation PCFacebookHelper

	- (id) init {
		self = [super init];
		if(self != nil) {
			_links = [[[PCFacebookLinks alloc] init] retain];
			_links.delegate = self;
		}
		return self;
	}

	- (void)postLinkWithURL:(NSString *)url comment:(NSString *)comment
	{
		[_links postWithURL:url comment:comment];
	}

	- (void)linkWasPosted:(id)links {
		[delegate linkWasPosted:self url:[links url] comment:[links comment]];
	}

	-(void)linkPosting:(id)links didFailWithError:(NSError*)error {
		[delegate linkPosting:self didFailWithError:error];
	}

	- (void)dealloc {
		[_links release];
		[super dealloc];
	}

	@end

Step 3: User Interface Integration

Finally, we need to tie this code to the application's UI to test the thing. We'll implement a view controller for this purpose, PCFacebookViewController. (We're assuming here that you know what to do with a view controller, to display stuff in an iPhone UI.)

	#import <UIKit/UIKit.h>
	#import "FBConnect/FBConnect.h"

	#import "PCFacebookHelper.h"

	@interface PCFacebookViewController : UIViewController <FBSessionDelegate, PCFacebookHelperDelegate> {
		FBSession *_session;
		PCFacebookHelper *_helper;
	}

	@end

Note: be sure to replace the sessionForApplication and secret arguments with the appropriate values for your Facebook application in the following implementation.

You will of course also want to change the link URL and comment text in session:didLogin. (Our actual application let's the user to edit the comment before posting, but that's out of the scope of this blog post.)

    #import "PCFacebookViewController.h"

	@implementation PCFacebookViewController

	- (void)viewDidLoad {
		[super viewDidLoad];

		_session = [FBSession sessionForApplication:@"REPLACE THIS ONE"
											 secret:@"AND THIS, TOO"

										   delegate:self];		

		FBLoginButton* button = [[[FBLoginButton alloc] init] autorelease];
		[self.view addSubview:button];

		_helper = [[PCFacebookHelper alloc] init];
		_helper.delegate = self;
	}

	- (void)viewDidAppear:(BOOL)animated {
		[super viewDidAppear:animated];
		[_session resume];

	}

	- (void)session:(FBSession*)session didLogin:(FBUID)uid {
		[_helper postLinkWithURL:@"http://www.pearcomp.com/" comment:@"This link was posted from my iPhone!"];
	}

	-(void)linkWasPosted:(id)helper url:(NSString*)url comment:(NSString*)comment {
		NSLog(@"Link %@ was posted on Facebook with comment '%@'", url, comment);
	}

	-(void)linkPosting:(id)helper didFailWithError:(NSError*)error {
		NSLog(@"Link posting failed: %@", [error description]);
	}

	- (void)dealloc {
		[_session release];
		[_helper release];
		[super dealloc];
	}

	@end

What Did We Just Do?

Okay, it works. Are we happy with it? Yes and no.

We created something reusable that we can from now on include in any iPhone application that needs Facebook integration. Adding further Facebook Connect API features to our helper classes should also be straightforward.

Faithfully complying with the tradition of reusable software design, we created a codebase that can be maintained and improved in a single place. Setting up that single place will require some extra work, though.

I guess that's precisely the thing: there is just so much code there for such a little thing! Two levels of delegation, six separate files, and so on. It's hard to escape the feeling that we let some unnecessary architecture astronautism enter our heads when doing this. How many Facebook features are you going to need in an iPhone application, after all? Not that many, ever.

Then again, spending even more time on simplifying the code would be just more overkill. So, let's accept it for what it is, write a blog post, and move on to better, greater and simpler things!


Trackbacks

Use the following link to trackback from your own site:
http://www.pearcomp.com/trackbacks?article_id=18

Leave a comment

Comments

enter>