A Facebook reusable class

In today’s post I’m going to share a piece of code that allows your games to easily connect to Facebook. It is a singleton class designed to be reusable, very simple to use and with only one task in mind: post new high scores on Facebook. You can find the example project source code at the end of the article.

The aim of this post is not to teach you about how to use the Facebook API to connect your iPhone games to Facebook. There are a lot of articles covering this topic and the official Facebook developers site. In this tutorial I would like to put the emphasis on the reusable and simplicity to use aspects. It is very easy to integrate in your projects the piece of code I’m going to show you.

Facebook icon_big

EDIT: This class has some issues pointed out by users at the comments section of this post. I have published an update called FacebookScorer that solves all the issues . You have the corresponding post here.

First, you need to download the last version of the Facebook iOS SDK from GitHub. Next, launch Xcode and create a new view based project. We will need to copy the Facebook API source files into our project. So, go to the “src” folder of the previously downloaded Facebook package and drag and drop all Facebook source files to your project. You will also need to include the JSON library source files.

You should now be able to build the project without issues.

Now we are going to create our FacebookHelper class. So, go to File/New File… and create a new NSObject based Ocjective-C class. I have named it “FacebookHelper”.

First, we are going to make it a singleton class. Paste this piece of code to your FacebookHelper.h file:

#import

@interface FacebookHelper : NSObject {

}

+ (FacebookHelper *) sharedInstance;

#pragma mark - Public Methods
// Public methods here.

@end

And paste this code to your FacebookHelper.m file:

#import "FacebookHelper.h"

@implementation FacebookHelper

#pragma mark -
#pragma mark Singleton Variables
static FacebookHelper *singletonDelegate = nil;

#pragma mark -
#pragma mark Singleton Methods

+ (FacebookHelper *)sharedInstance {
	@synchronized(self) {
		if (singletonDelegate == nil) {
			[[self alloc] init]; // assignment not done here
		}
	}
	return singletonDelegate;
}

+ (id)allocWithZone:(NSZone *)zone {
	@synchronized(self) {
		if (singletonDelegate == nil) {
			singletonDelegate = [super allocWithZone:zone];
			// assignment and return on first allocation
			return singletonDelegate;
		}
	}
	// on subsequent allocation attempts return nil
	return nil;
}

- (id)copyWithZone:(NSZone *)zone {
	return self;
}

- (id)retain {
	return self;
}

- (unsigned)retainCount {
	return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
	//do nothing
}

- (id)autorelease {
	return self;
}

#pragma mark - Public Methods

@end

It is a bunch of code but don’t worry about the details. It is only the way a singleton class is implemented in Objective-C.

So, for the moment it is only a generic singleton class. We need to add the desired protocols, attributes and methods. So, paste this code to your FacebookHelper.h file:

#import
#import "FBConnect.h"

@interface FacebookHelper : NSObject  {
    Facebook* _facebook;
    NSArray* _permissions;
}

@property(readonly) Facebook *facebook;

+ (FacebookHelper *) sharedInstance;

#pragma mark - Public Methods
// Public methods here.
-(void) login;
-(void) logout;
-(void) postToWallWithDialogNewHighscore:(int)highscore;

@end

As you can see, we only have 3 methods and only one is meaningful: postToWallWithDialogNewHighscore:

We will use this method to present the user a dialog that will allow him to post a new high score on Facebook and a short message.

Let’s implement those 3 methods. Add this code to your FacebookHelper.m file:

-(NSMutableDictionary*) buildPostParamsWithHighscore:(int)highscore {
    NSString *customMessage = [NSString stringWithFormat:kCustomMessage, highscore, kAppName];
    NSString *postName = kAppName;
    NSString *serverLink = [NSString stringWithFormat:kServerLink];
    NSString *imageSrc = kImageSrc;

    // Final params build.
    NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                   //@"message", @"message",
                                   imageSrc, @"picture",
                                   serverLink, @"link",
                                   postName, @"name",
                                   @" ", @"caption",
                                   customMessage, @"description",
                                   nil];

    return params;
}

-(void) login {
    // Check if there is a valid session.
    _facebook = [[Facebook alloc] initWithAppId:kAppId];
    _facebook.accessToken    = [[NSUserDefaults standardUserDefaults] stringForKey:@"AccessToken"];
    _facebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:@"ExpirationDate"];
    if (![_facebook isSessionValid]) {
        [_facebook authorize:_permissions delegate:self];
    }
    else {
        [_facebook requestWithGraphPath:@"me" andDelegate:self];
    }
}

