Facebook SDK – Posting to User News Feed

What the Posts will look like!


Hello, I’m Andy Yanok (Twitter) and I recently finished working on Friended for Facebook.  Many applications use Facebook for posting content from their respective applications, there could be infinite items that a developer would like to post to users feeds whilst using their applications but today we will be focusing on posting a link to a users feed.

In order to get started using the Facebook SDK & API’s you will need to download the latest copy of the Facebook SDK for iOS.  For further reference to using the Graph and FQL API’s you can refer to Facebook’s API documentation.  As I am sure you are used to using the great documentation provided by Apple for developing your iOS applications, but you may find the Facebook API documentation to not be so great.

In order to integrate Facebook into your app you will need to create an application on www.facebook.com/developers.  After creating the application you will be provided with keys needed for accessing the API’s. There are some nice features to integrating Facebook into your application such as seeing how many people “Like” your application, and statistics about the usage of your application.

In this tutorial we will be creating a simple Facebook news feed posting application with the following features/functionality.

  • Facebook API / SDK Request Wrapper
  • News feed post class
  • Simple UI to test the code

Getting Started:

Before you can begin writing code there are a few setup steps you will need to perform to setup access to the Facebook API.

First you will need to logon to www.facebook.com/developers, and create an application.  For this tutorial you can use the keys provided in the sample project. Once you have created an application the developer page Facebook will provide you with the keys needed .

Secondly, you will need to download the source for the Facebook SDK https://github.com/facebook/facebook-ios-sdk.  You may also pull the SDK source out of the sample project provided.  Included with the Facebook SDK are JSON parsing libraries, the Facebook SDK uses these libraries to parse the incoming data from their servers. Once this is done you can start to code!

Within your xCode project you will need to include the Facebook SDK folder and the JSON parsing Library folder.

Creating FBRequestWrapper class:
This class can be used to make GET and POST requests to the API. I like using a singleton class for this, obviously whilst writing Friended we have to make many requests and this proved to be the best method for what we needed. This is a singleton class that will handle all the requests for accessing the Facebook API.

#import "Facebook.h"

#define FB_APP_ID @"197366800287642"
#define FB_API_KEY @"3269fff9ef3b6fc13255e670ebb44c4d"
#define FB_APP_SECRET @"d038b12cc8632865952f69722fe26393"

@interface FBRequestWrapper : NSObject
{
	Facebook *facebook;
	BOOL isLoggedIn;
}

@property (nonatomic, assign) BOOL isLoggedIn;

+ (id) defaultManager;
- (void) setIsLoggedIn:(BOOL) _loggedIn;
- (void) FBSessionBegin:(id) _delegate;
- (void) FBLogout;
- (void) getFBRequestWithGraphPath:(NSString*) _path andDelegate:(id) _delegate;
- (void) sendFBRequestWithGraphPath:(NSString*) _path params:(NSMutableDictionary*) _params andDelegate:(id) _delegate;

@end

In the FBRequestWrapper.h file you will need to define the keys provided when you created your application on the Facebook Developer site.

FB_APP_ID, FB_API_KEY, FB_APP_SECRET

We will be creating two class variables, one for the Facebook SDK class, and a logged in Boolean variable which holds the status of being logged in.

Moving on to the Implementation of the FBRequestWrapper. FBSessionBegin this method is being used to instantiate the facebook object if it already has not, also will be connect the application to the Facebook SDK. Within this method we pull out the stored values for the accessToken and expiration date. These values are stored in NSUserDefaults once a user has logged in, we store these so that we will not have to repeatedly need to request these values from Facebook.

Once we have that setup, we need to specify an Array of the permissions this application will be requesting. For this application we only need the “publish_stream” permission, for more information on other permissions you can refer to the Facebook API Reference. Once we have created this array with the requested permissions we call the facebook authorize method. We are specifying the delegate passed to this wrapper class as the call back for a success or failure to login, this will be discussed in detail later.

- (void) FBSessionBegin:(id) _delegate {

	if (facebook == nil) {
		facebook = [[Facebook alloc] init];

		NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];
		NSDate *exp = [[NSUserDefaults standardUserDefaults] objectForKey:@"exp_date"];

		if (token != nil && exp != nil && [token length] > 2) {
			isLoggedIn = YES;
			facebook.accessToken = token;
            facebook.expirationDate = [NSDate distantFuture];
		} 

		[facebook retain];
	}

	NSArray * permissions = [NSArray arrayWithObjects:
							 @"publish_stream",
							 nil];

	//if no session is available login
	[facebook authorize:FB_APP_ID permissions:permissions delegate:_delegate];
}

