A step-by-step tutorial: sample code included!
Context for this Tutorial
Carnival Craze is a 2D iPhone game we're submitting to the app store this week. It's programmed in Unity's 3D engine with an orthographic 2D camera. In the game, the Fortune Teller gives you any of a number of fortunes. You can post your favorite ones to your Facebook wall for all you friends to see. This tutorial shows how to do this, with all the code you need.
Writing to Player Prefs
In the game, each fortune has a button saying “POST THIS FORTUNE TO YOUR WALL.” In script, we write to PlayerPrefs when that button is pressed. We're naming this PlayerPref “Fortune” and the associated value is whatever the string currentFortune is holding. When the game is running on device, this adds a key-value pair to NSUserDefaults. The key is “Fortune” and the value is “Plan for many pleasures ahead.” in this instance.
var invisible : GUIStyle;
private var currentFortune : String = "Plan for many pleasures ahead.";
function OnGUI()
{
if( GUI.Button( Rect( 100, 260, 120, 40 ), "", invisible ))
{
PlayerPrefs.SetString("Fortune", currentFortune);
}
}
Observing NSUserDefaults in Objective-C
Set up KVO
Next, do some Key-Value Observing (KVO) as explained so well by TinyTim Games. First, build your Unity 3D project so you have an Xcode project to work with. In your Xcode project, open AppController.mm. This is where we hook into Unity's startup process. Find applicationDidFinishLaunching and insert KVO initialization code. The function now looks like this, since we're listening for the NSUserDefault (PlayerPref) labeled “Fortune”:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
printf_console("-> applicationDidFinishLaunching()\n");
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"Fortune" options:0 context:nil];
[self startUnity:application];
}
Right under applicationDidFinishLaunching, add the function (void)observeValueForKeyPath. This gets called when a NSUserDefault is set for “Fortune”. Since it will be fun to see this working, just log that we've gotten this far:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
NSLog( @"The PlayerPref: %@ was set form Unity script.\n", keyPath );
}
So to summarize, you have
- written to PlayerPrefs in script,
- added a NSUserDefaults observer and
- added the function to log confirmation that you've access objective-c from inside Unity. Run your project with the Xcode debugger console open and push the button that writes to PlayerPrefs. You should see the message you logged.
Read NSUserDefaults
If you haven't read from NSUserDefaults before, I like the simple, robust example from CocoaDev. Now the observer function looks like this:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
//Read NSUserDefault for this Key-Path
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *val = nil;
if (standardUserDefaults)
val = [standardUserDefaults objectForKey:keyPath];
NSLog( @"The PlayerPref: %@ was set from Unity script with the value: %@.\n", keyPath, val );
}
Run your program again and check what is logged in the debugger console.
Adding FacebookNext you need to setup a facebook application. It literally takes 5 minutes if you've done it before. If not, I really like Dan Grigsby's walkthrough on Mobile Orchard. After you have a Facebook application set up, you need to add the Facebook iPhone SDK to your Xcode project.
1. Download the Facebook iPhone SDK and unzip. Open the Xcode project in facebook-connect/src/.
2. Drag the folder “FBConnect” from that project into yours, but do not check the option to “Copy items...”. Make sure that is unchecked.
3. In Xcode, select from the menu Project > Edit Project Settings. Find the line to add a Header Search Path under the section “Search Paths”. Add the path to your Facebook iPhone SDK /src/ directory. If this is tricky for you, you can reference my setup:
- My Xcode project is in Documents/carnivalCraze/Carnival iPhone/Xcode Carnival/
- The path to my Facebook iPhone SDK is Documents/facebook-connect/
- So my new header search path is ../../../facebook-connect/src
4. Add the CoreGraphics framework to your project.
5. Add #import "FBConnect/FBConnect.h" to AppController.h and build your project. If you did everything right so far, you will be error free. If not, double-check your work so far.
Integrating Facebook
Next, you need to prompt the user to log into facebook. This is a one-time prompt that you can put anywhere in the game, and the login is cached. Since I want to wait to log the player into facebook until they attempt their first wall post, I'll fire the login code when they push the button to post.
First, you need the controller to implement the FBSessionDelegate and FBDialogDelegate protocols. You should create NSStrings for your Facebook API key and app secret. Also declare an FBSession pointer. Now the interface looks like this:
In AppController.h
@interface AppController : NSObject<UIAccelerometerDelegate, UIApplicationDelegate, FBSessionDelegate, FBDialogDelegate >
{
UIWindow* _window;
FBSession* session;
NSString* apiKey;
NSString* appSecret;
}
To keep your code clean, you should also add the function declaration:
- (void) postToFacebookWithKey: (NSString*) key andValue: (NSString*) val;
In AppController.mm
Define your API key and app secret. You find these through the Facebook Developer App by clicking on the app you setup earlier. You can sssign these in applicationDidFinishLaunching to keep everything in one place. The final version of this function looks like this:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
printf_console("-> applicationDidFinishLaunching()\n");
apiKey = @"YOUR API KEY HERE";
appSecret = @"YOUR APP SECRET HERE";
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"Fortune" options:0 context:nil];
[self startUnity:application];
}
Log into Facebook
After catching NSUserDefault in observeValueForKeyPath, call your new function:
[self postToFacebookWithKey: keyPath andValue: val];
That function definition looks like this:
- (void) postToFacebookWithKey: (NSString*) key andValue: (NSString*) val
{
session = [FBSession sessionForApplication: apiKey secret: appSecret delegate:self];
if ([session resume] == NO)
{
FBLoginDialog* dialog = [[[FBLoginDialog alloc] initWithSession: session] autorelease];
dialog.delegate = self;
[dialog show];
}
}
This code checks whether the users has logged into facebook yet, and if not, presents him with the dialog to do so.
Another requirement is to handle the callback when a login is successful. This is necessary since AppController implements the FBSessionDelegate protocol. Add the following right below postToFacebookWithKey...
- (void)session:(FBSession*)session didLogin:(FBUID)uid {
}
The last thing you need to do is cleanup, so add this to (void) dealloc in AppController.mm:
[session.delegates removeObject: self];
Run your project and enjoy seeing the Facebook login dialog pop up over the Unity canvas.
Post to Your Facebook Wall
You'll want to get very familiar with the documentation for FBConnect to understand better how the post works. If you're logged in, this sample code prompts you to post something to your wall. After adding you're own content, press run and see the successful culmination of all your work! Please add your comments about additions you'd like to see and any question you may have!
- (void) postToFacebookWithKey: (NSString*) key andValue: (NSString*) val
{
session = [FBSession sessionForApplication: apiKey secret: appSecret delegate:self];
if ([session resume] == NO)
{
FBLoginDialog* dialog = [[[FBLoginDialog alloc] initWithSession: session] autorelease];
dialog.delegate = self;
[dialog show];
}
else
{
NSString* name = @"Carnival Craze for iPhone";
NSString* itunesURL = @"http://itunes.com/apps/CarnivalCraze/";
NSString* caption = @"The Fortune Teller Says:";
NSString* description = val;//This holds the fortune to be posted.
NSString* imageSrc = @"http://store.chapbros.com/images/carnival/FT_128.jpg";
NSString* publishersName = @"Chapbros";
NSString* publishersURL = @"http://chapbros.com/";
FBStreamDialog* dialog = [[[FBStreamDialog alloc] init] autorelease];
dialog.delegate = self;
dialog.userMessagePrompt = @"What do you think of this Fortune?";
dialog.attachment = [NSString stringWithFormat: @"{\"name\":\"%@\","
"\"href\":\"%@\","
"\"caption\":\"%@\",\"description\":\"%@\","
"\"media\":[{\"type\":\"image\","
"\"src\":\"%@\","
"\"href\":\"%@\"}],"
"\"properties\":{\"brought to you by\":{\"text\":\"%@\",\"href\":\"%@\"}}}",
name, itunesURL, caption, description, imageSrc, itunesURL, publishersName, publishersURL];
[dialog show];
}
}
Love this!
Posted by: Tim Seymor | March 12, 2010 at 10:17 AM
This is going in all my Unity games now that it's succinctly laid out.
Posted by: PepperGum Games | March 15, 2010 at 08:37 AM
HI
Does this work for Unity iphone basic or only on the professional version?
Posted by: Mango | April 19, 2010 at 03:36 AM
This works with both Unity iPhone Basic and Advanced. This is meant particularly for iPhone Basic since iPhone Advanced doesn't need the NSUserDefaults work around to access objective-C calls.
Posted by: Ontario Britton | April 19, 2010 at 08:06 AM
I'm getting a error right at the end
I think i'm not to clear on where to place this line.
[self postToFacebookWithKey: keyPath andValue: val];
If anyone can help that would be awesome.
Posted by: ken | April 22, 2010 at 07:11 AM
postToFacebookWithKey should be called in observeValueForKeyPath when your NSUserDefault is set.
Posted by: Ontario Britton | April 22, 2010 at 12:56 PM
I'm sure I'm doing this all wrong . even after i moved it
I think I follow it pretty good up to Integrating Facebook section. Here is a zip of me trying it in a clean unity3d iphone project.
If anyone has time I would be greatful...
http://kennorman.com/files/example.zip
Posted by: ken | April 22, 2010 at 04:03 PM
you might have to click the link . highlight it and then hit enter... well that what i had to do :)
Posted by: ken | April 22, 2010 at 07:20 PM
Here are the two error to go with that file.
/Users/kennorman/facebook/facebookpush/build/facebookpush.app/facebookpush
Undefined symbols:
"_OBJC_CLASS_$_FBSession", referenced from:
__objc_classrefs__DATA@0 in AppController.o
"_OBJC_CLASS_$_FBLoginDialog", referenced from:
__objc_classrefs__DATA@0 in AppController.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
"_OBJC_CLASS_$_FBSession", referenced from:
__objc_classrefs__DATA@0 in AppController.o
"_OBJC_CLASS_$_FBLoginDialog", referenced from:
__objc_classrefs__DATA@0 in AppController.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Build failed (2 errors)
Posted by: ken | April 23, 2010 at 04:37 AM
Since you're getting a linker error when you try integrating facebook, I wonder if your header search path to facebook-connect is correct? I would try empirically recreating the example I give, to verify whether that was the problem. If so, then take some more time getting your search path pointed to the right directory level. This tripped me up a little at first too.
Posted by: Ontario Britton | April 23, 2010 at 03:23 PM
when I copied the FBConnect folder over I had to place it in the Classes folder for the import to find it and then i had to change the imports in alot of files .. I think this was the beginning of my down fall..
Add #import "FBConnect/FBConnect.h" to AppController.h
Posted by: ken | April 24, 2010 at 05:15 AM
hi!
im getting a error:
'val' was not declared in this scope
thanks in advance,
Joan
Posted by: joan | May 24, 2010 at 01:43 PM
done.
thanks anyway :)
Posted by: joan | May 25, 2010 at 12:42 PM
I wonder if the tips can also be done for iPhone 4... I'm so gonna try it!
Posted by: iPhone 4 addict | June 08, 2010 at 12:21 AM
"Another requirement is to handle the callback when a login is successful" My implementation seems to be failing here. After the user clicks the connect button I get a short message from FB that it's "redirecting". The user gets logged in but the post dialog doesn't appear. Note that if the user revisits my page the post dialog appears as it should (login being cached).
I've got the code in place, all looks good/works except callback/redirect is failing. just wondering if anyone had encountered this and what they did to resolve.
Thanks in advance.
Posted by: Purpleslurple | June 14, 2010 at 09:24 AM
ONTARIO BRITTON
Did you resolve your linker errors, i get the same 2 errors?
Cheers
Posted by: Opticfibre | June 18, 2010 at 06:31 AM
are the search paths in the Header Search Path or User Search Path.
If i remove my search path, the app wont compile at all saying FBconnect.h not found. 16 errors
Putting my search path back in compiles but for 3 errors
"_OBJC_CLASS_$_FBSession", referenced from:
__objc_classrefs__DATA@0 in AppController.o
"_OBJC_CLASS_$_FBStreamDialog", referenced from:
__objc_classrefs__DATA@0 in AppController.o
"_OBJC_CLASS_$_FBLoginDialog", referenced from:
__objc_classrefs__DATA@0 in AppController.o
ld: symbol(s) not found
These look like linker errors, but what to do?
Posted by: Opticfibre | June 18, 2010 at 06:37 AM
To whom it may concern, regarding the linker errors,
My problem was not reading the tutorial correctly, the tutorial says to drag the FBConnect folder FROM THE FACEBOOK XCODE PROJECT FOLDER.
I was dragging it from src/FBConnect which was causing the errors, my bad.
Thats a couple of days i could have gained back if i had of realised
Posted by: Opticfibre | June 18, 2010 at 10:33 AM
IPHONE 4 ADDICT
Yes im all working now except for the redirecting thing, and havinf to press the Facebook connect button again, and then being presented with the post message window?
Anyone.... anyone,,,, Bueller
Posted by: Opticfibre | June 18, 2010 at 11:01 AM
Thanks for this great post.
I am getting "Not Published Yet via Mobile MKAbeFook" this line at the bottom while posting feed in webview.
I want to change this line. I tried but still not able to solve it.
In your app you have edited text. Can you show me the good way to do this?
Please.
Posted by: Nsnarvekar | July 21, 2010 at 05:24 AM
A lot of people are having a hard time logging into facebook recently. Check out the articles and comments at facebook login, there's some really useful tips!
Posted by: Patrick | July 24, 2010 at 11:49 PM
I haven't tried this game but I believe this is a cool one for iPhone users. Thanks for sharing this.
Posted by: Free iPhone 4 | July 28, 2010 at 07:56 AM
Hello!
Fantastic tutorial, I was able to go through without any compiling errors. Great stuff!
Unfortunately, it seems that none of my posting to facebook show up on my facebook page. Does the facebook application need to be registered to work? (More than 5 users I think...).
Cheers and thanks again!
Posted by: fred | August 18, 2010 at 11:38 PM
Actually... it works! I was just posting at the wrong address!
Posted by: fred | August 19, 2010 at 12:12 AM
hiii
i am new in the field of unity development. i want implement the feature of facebook in my game but i am unable to understand the first step can u explain it in more detail like what is PlayerPref and where i have to add the following script:
var invisible : GUIStyle;
private var currentFortune : String = "Plan for many pleasures ahead.";
function OnGUI()
{
if( GUI.Button( Rect( 100, 260, 120, 40 ), "", invisible ))
{
PlayerPrefs.SetString("Fortune", currentFortune);
}
}
please help
Posted by: Piyushroongta | August 19, 2010 at 02:35 AM