-(void) logout {
    [_facebook logout:self];
}

-(void) postToWallWithDialogNewHighscore:(int)highscore {
    NSMutableDictionary* params = [self buildPostParamsWithHighscore:highscore];

    // Post on Facebook.
    [_facebook dialog:@"feed" andParams:params andDelegate:self];
}

We use a helper method to build the parameters dictionary: buildPostParamsWithHighscore:

In this method we use a bunch of constants that are used to build the template message that we are going to post to the user’s Facebook wall. So let’s add this constants to our FacebookHelper.h file for easy access and customization. Add this code to the FacebookHelper.h file:

#define kAppName        @"Your App's name"
#define kCustomMessage  @"I just got a score of %d in %@, an iPhone/iPod Touch game by me!"
#define kServerLink     @"http://indiedevstories.com"
#define kImageSrc       @"http://image_url.jpg"

If you now try to build the project you will get an error because the App ID is still not defined. To get a Facebook App ID you will need to register an app on the developers site (the process of app registration is out of the scope of this tutorial). Then, pick up the App ID and add this code to the top of your FacebookHelper.m file, just before the implementation of the class:

static NSString* kAppId = @"000000000000000";

We also need to implement the “init” method in FacebookHelper.m:

- (id)init {
    if (!kAppId) {
        NSLog(@"missing app id!");
        exit(1);
        return nil;
    }

    if ((self = [super init])) {
        _permissions =  [[NSArray arrayWithObjects: @"read_stream", @"publish_stream", @"offline_access",nil] retain];
    }

    return self;
}

Ok, our basic implementation of the FacebookHelper class has been completed. However, there are still two details that need to be addressed. First, add this method to your AppDelegate.m file:

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    return [[[FacebookHelper sharedInstance] facebook] handleOpenURL:url];
}

Remember to include the FacebookHelper.h. Now we need to configure the “Info.plist” file. Add “URL Types” array as shown on the image below:

plist Facebook

Ok, now we need a way to test our new created class. Paste the following code in a button callback method or wherever you want:

[[FacebookHelper sharedInstance] login];
[[FacebookHelper sharedInstance] postToWallWithDialogNewHighscore:123];

And that’s it! You should see a dialog similar to the following one. If you want to have control over the life cycle of  connections and request to Facebook you should add the corresponding delegate methods and their implementations.

Facebook Post

Here you have the source code. Hope you find it useful!

This post is part of iDevBlogADay, a group of indie iPhone development blogs featuring two posts per day. You can keep up with iDevBlogADay through the web siteRSS feed, or Twitter.