Next we need to create the method that will be passing the POST requests to the Facebook API.

// Used for publishing
- (void) sendFBRequestWithGraphPath:(NSString*) _path params:(NSMutableDictionary*) _params andDelegate:(id) _delegate {

	if (_delegate == nil)
		_delegate = self;

	if (_params != nil && _path != nil) {

		[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
		[facebook requestWithGraphPath:_path andParams:_params andHttpMethod:@"POST" andDelegate:_delegate];
	}
}

In this method we are simply doing some validation, and sending the POST request to Facebook. The delegate passed to this method will be receiving the Pass/Fail response. This method will be used for all POST requests in the future. Other methods that are in the sample project are not contained in the scope of this tutorial.

Next we need to create the FBFeedPost class which will handle posting three different types of items to the users news feed. We will start by creating an enumeration to handle the 3 different types of posts that we will be explaining. We need three different post types, FBPostTypeStatus, FBPostTypePhoto, FBPostTypeLink. Once the enumeration has been completed we will create 5 properties in the class to be used, and create a delegate for a callback with the result of posting to Facebook.

#import "FBRequestWrapper.h"

@protocol FBFeedPostDelegate;

typedef enum {
  FBPostTypeStatus = 0,
  FBPostTypePhoto = 1,
  FBPostTypeLink = 2
} FBPostType;

@interface FBFeedPost : NSObject
{
	NSString *url;
	NSString *message;
	NSString *caption;
	UIImage *image;
	FBPostType postType;

	id  delegate;
}

@property (nonatomic, assign) FBPostType postType;
@property (nonatomic, retain) NSString *url;
@property (nonatomic, retain) NSString *message;
@property (nonatomic, retain) NSString *caption;
@property (nonatomic, retain) UIImage *image;

@property (nonatomic, assign) id  delegate;

- (id) initWithLinkPath:(NSString*) _url caption:(NSString*) _caption;
- (id) initWithPostMessage:(NSString*) _message;
- (id) initWithPhoto:(UIImage*) _image name:(NSString*) _name;
- (void) publishPostWithDelegate:(id) _delegate;

@end

@protocol FBFeedPostDelegate
@required
- (void) failedToPublishPost:(FBFeedPost*) _post;
- (void) finishedPublishingPost:(FBFeedPost*) _post;
@end

This class will be specifically be used for posting three different types of posts to your news feed. In this case I have created three different initialization methods for the different posts.

- (id) initWithLinkPath:(NSString*) _url caption:(NSString*) _caption {
	self = [super init];
	if (self) {
		postType = FBPostTypeLink;
		url = [_url retain];
		caption = [_caption retain];
	}
	return self;
}

- (id) initWithPostMessage:(NSString*) _message {
	self = [super init];
	if (self) {
		postType = FBPostTypeStatus;
		message = [_message retain];
	}
	return self;
}

- (id) initWithPhoto:(UIImage*) _image name:(NSString*) _name {
	self = [super init];
	if (self) {
		postType = FBPostTypePhoto;
		image = [_image retain];
		caption = [_image retain];
	}
	return self;
}

The meat and potatoes of this class is contained within the publishPostWithDelegate method.

We pass in the delegate that will be handling the response we receive from the FBRequrestWrapper class. From there we will be checking whether we are currently logged in to Facebook, if not make the Request to login via the FBRequestWrapper. If this is the first time the application is being ran for the users login they will be prompted to allow access to post to the users feed. Once the user has allowed access for the application to access the users feed they will no longer be prompted again to allow access.

Once logged in, we determine which post type we will be sending to the users feed. Each of the three different post types require specific parameters to be passed to Facebook via a dictionary.

Once the proper parameters have been set simply call:

 [[FBRequestWrapper defaultManager] sendFBRequestWithGraphPath:graphPath params:params andDelegate:self];

Whilst making the request we will be passing the delegate of the FBRequestWrapper as self, not the previously passed in delegate for the FBFeedPost class. We do this in order to handle deallocating the FBFeedPost instance after a failure or success message has been passed back from the Facebook API.

Within the FBFeedPost class we are implementing the Facebook SDK call back methods.

- (void) publishPostWithDelegate:(id) _delegate {

	//store the delegate incase the user needs to login
	self.delegate = _delegate;

	// if the user is not currently logged in begin the session
	BOOL loggedIn = [[FBRequestWrapper defaultManager] isLoggedIn];
	if (!loggedIn) {
		[[FBRequestWrapper defaultManager] FBSessionBegin:self];
	}
	else {
		NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];

		//Need to provide POST parameters to the Facebook SDK for the specific post type
		NSString *graphPath = @"me/feed";

		switch (postType) {
			case FBPostTypeLink:
			{
				[params setObject:@"link" forKey:@"type"];
				[params setObject:self.url forKey:@"link"];
				[params setObject:self.caption forKey:@"description"];
				break;
			}
			case FBPostTypeStatus:
			{
				[params setObject:@"status" forKey:@"type"];
				[params setObject:self.message forKey:@"message"];
				break;
			}
			case FBPostTypePhoto:
			{
				graphPath = @"me/photos";
				[params setObject:self.image forKey:@"source"];
				[params setObject:self.caption forKey:@"message"];
				break;
			}
			default:
				break;
		}

		[[FBRequestWrapper defaultManager] sendFBRequestWithGraphPath:graphPath params:params andDelegate:self];
	}
}