27 thoughts on “A Facebook reusable class

  1. Hi again Toni,

    Made a slight addition since the “dialogDidComplete:” callback is fired when the user chooses “Submit” or “Cancel” and is not fired if the user taps the tiny “x” up in the corner. I am tracking posts and having it count on Submit and Cancel isn’t what I was looking for. After a bit of testing I found the callback below can be used reliably and a check of the length of the returned url is a quick and easy way to suit my needs. Here is the method I added to FacebookHelper.m

    – (void)dialogCompleteWithUrl:(NSURL *)url {
    BOOL didSubmit = ([[NSString stringWithFormat:@”%@”,url] length] > 30);
    if (didSubmit) {
    // this is my rewards tracking and should only be called if the user actually posted
    [[MWgameManager sharedManager] incrementTrackingID:gcWallPost WithInt:1];
    NSLog(@”DidSubmitPost”);
    }
    }

    Note: the length of the actual “Cancel” url is 23 but a small buffer shouldn’t hurt since a “Submit” url includes the post_id and is itself 31 characters long.

    Thanks again
    Greg

  2. Hello Greg,

    Thank you very much for your feedback and contribution. It’s great! :)

    The class was not intended to have this level of control. I don´t really care about wether the user actually posts the score on Facebook. However, looking at your code, I guess that you are tracking the number of posts the user is submitting to Facebook (maybe an achievement? :p). So, you need a way to be sure about whether a post has been posted or not.

    Thanks again!

  3. I like your Tutorial, I’m new to Xcode so I’m sorry if my question sounds stupid but I need you help, how can I point the source of the image in
    #define kImageSrc @”http://image_url.jpg”
    to a image that is inside my project in Xcode? lets say the same image I use for my app Icon.
    and what’s
    #define kServerLink @”http://indiedevstories.com”
    needed for? do I need to have a kServerLink? I don’t have a web site so what can I use insted?
    Thanks.

  4. Hello!
    kImageSrc must an URL on internet. The reason is that it is Facebook who actually is showing the dialog view. So the image needs to be loaded from Internet.

    kServerLink is supposed to be your web page or your game’s webpage. But you can leave it blank.

    Thanks for your comment!

  5. Hi Toni, you know I’m trying to incorporate your sample code to my app, but I’m getting an error in the FacebookHelper.m file in my project at the code bellow:
    -(void) login {
    // Check if there is a valid session.
    _facebook = [[Facebook alloc] initWithAppId:kAppId];
    _facebook.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:@”AccessToken”];
    _facebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:@”ExpirationDate”];
    if (![_facebook isSessionValid]) {
    [_facebook authorize:_permissions delegate:self];
    }
    else {
    [_facebook requestWithGraphPath:@”me” andDelegate:self];
    }
    }

    on the
    _facebook = [[Facebook alloc] initWithAppId:kAppId];
    it gives me a SIGABRT error
    no ‘-initWithAppId’ method found, ‘Facebook’ may not respond to ‘-authorize delegate:’
    local declaration of _permissions hides instance variable

    and a warning on
    [_facebook authorize:_permissions delegate:self];
    instance method ‘-authorize:delegate:’ not found (return type default to ‘id’)

    I’ve been hitting my head against the wall trying to figure what this means and how can I fix it but can’t so maybe you can give me an idea, I’m using Xcode 4. your sample is what I need in my app but can’t get it to work.
    Thank you.

    • Never mind I got my error in the kAppId I had it as AppId in the Facebook.m/h but is it just me or this captcha security screen never loads? It work before but now it never loads an image so I can’t enter those letters or words! :(

  6. I still get the error

    no ‘-initWithAppId’ method found, ‘Facebook’ may not respond to ‘-authorize delegate:’

    looks like we’re missing the andDelegate parameter? Any suggestions would be welcome, because otherwise this looks like a great bit of code. I’m using Xcode4.

  7. Hey Toni, I am also getting the same warning as David Jennings when I try to implement it to my project.

    Your sample code doesn’t have this for some reason.

    • I fixed the error above now i’m getting a warning:
      [_facebook authorize:_permissions delegate:self];
      instance method ‘-authorize:delegate:’ not found (return type default to ‘id’)

    • Actually I just imported what you had over to my project and it came with those warnings.

      this line:
      [_facebook authorize:_permissions delegate:self];

      had to be replaced with this:
      [_facebook authorize:_permissions delegate:self localAppId:kAppId];

      i haven’t tried if it works or not. I’m not sure why it won’t let me use the first line, even though it’s in facebook.m.

      actually just retyped it and it no longer gives me that warning i just mentioned.

      In fact even my previous warning with this line (Same warning as david jennings):
      _facebook = [[Facebook alloc] initWithAppId:kAppId];

      isn’t being picked up anymore.. how strange. I’ve been working on my other part of my app and haven’t touched it since my last post which has been 12+ hours.

      very strange =/.

  8. I got it! my mistake with the last number of my api ID. My mistake, sorry.

    Anyway I have some drawbacks using your sample.

    1 – I press Facebook support button. (OK)
    2 – It loads Facebook app and display the typical screen to logging from the iOS app (OK)
    3 – Accept (WRONG)
    4 – I click the home button to enter again to your sample app (WRONG)
    5 – I dialog to allow me to post to my wall is display (OK)

    I don’t understand the need of having to do step 3 and 4 to get it work.

    Any ideas?

    Thanks!

  9. Pingback: Facebook SDK Singleton in xcode auto login before post? | PHP Developer Resource

  10. Pingback: Facebook SDK Singleton in xcode | PHP Developer Resource

  11. Hi Tony,

    You saved my week (not my day) … thank you … Facebook this SDK was a black box to me. I made several attempts to build a singleton for that, but without success. Now, as we speak here in Brazil: it’s just going to hug!

    • Hi Toni,

      Thank’s …

      But now I have problems if the user has a Facebook app on your device … login does not work … I am trying to solve this, but if you have any idea what to do, it would be great to know … :)

      • Should work with the Facebook app as well.

        Ensure you added correctly your app ID both in FacebookScorer.m and your main plist file.

        Also ensure you have added correctly the url methods on your app delegate.

        HTH

  12. Thanks man ! your tutorial is the best so far i had found and it worked like a charm…..thanks a lot keep up the sharing work….

  13. Pingback: Facebook SDK Singleton in xcode auto login before post? - Objective-C Solutions - Developers Q & A

Leave a Reply