#pragma mark -
#pragma mark FacebookSessionDelegate

- (void)fbDidLogin {
	[[FBRequestWrapper defaultManager] setIsLoggedIn:YES];

	//after the user is logged in try to publish the post
	[self publishPostWithDelegate:self.delegate];
}

- (void)fbDidNotLogin:(BOOL)cancelled {
	[[FBRequestWrapper defaultManager] setIsLoggedIn:NO];

}

#pragma mark -
#pragma mark FBRequestDelegate

- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
	[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
	NSLog(@"ResponseFailed: %@", error);

	if ([self.delegate respondsToSelector:@selector(failedToPublishPost:)])
		[self.delegate failedToPublishPost:self];
}

- (void)request:(FBRequest *)request didLoad:(id)result {
	[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
	NSLog(@"Parsed Response: %@", result);

	if ([self.delegate respondsToSelector:@selector(finishedPublishingPost:)])
		[self.delegate finishedPublishingPost:self];
}

Once either Facebook has returned a success message or failure we will call the FBFeedPost’s corresponding delegate methods with a parameter of self so we can handle the memory management necessary after we have finished posting to the users feed.

Now that this class is implemented we can easily use this class! Within the sample project you can see all three post type examples. To Post a status to your feed, all you need to implement are the FBFeedPost Delegate methods and creating the request.

- (IBAction) btnPostPress:(id) sender {

	[self.txtView resignFirstResponder];

	//we will release this object when it is finished posting
	FBFeedPost *post = [[FBFeedPost alloc] initWithPostMessage:self.txtView.text];
	[post publishPostWithDelegate:self];

	IFNNotificationDisplay *display = [[IFNNotificationDisplay alloc] init];
	display.type = NotificationDisplayTypeLoading;
	display.tag = NOTIFICATION_DISPLAY_TAG;
	[display setNotificationText:@"Posting Status..."];
	[display displayInView:self.view atCenter:CGPointMake(self.view.center.x, self.view.center.y-100.0) withInterval:0.0];
	[display release];
}

#pragma mark -
#pragma mark FBFeedPostDelegate

- (void) failedToPublishPost:(FBFeedPost*) _post {

	UIView *dv = [self.view viewWithTag:NOTIFICATION_DISPLAY_TAG];
	[dv removeFromSuperview];

	IFNNotificationDisplay *display = [[IFNNotificationDisplay alloc] init];
	display.type = NotificationDisplayTypeText;
	[display setNotificationText:@"Failed To Post"];
	[display displayInView:self.view atCenter:CGPointMake(self.view.center.x, self.view.center.y-100.0) withInterval:1.5];
	[display release];

	//release the alloc'd post
	[_post release];
}

- (void) finishedPublishingPost:(FBFeedPost*) _post {

	UIView *dv = [self.view viewWithTag:NOTIFICATION_DISPLAY_TAG];
	[dv removeFromSuperview];

	IFNNotificationDisplay *display = [[IFNNotificationDisplay alloc] init];
	display.type = NotificationDisplayTypeText;
	[display setNotificationText:@"Finished Posting"];
	[display displayInView:self.view atCenter:CGPointMake(self.view.center.x, self.view.center.y-100.0) withInterval:1.5];
	[display release];

	//release the alloc'd post
	[_post release];
}

Posting to Facebook is easy as that! You can find all the code from this tutorial in the sample project .

Leave a Reply

Your email address will not be published. Required fields are marked *