Saturday 10 November 2012

Add Facebook API into iphone Application .....


This multi-part tutorial walks you through building a Facebook integrated iOS app. You'll create a timeline app that lets people post about meals they ate.
This sample app is based on the Scrumptious sample app bundled with the SDK. You can use the completed sample as a reference or jump to a specific step to see how to implement a specific feature.

To complete the tutorial, you'll need a familiarity with Objective-C, Xcode and some common frameworks like CoreLocation. Before you start you should install the Facebook SDK for iOS, create your Facebook App, and start a new Xcode project as described in Getting Started with the iOS SDK.
Note: Use the following settings in your Xcode project options, so you can match this tutorial:

Once you've done that, work through the following steps of the tutorial:
  • Authenticate: Implement Login with Facebook, ask the user for the permissions your app needs, handle session changes, and log out the user.
  • Personalize: Personalize the experience for the user with their profile picture and name when they login to your app.
  • Show Friends: Display the user's friends and let them select one or more friends.
  • Show Nearby Places: Display a list of nearby places and let the user tag where they're currently located.
  • Publish an Open Graph Action: Publish activity from your app to timeline and news feed — and set up the back-end server for Open Graph objects.
By the end of this tutorial, you should have a working knowledge of how to authenticate, personalize and make any app social. Let's start the tutorial.

1 - Authenticate

This tutorial walks you through how to authenticate a user with the Facebook SDK for iOS.
We'll introduce you to one of the Facebook SDK's core objects: FBSession. This object is used to authorize a user, manage the Login with Facebook flow and manage the session. Any Facebook API calls that require an authenticated user need to useFBSession. While an app can create instances of FBSession and manage them itself, FBSession provides helpers to simplify the common scenario of an app having a single logged-in user at a time. We'll use those helpers here. Here's the basic authorization flow:
  • Call the openActiveSessionWithReadPermissions:allowLoginUI:completionHandler: class method on the FBSession class, specifying the read permissions your app needs and providing a callback handler that will be notified of session changes in the future.
  • Handle the incoming link from the Facebook App.
  • Implement the successful login or error conditions in your open session callback handler.
This tutorial walks through the following:
Note: Before you start this tutorial, make sure you've setup your basic Facebook App.

Step 1: Set Up the User Interface

Our basic authentication flow involves two view controllers. The first one, SCViewController, will be active when the user is logged in. The second view controller, SCLoginViewController, will show up if the user is not logged in. The login view controller displays the login UI, initiates the authentication and passes control to SCViewController.
First, copy the image assets you'll use in the tutorial. Many of these assets appear in the login flow, so you can copy them now. The image assets can be found in the completed sample packaged with the Facebook SDK for iOS. They consist of images used in the app and app icon images. Images used in the app can be found in the images folder under~/Documents/FacebookSDK/Samples/Scrumptious/scrumptious. Copy these by dragging the images folder into your Xcode project:
App icon images can be found under the ~/Documents/FacebookSDK/Samples/Scrumptious folder. Copy these images into your Xcode project.
Next, create a new view controller by right-clicking on the ''Scrumptious'' folder > New File > Objective-C class, and name the class ''SCLoginViewController''. Make sure to generate the corresponding NIB file as well:
Now, set up the SCLoginViewController.xib file as follows:
  • View: Change the main view's background color to 003366.
  • App Logo: Add an Image View object with the following properties: X, Y (offset): 20, 26; Width, Height (size): 74, 74; Image: Icon-72.png; Mode: center; Background: white
  • App Title: Add a Label object with the following properties: X, Y (offset): 107, 14; Width, Height (size): 193, 86; Text: ''Scrumptious''; Text font: Zapfino 28.0; Text color: 66CCFF;
  • Intro: Add a Label object with the following properties: X, Y (offset): 73, 137; Width, Height (size): 174, 59; lines: 0; Text: ''To get started, login using Facebook''; Alignment: center; Text color: FFFFFF;
  • Login Button: Add a Round Rect Button object. Center the button below the Intro text and modify the following properties: Title: ''Login''
  • Spinner: Add an Activity Indicator View object and center it under the login button. Modify the following properties: Hidden: YES; Hides When Stopped: YES.
Feel free to use your own colors and fonts. When you're done, your UI in Interface Builder should look like this:


Step 2: Wire Up the Authentication Logic

Before wiring up the login flow, you should understand the flow you're implementing. Initiate the login flow when the user taps the login button in the SCLoginViewController view. Once the user is successfully logged in, display theSCViewController. As a best practice, whenever the app starts, check for a previously stored session and show the logged-in versus logged-out UI.
In terms of how the views are managed, your main view controller will be SCViewController. Show theSCLoginViewController as a modal whenever you want to display the login UI. The app delegate contains most of the code that hands off control to the correct view controller.
With that background, let's get into the authentication logic.

Step 2a: Include the Facebook SDK

Open up the app delegate implementation file and include the Facebook SDK:
#import <FacebookSDK/FacebookSDK.h>
You can now reference the SDK functionality in your code.

Step 2b: Show the Login View

Now in the app delegate implementation file, import the SCLoginViewController header:
#import "SCLoginViewController.h"
As you'll present the login view through a modal, SCViewController needs to be part of a UINavigationController. So, first define a private property for the new navigation controller by adding the following code before the SCAppDelegateimplementation code:
@interface SCAppDelegate ()

@property (strong, nonatomic) UINavigationController* navController;

@end
Synthesize this new property:
@synthesize navController = _navController;
By default, Xcode creates a public property named viewController for the SCViewController instance. Let's rename this to mainViewController and make it private. To do this, first remove this declaration from the app delegate header file:
@property (strong, nonatomic) SCViewController *viewController;
Then, add a new SCViewController property in the app delegate private interface with the modified name:
@property (strong, nonatomic) SCViewController *mainViewController;
Next, modify the synthesized property to have the new name by changing:
@synthesize viewController = _viewController;
to:
@synthesize mainViewController = _mainViewController;
Then, make SCViewController the root view controller for this navigation controller. In theapplication:didFinishLaunchingWithOptions: method, change this code:
self.viewController = [[SCViewController alloc] 
                          initWithNibName:@"SCViewController" bundle:nil];
self.window.rootViewController = self.viewController;
to:
self.mainViewController = [[SCViewController alloc] 
                              initWithNibName:@"SCViewController" bundle:nil];
self.navController = [[UINavigationController alloc]
                         initWithRootViewController:self.mainViewController];
self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];
Now you can put the flow together. First, define a private method in the app delegate that shows the login view:
- (void)showLoginView 
{
    UIViewController *topViewController = [self.navController topViewController];

    SCLoginViewController* loginViewController = 
    [[SCLoginViewController alloc]initWithNibName:@"SCLoginViewController" bundle:nil];
    [topViewController presentModalViewController:loginViewController animated:NO];
}
Finally, in the application:didFinishLaunchingWithOptions: method, before the return statement check the Facebook session before displaying the correct UI:
// See if we have a valid token for the current state.
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
    // To-do, show logged in view
} else {
    // No, display the login page.
    [self showLoginView];
}
Build and run the project to make sure it runs without errors. You should see the login screen, but login button won't work yet. Next, you'll add logic to handle the button click and kick off the authentication flow.

Step 2c: Add the Login Action and Handler

Wire up the SCLoginViewController UI so that when the user clicks the login button, they're taken through the authorization flow. The UI you created includes a login button and an activity indicator. Attach the login button to an actionto initiate the autorize flow. Then, attach the activity indicator to an outlet and animate it during the authorization flow. The main logic for the authorization flow is in the app delegate file.
First, in SCLoginViewController.xib attach an action to the login button and call this performLogin. Next attach anoutlet to the activity indicator and call it spinner. Add the action and outlet to the implementation file (not the header file):

You should now have an empty performLogin: method in the SCLoginViewController implementation file.
Now, switch to the app delegate implementation file. Define a public method openSession that will be called when someone clicks the login button. This method will invoke the FBSession class methodopenActiveSessionWithReadPermissions:allowLoginUI:completionHandler: that initiates the authentication flow. The handler you define for the SDK authorization call, sessionStateChanged:state:error:, will be responsible for handling the success and error scenarios. It will also publish an app-wide notification when the session's state changes, so other parts of the app can react without hard-coding that dependency in the session state handler. Add these methods:
- (void)sessionStateChanged:(FBSession *)session 
                      state:(FBSessionState) state
                      error:(NSError *)error
{
    switch (state) {
        case FBSessionStateOpen: {
                UIViewController *topViewController = 
                    [self.navController topViewController];
                if ([[topViewController modalViewController] 
                    isKindOfClass:[SCLoginViewController class]]) {
                    [topViewController dismissModalViewControllerAnimated:YES];
                }
            }
            break;
        case FBSessionStateClosed:
        case FBSessionStateClosedLoginFailed:
            // Once the user has logged in, we want them to 
            // be looking at the root view.
            [self.navController popToRootViewControllerAnimated:NO];
            
            [FBSession.activeSession closeAndClearTokenInformation];
            
            [self showLoginView];
            break;
        default:
            break;
    }

    if (error) {
        UIAlertView *alertView = [[UIAlertView alloc]
                     initWithTitle:@"Error"
                           message:error.localizedDescription
                          delegate:nil
                 cancelButtonTitle:@"OK"
                 otherButtonTitles:nil];
        [alertView show];
    }    
}

- (void)openSession
{
    [FBSession openActiveSessionWithReadPermissions:nil
                                       allowLoginUI:YES
                                  completionHandler:
                                       ^(FBSession *session, 
                                         FBSessionState state, NSError *error) {
                  [self sessionStateChanged:session state:state error:error];
    }];    
}
As a best practice, whenever the user opens the app, check for a cached session and go through a session check. You can call the openSession method to handle this. Modify the application:didFinishLaunchingWithOptions: method to add this call:
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
    // Yes, so just open the session (this won't display any UX).
    [self openSession];
} else {
    // No, display the login page.
    [self showLoginView];
}
Now, add the openSession method to the app delegate header SCAppDelegate.h so it can be called from the login view controller:
- (void)openSession;
Next, switch to the login view controller implementation file, SCLoginViewController.m. Import the app delegate header so you can invoke the authorization methods you defined:
#import "SCAppDelegate.h"
Now add the following logic to the performLogin: method to implement login by calling the method in the app delegate file:
- (IBAction)performLogin:(id)sender 
{
    [self.spinner startAnimating];
    
    SCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate openSession];
}
If there is an issue during the authorization flow, for example if the user cancels the authorization flow, you'll want to do some cleanup, like stopping the activity animation. To do this, define a login failed cleanup method:
- (void)loginFailed
{
    // User switched back to the app without authorizing. Stay here, but
    // stop the spinner.
    [self.spinner stopAnimating];
}
Declare this method in the SCLoginViewController header file so the app delegate can call it:
- (void)loginFailed;
If the login fails or is canceled, the user will see the login view through a call to the showLoginView defined in the app delegate file. To cleanup any login flow UI like the spinner animation, modify the showLoginView method in the app delegate to call the loginFailed method in the SCLoginViewController view controller:
- (void)showLoginView 
{
    UIViewController *topViewController = [self.navController topViewController];
    UIViewController *modalViewController = [topViewController modalViewController];
    
    // If the login screen is not already displayed, display it. If the login screen is 
    // displayed, then getting back here means the login in progress did not successfully 
    // complete. In that case, notify the login view so it can update its UI appropriately.
    if (![modalViewController isKindOfClass:[SCLoginViewController class]]) {
        SCLoginViewController* loginViewController = [[SCLoginViewController alloc]
            initWithNibName:@"SCLoginViewController" 
                     bundle:nil];
        [topViewController presentModalViewController:loginViewController animated:NO];
    } else {
        SCLoginViewController* loginViewController = 
            (SCLoginViewController*)modalViewController;
        [loginViewController loginFailed];
    }
}
You now have most of the authorization logic complete. Next, you'll add logic to handle callback URLs from the Facebook App.

Step 2d: Handle the Login with Facebook Callback

During the Login with Facebook flow, your app passes control to the Facebook iOS app or Facebook in mobile Safari. After the user is authenticated, your app will be called back with the session information. In the app delegate, implement theapplication:openURL:sourceApplication:annotation: delegate method to call the Facebook session object that handles the incoming URL:
- (BOOL)application:(UIApplication *)application 
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication 
         annotation:(id)annotation 
{
    return [FBSession.activeSession handleOpenURL:url]; 
}
The flow back to your app may be interrupted (for ex: if the user clicks the Home button if authenticating via the Facebook for iOS app). If this happens, the Facebook SDK can take care of any cleanup that may include starting a fresh session. To enable this, in the applicationDidBecomeActive: delegate method call the active session'shandleDidBecomeActive method:
// We need to properly handle activation of the application with regards to SSO
// (e.g., returning from iOS 6.0 authorization dialog or from fast app switching).
[FBSession.activeSession handleDidBecomeActive];
Build and run the project to make sure it runs without errors. You should see the login screen. When you click the Login button, you should see the permissions dialog. Once you authorize the app, you should see a blank screen.
Check that the session caching works correctly by stopping the app, ending any running sessions and relaunching the app. You should see the blank screen that signifies a logged in user.

Step 3: Add the Logout Flow

Give your users a way to logout from Facebook and clear their session information. To do this, add a button to the main view controller and call a new method to logout the user.
Open up the SCViewController.m file where you'll build the logged-in UI. Import the Facebook SDK header:
#import <FacebookSDK/FacebookSDK.h>
Next, create a new method that will close the current session and log the user out:
-(void)logoutButtonWasPressed:(id)sender {
    [FBSession.activeSession closeAndClearTokenInformation];
}
Next, add a logout button to the right bar of the navigation that calls the logoutButtonWasPressed: you defined. Add this logout button code at the end of the viewDidLoad method of SCViewController:
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] 
                                    initWithTitle:@"Logout"
                                    style:UIBarButtonItemStyleBordered
                                    target:self
                                    action:@selector(logoutButtonWasPressed:)];
Build and run the project to make sure it runs without errors. After logging in, you should now see a ''Logout'' button on the top right. When you click it, you should be taken back to the login view.

Next Steps

Learn how to personalize your app for a logged-in user.

2 - Personalize

This tutorial outlines how to personalize your app experience with the Facebook SDK for iOS by displaying the user's profile picture and name when they've authenticated.
This tutorial introduces you to the Facebook SDK FBRequest object. Use this object to construct requests that make Facebook API calls. If you're making requests that require a user to be authenticated, you'll need to ensure that theactiveSession on FBSession has been opened, or else provide an explicit FBSession instance to the FBRequestcalls.
We'll walk through making an API call to get the user's basic information to add a personalization touch to your app. The Facebook SDK provides a static FBRequest class method called requestForMeWithSession that returns an FBRequestobject that you can use to make an API call to return user data. The user data will be a strongly typed FBGraphUser object that represents a Facebook user.
Once you've constructed an FBRequest object, you can initiate the API call by invoking thestartWithCompletionHandler: method. The method requires you to pass in a block callback handler of typeFBRequestHandler, that will be called when the request is successful, canceled or produces an error. The request callback handler will be invoked with any result and any error data from the request.
Use the FBProfilePictureView Facebook SDK view to display the user's profile picture once the user API request returns successfully. This specialized view takes in properties that represent the user's ID and a desired picture size to format the display.
This tutorial walks through the following:
Note: Before you start personalizing your app, make sure you've set up authentication.

Step 1: Set Up the User Interface

Add the personal information to the SCViewController that displays when the user logs in. Make these changes inSCViewController.xib:
  • View: Change the main view's background color to FFFFFF.
  • Profile Image: Add a View object with the following properties: X, Y (offset): 20, 17; Width, Height (size): 75, 75; Autosizing: flexible right margin, flexible bottom margin; Class: FBProfilePictureView
  • User's Name: Add a Label object with the following properties: X, Y (offset): 104, 17; Width, Height (size): 210, 21; Text: none
When you're done, your UI in Interface Builder should look like this:
Next, wire up the profile image view and the user name label to the SCViewController implementation file:
  • Create an outlet for the Profile Image View object called userProfileImage.
  • Create an outlet for the Profile Image View object called userNameLabel.


Step 2: Get and Display the User's Information

To display the user's personal information, make a call to the requestForMeWithSession method of the FBRequestobject. Define a new method to do this:
- (void)populateUserDetails 
{
    if (FBSession.activeSession.isOpen) {
        [[FBRequest requestForMe] startWithCompletionHandler:
         ^(FBRequestConnection *connection, 
           NSDictionary<FBGraphUser> *user, 
           NSError *error) {
             if (!error) {
                 self.userNameLabel.text = user.name;
                 self.userProfileImage.profileID = user.id;
             }
         }];      
    }
}
If the call is successful, you can set the user's name and show the user's profile picture by setting the user ID property on theFBProfilePictureView object.
Next, call the populateUserDetails method when you want to show the logged-in state by modifying theviewWillAppear: method.
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    if (FBSession.activeSession.isOpen) {
        [self populateUserDetails];
    }
}
You want to make sure that the data displayed is updated any time the user logs in or out from Facebook. Use theNSNotificationCenter to get notified when the state of the active session changes.
Declare a string constant for the name of our new notification by adding the following to the top of the app delegate header file:
extern NSString *const SCSessionStateChangedNotification;
Next, in the app delegate implementation file, add the following at the top of the file:
NSString *const SCSessionStateChangedNotification = 
    @"com.facebook.Scrumptious:SCSessionStateChangedNotification";
Next, send the notification whenever the session state changes by adding the following to thesessionStateChanged:state:error: method, just before the line that reads if (error) {:
[[NSNotificationCenter defaultCenter] 
    postNotificationName:SCSessionStateChangedNotification 
                  object:session];
Switch to the SCViewController.m file to hook up a listener for the notification. Import the app delegate header so you can refer to the notification variables:
#import "SCAppDelegate.h"
Next, add the following at the bottom of the viewDidLoad method:
[[NSNotificationCenter defaultCenter] 
    addObserver:self 
       selector:@selector(sessionStateChanged:) 
           name:SCSessionStateChangedNotification
         object:nil];
Then add the following to viewDidUnload:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Add a simple notification handler that just updates the UI any time the session state changes. Add the following method that was referenced above:
- (void)sessionStateChanged:(NSNotification*)notification {
    [self populateUserDetails];
}
If you run the project at this point, you may see an error like this:
2012-05-18 04:04:11.620 Scrumptious[6548:f803] Unknown class 
FBProfilePictureView in Interface Builder file.
To resolve this, add this as the first line in the app delegate's application:didFinishLaunchingWithOptions:method:
[FBProfilePictureView class];
Build and run the project to make sure it runs without errors. Once you're authenticated, you should see your profile picture and your name.

Next Steps

Learn how to show the user's friends in your app to make it a more social experience.



3 - Show Friends

This tutorial outlines how to show a user's friends in your app with the Facebook SDK for iOS. This makes your app more engaging and feel more personal. To illustrate where friends come in the picture, let's look at the test app you're building.

The finished app will allow the user to select one or more friends, tag a current location, take a photo and publish a story with these details on timeline and news feed. This tutorial covers the friend selector flow. When you've finished these steps, your flow should look like this:
We'll walk you through setting up the home menu selection and implementing the friend selector flow.
To display the friend selector, use the FBFriendPickerViewController Facebook SDK object. To use this object, first initialize an instance through the initWithNibName:bundle: method. Once you have an instance, set the delegatemethod to define the object that will handle the FBFriendPickerDelegate notifications. Finally, call the loadData to initiate the API call to get the user's friends. You can then display the FBFriendPickerViewController to show the friend selector.
In this tutorial, you'll implement the FBFriendPickerDelegate methodfriendPickerViewControllerSelectionDidChange: that is notified when a user selects a friend. That delegate method will be passed the FBFriendPickerViewController instance and you'll use the selection property to retrieve the selected friends.
This tutorial walks through the following:
Note: Before you start this tutorial, make sure you've personalized your app.

Step 1: Set Up the User Interface

The UI you set up in this step corresponds to the home menu selection where one of the menu items triggers the friend selector display. The friend selector display is implemented programmatically.
Add the home menu in the SCViewController that is displayed when the user is authenticated. Make the following changes in SCViewController.xib:
  • Home Menu: Add a Table View object with the following properties: Style: Grouped; Show Selection on Touch: NO; Scrolling Enabled: NO; Background: FFFFFF; Section Height Header: 10; Section Height Footer: 10; X, Y (offset): 0, 118; Width, Height (size): 320, 342; Autosizing: flexible bottom margin;
When you're done, your UI in Interface Builder should look like this:

Next, wire up the menu table to the SCViewController implementation file:
  • Create an outlet for the Table View object and call it menuTableView.
  • Connect the dataSource outlet to the File's Owner.
  • Connect the delegate outlet to the File's Owner.

Step 2: Set Up The Menu Display

Now add code to show the menu in the table view that was set up in SCViewController.xib.
First, in the SCViewController implementation file, add UITableViewDataSource and UITableViewDelegate to the list of protocols it conforms to:
@interface SCViewController ()
<UITableViewDataSource,
UITableViewDelegate> 
Next, add the UITableView data source and delegate methods.
The data in the menu table is defined as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView 
      cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = (UITableViewCell*)[tableView 
        dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
            reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        
        cell.textLabel.font = [UIFont systemFontOfSize:16];
        cell.textLabel.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
        cell.textLabel.lineBreakMode = UILineBreakModeTailTruncation;
        cell.textLabel.clipsToBounds = YES;

        cell.detailTextLabel.font = [UIFont systemFontOfSize:12];
        cell.detailTextLabel.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
        cell.detailTextLabel.textColor = [UIColor colorWithRed:0.4 
                                                         green:0.6
                                                          blue:0.8 
                                                         alpha:1];
        cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
        cell.detailTextLabel.clipsToBounds = YES;
    }
    
    switch (indexPath.row) {
        case 0:
            cell.textLabel.text = @"What are you eating?";
            cell.detailTextLabel.text = @"Select one";
            cell.imageView.image = [UIImage imageNamed:@"action-eating.png"];
            break;
            
        case 1:
            cell.textLabel.text = @"Where are you?";
            cell.detailTextLabel.text = @"Select one";
            cell.imageView.image = [UIImage imageNamed:@"action-location.png"];
            break;
            
        case 2:
            cell.textLabel.text = @"With whom?";
            cell.detailTextLabel.text = @"Select friends";
            cell.imageView.image = [UIImage imageNamed:@"action-people.png"];
            break;
            
        case 3:
            cell.textLabel.text = @"Got a picture?";
            cell.detailTextLabel.text = @"Take one";
            cell.imageView.image = [UIImage imageNamed:@"action-photo.png"];
            break;
            
        default:
            break;
    }

    return cell;
}
The menu table consists of one section with four menu items:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
    return 4;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
    return 1;
}
For now, set things up so tapping on any row does nothing. Wire up the friend selector after verifying the menu displays correctly.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Do nothing for now
}
You may have noticed that you have not set a title for the SCViewController view. Let's take care of that now. Add a title by adding the following statement at the end of the viewDidLoad method:
self.title = @"Scrumptious";
Build and run the project to make sure it runs without errors. Once you're authenticated, you should see the home menu selection list that includes the friend selector option.

Step 3: Show the Friend Selector

Create a property to hold the friend picker view controller that displays the friend selector:
@property (strong, nonatomic) FBFriendPickerViewController *friendPickerController;
Synthesize the new property:
@synthesize friendPickerController = _friendPickerController;
Next, make sure this view is released when the viewDidUnload method. Add this to the end of the method:
self.friendPickerController = nil;
Modify the tableView:didSelectRowAtIndexPath: delegate method to set up the friend selector using theFBFriendPickerViewController Facebook SDK control:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    switch (indexPath.row) {
        case 2:
            if (!self.friendPickerController) {
                self.friendPickerController = [[FBFriendPickerViewController alloc] 
                    initWithNibName:nil bundle:nil];
                self.friendPickerController.title = @"Select friends";
            }

            [self.friendPickerController loadData];
            [self.navigationController pushViewController:self.friendPickerController 
                animated:true];
    }
}
Build and run the project to make sure it runs without errors. Once you see the home menu selection, select the ''With whom?'' menu option. You should see a list of your friends to choose from. Selecting a friend and tapping the Back button should do nothing at this point. You will add the functionality that saves the selected friend next.

Step 4: Display Selected Friends

First, add FBFriendPickerDelegate to the list of protocols that the SCViewController conforms to.
Modify the tableView:didSelectRowAtIndexPath: method to set the delegate that will handle the friend selector:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    switch (indexPath.row) {
        case 2:
            if (!self.friendPickerController) {
                self.friendPickerController = [[FBFriendPickerViewController alloc] 
                    initWithNibName:nil bundle:nil];

                // Set the friend picker delegate
                self.friendPickerController.delegate = self;

                self.friendPickerController.title = @"Select friends";
            }

            [self.friendPickerController loadData];
            [self.navigationController pushViewController:self.friendPickerController 
                animated:true];
            break;
    }
}
When the view is deallocated, nil out the friend picker delegate. Add the dealloc method to do this:
- (void)dealloc
{
    _friendPickerController.delegate = nil;
}
Create a property to hold the friend selections.
@property (strong, nonatomic) NSArray* selectedFriends;
Synthesize:
@synthesize selectedFriends = _selectedFriends;
Display any selected friends in the friend selector menu option. Add two new helper methods that you will use to update the display:
- (void)updateCellIndex:(int)index withSubtitle:(NSString*)subtitle {
    UITableViewCell *cell = (UITableViewCell *)[self.menuTableView 
        cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
    cell.detailTextLabel.text = subtitle;
}

- (void)updateSelections 
{

    NSString* friendsSubtitle = @"Select friends";
    int friendCount = self.selectedFriends.count;
    if (friendCount > 2) {
        // Just to mix things up, don't always show the first friend.
        id<FBGraphUser> randomFriend = 
            [self.selectedFriends objectAtIndex:arc4random() % friendCount];
        friendsSubtitle = [NSString stringWithFormat:@"%@ and %d others", 
            randomFriend.name,
            friendCount - 1];
    } else if (friendCount == 2) {
        id<FBGraphUser> friend1 = [self.selectedFriends objectAtIndex:0];
        id<FBGraphUser> friend2 = [self.selectedFriends objectAtIndex:1];
        friendsSubtitle = [NSString stringWithFormat:@"%@ and %@",
            friend1.name,
            friend2.name];
    } else if (friendCount == 1) {
        id<FBGraphUser> friend = [self.selectedFriends objectAtIndex:0];
        friendsSubtitle = friend.name;
    }
    [self updateCellIndex:2 withSubtitle:friendsSubtitle];
}
The updateSelections helper method controls how the friends are displayed. If one or two friends are selected, those friends' names will be displayed. If more than two friends are selected, a random friend's name will be displayed as well as a count value of the additional friends that have been selected.
Implement the FBFriendPickerDelegate method that will be notified of a friend selection and call the helper method you defined to update the table row display to show the selected friends:
- (void)friendPickerViewControllerSelectionDidChange:
(FBFriendPickerViewController *)friendPicker
{
    self.selectedFriends = friendPicker.selection;
    [self updateSelections];
}
Build and run the project to make sure it runs without errors. Tap the ''With whom?'' menu selection, and then select a friend from the friend selector. Tap the Back button. You should see the friend you selected in the ''With whom?'' row. Try selecting one or more friends to ensure that selected friends are displayed correctly.

Next Steps

Learn how to show the user a list of nearby places.


4 - Show Nearby Places

This tutorial outlines how to add the user's location to a story with the Facebook SDK for iOS. When you've finished these steps, your flow should look like this:
To display the place picker, make use of the FBPlacePickerViewController Facebook SDK object. To use this object, first initialize an instance through the initWithNibName:bundle: method. Once you have an instance, set the sessionproperty to tie it to the authenticated user. Then, set the locationCoordinate property to the coordinate information from the CoreLocation framework's location update. Then, set the delegate method to define the object that handles theFBPlacePickerDelegate notifications. Finally, call the loadData to initiate the API call to get places nearby and display them with the FBPlacePickerViewController.
In this tutorial, you'll implement the FBPlacePickerDelegate methodplacePickerViewControllerSelectionDidChange: that is notified when a user selects a place. That delegate method will pass the FBPlacePickerViewController instance and you'll use the selection property to retrieve the place. The selected place will be a FBGraphPlace, a strongly typed object that represents a place.
This tutorial walks through the following:
Note: Before you start this tutorial, make sure you've run through the Show Friends Tutorial.

Step 1: Add the Core Location Framework

Add the Core Location Framework to the project:

Step 2: Get the User's Current Location

Use the Core Location Framework to get the user's current location. If you're not familiar with how to set that up, review theApple documentation.
Start the location manager with Core Location when the view loads. When you get an accurate location reading, set up the framework to update the places view controller in the steps that follow. The following changes apply to theSCViewController implementation file.
First, make sure the SCViewController class conforms to the CLLocationManagerDelegate protocol.
Next, create a property to represent an instance of the CLLocationManager class.
@property (strong, nonatomic) CLLocationManager *locationManager;
Synthesize the new property:
@synthesize locationManager = _locationManager;
Next, start the location updates in the viewDidLoad method by adding this code to the end of the method:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
// We don't want to be notified of small changes in location, 
// preferring to use our last cached results, if any.
self.locationManager.distanceFilter = 50;
[self.locationManager startUpdatingLocation];
When the view is deallocated, nil out the location manager delegate. Add the following to the dealloc method:
Then, to nil out the delegate the dealloc method is called by adding the following to that method:
_locationManager.delegate = nil;
Finally, implement the CLLocationManagerDelegate methods for handling location updates and errors:
- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation 
{
    if (!oldLocation ||
        (oldLocation.coordinate.latitude != newLocation.coordinate.latitude && 
         oldLocation.coordinate.longitude != newLocation.coordinate.longitude)) {

        // To-do, add code for triggering view controller update
        NSLog(@"Got location: %f, %f", 
              newLocation.coordinate.latitude, 
              newLocation.coordinate.longitude);
    }
}

- (void)locationManager:(CLLocationManager *)manager 
didFailWithError:(NSError *)error {
    NSLog(@"%@", error);
}
Build and run the project to make sure it runs without errors. You should see an alert requesting to use your current location:

Once you grant this permission, you should see a debug log of the current location's latitude and longitude based on theNSLog you added to the locationManager:didUpdateToLocation:fromLocation: delegate method. Here's an example:
2012-05-20 23:11:35.622 Scrumptious[26225:f803] Got location: 37.444574, -122.162776
Remove the debug NSLog statement from the locationManager:didUpdateToLocation:fromLocation: method after you verify it runs successfully.

Step 3: Show the Places Picker

Now that you have the current location, use it with the FBPlacePickerViewController object to show the place picker when the user taps on the ''Where are you?'' menu option.
First, create a property for the FBPlacePickerViewController object:
@property (strong, nonatomic) FBPlacePickerViewController *placePickerController;
Synthesize this property:
@synthesize placePickerController = _placePickerController;
Next, make sure that the view is released in the viewDidUnload method, by adding this to the end of the method:
self.placePickerController = nil;
Next, modify the tableView:didDeselectRowAtIndexPath: delegate method to set up the place picker. Add a new case statement that will do the following - initialize an instance of FBPlacePickerViewController if necessary, set the required properties for the FBPlacePickerViewController instance and finally push theFBPlacePickerViewController view controller:
case 1:
    if (!self.placePickerController) {
        self.placePickerController = [[FBPlacePickerViewController alloc] 
                                         initWithNibName:nil bundle:nil];
        self.placePickerController.title = @"Select a restaurant";
    }
    self.placePickerController.locationCoordinate = 
    self.locationManager.location.coordinate;
    self.placePickerController.radiusInMeters = 1000;
    self.placePickerController.resultsLimit = 50;
    self.placePickerController.searchText = @"restaurant";

    [self.placePickerController loadData];
    [self.navigationController pushViewController:self.placePickerController 
                                         animated:true];
    break;
Build and run the project to make sure it runs without errors. Once you see the home menu selection, select the ''Where are you?'' menu option. You should see a list of places nearby. Selecting a restaurant and tapping the Back button should do nothing at this point. You will add the functionality that saves the selected place next.

Step 4: Display the Selected Place

Add FBPlacePickerDelegate to the list of protocols that the SCViewController conforms to.
Modify the tableView:didSelectRowAtIndexPath: method to set the delegate that handles the nearby place selection. Do this by adding the following statement right after the FBPlacePickerViewController object is initialized:
self.placePickerController.delegate = self;
When the view is deallocated, nil out the place picker delegate by adding this code to the end of the dealloc method:
_placePickerController.delegate = nil;
Next, create a property that will hold the selected place:
@property (strong, nonatomic) NSObject<FBGraphPlace>* selectedPlace;
Synthesize this new property:
@synthesize selectedPlace = _selectedPlace;
Modify the updateSelections method to take care of the place selection and display it under the ''Where are you?'' menu. Add this code to the end of the method:
[self updateCellIndex:1 withSubtitle:(self.selectedPlace ?
                                     self.selectedPlace.name :
                                     @"Select One")];
Finally, implement the FBPlacePickerDelegate method that is notified when a place is selected. In this method, call theupdateSelections method to display the selected restaurant:
- (void)placePickerViewControllerSelectionDidChange:
(FBPlacePickerViewController *)placePicker
{
    self.selectedPlace = placePicker.selection;
    [self updateSelections];
    if (self.selectedPlace.count > 0) {
        [self.navigationController popViewControllerAnimated:true];
    }
}
Build and run the project to make sure it runs without errors. Tap the ''Where are you?'' menu selection and select a location from the restaurant list. Tap the Back button. You should see the place you selected displayed in the ''Where are you?'' row.

Next Steps

Complete the tutorial after learning how to publish an Open Graph action.


5 - Publish Open Graph Story

In this tutorial, you'll bring everything together and publish an Open Graph story. The previous steps let the user specify where they are and who they're with. Now, we'll implement a flow that lets the user select a meal and upload a photo to share what they're doing. Once they enter this information, they can publish a rich story that looks like this:

The FBRequest class has a helper method called requestForUploadPhoto:, which lets you quickly set up a request to post a photo to the app's album. When you provide a UIImage for the photo, you'll get an FBRequest object back. Then, you can initialize and start the connection to upload the photo.
The FBRequest class also has a requestForGraphPath: method that is used to make a Graph API call to get information for any Facebook object by passing in an NSString representing the path to the object. For example, you can pass me to retrieve basic user information corresponding to the session. In this tutorial, you'll pass in an object ID representing the uploaded photo. You'll then use the returned data to help publish the Open Graph action.
The action you'll publish will include information about the meal and if available, optional information representing the selected friends, place and photo. You'll use the methodstartForPostWithGraphPath:graphObject:completionHandler: in the FBRequestConnection class to publish the action. The graphPath you pass will correspond to the me/<YOUR_NAMESPACE>:eat Open Graph action. ThegraphObject parameter will be a protocol object based on the FBGraphObject protocol and will represent the publish action parameters.
This tutorial walks through the following:
Note: Before you start this tutorial, make sure you've run through the Show Nearby Places Tutorial.

Step 1: Configure Open Graph in the App Dashboard

In this step, you'll define the Open Graph action, object and aggregation in the App Dashboard.
First, set up an app namespace. It has to be unique; you'll use it to define your Open Graph actions and objects.

If you don't have a backend server, you can set one up through one of our partners. We'll discuss this further in the next step.
Once you have an app namespace, define an ''eat'' action and a corresponding ''meal'' object. You can define a simple aggregation on the ''eat'' action that will display a list the meals eaten recently on the user's timeline. See the Open Graph Tutorial for more details on setting up your action, object and aggregation.

When you're done with the flow your Open Graph dashboard should look like this:

Next, set up a backend server to host your objects.

Step 2: Set Up Your Backend Server

Open Graph objects need to exist as webpages on a server that Facebook can access. These pages have special markup, called Open Graph tags, that Facebook servers scrape to properly identify the data and connect it to the user. For example, you could have a ''pizza'' webpage that represents a ''pizza'' type of meal. When the user publishes a story with your app, this will connect an ''eat'' action to the ''pizza'' object.
In this tutorial, you won't set up static pages to represent each meal (ex: pizza or hot dogs). Instead, you'll create a dynamic endpoint that creates the proper object Open Graph markup. Here's a sample PHP endpoint:
<?php
function curPageURL() {
 $pageURL = 'http://';
 if ($_SERVER["SERVER_PORT"] != "80") {
  $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
 } else {
  $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
 }
 return $pageURL;
}
?>

<html>
  <head prefix="og: http://ogp.me/ns# product: http://ogp.me/ns/product#">
    <meta property="fb:app_id" content="<?php echo strip_tags($_REQUEST['fb:app_id']);?>">
      <meta property="og:url" content="<?php echo strip_tags(curPageURL());?>">
      <meta property="og:type" content="<?php echo strip_tags($_REQUEST['og:type']);?>">
      <meta property="og:title" content="<?php echo strip_tags($_REQUEST['og:title']);?>">
      <meta property="og:image" content="<?php echo strip_tags($_REQUEST['og:image']);?>">
      <meta property="og:description" content="<?php echo strip_tags($_REQUEST['og:description']);?>">
      <title>Product Name</title>
  </head>
    <body>
      <?php echo strip_tags($_REQUEST['body']);?>
    </body>
</html>
Upload this sample PHP file or similar code to your backend server. If you don't have a backend server, try one of the services we partner with, like Heroku. You can initiate the setup right from the App Dashboard on the app's basic settings page.
Once you've uploaded the dynamic object creation code, test a sample object using the Object Debugger. Enter a URL into the debugger, for example:
https://fbsdkog.herokuapp.com/repeater.php?fb:app_id=233936543368280
&og:type=fb_sample_scrumps:meal
&og:title=Pizza
&og:description="Pizza"
&og:image=https%3A%2F%2Fs-static.ak.fbcdn.net%2Fimages%2Fdevsite%2Fattachment_blank.png
&body=Pizza
Fix any errors you find before moving to the next step.

Step 3: Publish a Test Action

Now that you've configured your Open Graph information and set up your objects, try publishing an action outside of your iOS app with the Graph API Explorer. Go to the Graph API Explorer and select your app from the ''Application'' list. Then, change the action type from the default ''GET'' to ''POST''. Enter me/<YOUR_APP_NAMESPACE>:eat in the Graph API endpoint field and add the following POST data:
  • meal = <OBJECT_URL>
  • image[0][url] = <LINK_TO_AN_IMAGE> e.g. http://www.sugarmedia.com/nyccookbook/images/pizza.jpg
  • tags = <FRIEND_USER_ID>
  • place = <PLACE_ID> e.g. 111615355559307
Once you enter the additional fields, your form should look like this:

Submit your test action. You should see a result ID similar to:
{
  "id": "4413537462971"
}
Now, login to Facebook and go to your Activity Log to verify the story posted correctly:

If you click on the time link for the story, you can preview the news feed story.
If you can't publish the test action, see if you have one of these common errors.
Error:
{
  "error": {
    "message": "(#200) Requires extended permission: publish_actions", 
    "type": "OAuthException", 
    "code": 200
  }
}
Reason:
You haven't authorized the app to publish to your timeline. Click on ''Get Access Token'' and click the ''Extended Permissions'' tab. Select the publish_actions permission and run your test again.
Error:
{
  "error": {
    "type": "Exception", 
    "message": "These users can't be tagged by your app: 100002768941660. Either they aren't developers of your app or your action haven't been approved for tagging.", 
    "code": 1611075
  }
}
Reason:
The user you're trying to tag is not an admin, developer or tester for your app. Go the Roles section for your app in the App Dashboard and add the user you want to tag to the appropriate role. You'll have to wait for that user to accept the request for you to add them to your app.
If you ever have issues using Graph API queries in your iOS app test the same queries out using the Graph API Explorer tool.

Step 4: Add the Meal Selection Flow

Now that you've set up and tested Open Graph publishing outside of your app, let's get back to the iOS code.
Continue setting up the UI by adding a view controller to allow the user to select a meal.
Create a new view controller by right-clicking on the ''Scrumptious'' folder > New File > Objective-C class, and name the class ''SCMealViewController''. Make sure to generate the corresponding NIB file as well.
Open up SCMealViewController.xib and add a Table View object to the view with the following properties: Style: Grouped; Section Height Header: 10; Section Height Footer: 10; X, Y (offset): 0, 0; Width, Height (size): 320, 460.
Next, wire up the meal selection table to the SCMealViewController implementation file:
  • Create an outlet for the Table View object and call it tableView.
  • Connect the dataSource outlet to the File's Owner.
  • Connect the delegate outlet to the File's Owner.
Next, in the SCMealViewController implementation file, add UITableViewDataSource and UITableViewDelegateto the list of protocols it conforms to:
@interface SCMealViewController ()
<UITableViewDataSource,
UITableViewDelegate> 
Then, create the table view data source by defining a property to hold the meal choices:
@interface SCMealViewController ()
<UITableViewDataSource,
UITableViewDelegate> {
    NSArray* _meals;
}
Set the view controller title and initialize the _meals array in the initWithNibName:bundle: custom initialization section:
self.title = @"Select a meal";

_meals = [NSArray arrayWithObjects:
            @"Cheeseburger", 
            @"Pizza",
            @"Hotdog",
            @"Italian",
            @"French",
            @"Chinese",
            @"Thai",
            @"Indian", nil];
Implement the UITableViewDatasource delegate and data source methods to display the data:
- (NSInteger)tableView:(UITableView *)tableView 
      numberOfRowsInSection:(NSInteger)section 
{
    return _meals.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView 
      cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [self.tableView 
        dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] 
                  initWithStyle:UITableViewCellStyleDefault 
                reuseIdentifier:CellIdentifier];
    }    
    
    cell.textLabel.text = [_meals objectAtIndex:indexPath.row];
    cell.imageView.image = [UIImage imageNamed:@"action-eating.png"];
    
    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
    return 1;
}
Define a callback in the SCViewController class that will be used to pass back information on the selected meal. Add the following to the SCViewController header file:
typedef void(^SelectItemCallback)(id sender, id selectedItem);
Next, open up the SCMealViewController header file and import the SCViewController.h file so you can reference the defined callback:
#import "SCViewController.h"
Add an interface property in SCMealViewController.h to hold the callback when a meal is selected:
@property (strong, nonatomic) SelectItemCallback selectItemCallback;
Next, open up the SCMealViewController implementation file and Synthesize this property:
@synthesize selectItemCallback = _selectItemCallback;
Then in the tableView:didSelectRowAtIndexPath: delegate method, add code to invoke the meal selection callback send the user to the previous view controller:
- (void)tableView:(UITableView *)tableView 
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.selectItemCallback) {
        self.selectItemCallback(self, [_meals objectAtIndex:indexPath.row]);
    }
    [self.navigationController popViewControllerAnimated:true];
}
Now that you've set up the meal selection view controller add code to push that controller when the user selects the relevant home menu option. Open up SCViewController.m and import the meal view controller:
#import "SCMealViewController.h"
Add a property for the meal selection view controller instance and the meal that will be selected in that controller:
@property (strong, nonatomic) SCMealViewController *mealViewController;
@property (strong, nonatomic) NSString* selectedMeal;
Synthesize the new properties:
@synthesize mealViewController = _mealViewController;
@synthesize selectedMeal = _selectedMeal;
Then, modify the tableView:didSelectRowAtIndexPath: delegate method to add a new case statement that is activated when the meal selection option is tapped:
case 0:
    if (!self.mealViewController) {
        __block SCViewController* myself = self;
        self.mealViewController = [[SCMealViewController alloc]
                  initWithNibName:@"SCMealViewController" bundle:nil];
        self.mealViewController.selectItemCallback = 
                    ^(id sender, id selectedItem) {
                      myself.selectedMeal = selectedItem;
                      [myself updateSelections];
        };
    }
    [self.navigationController 
                pushViewController:self.mealViewController 
                          animated:true];
    break;
Finally, modify the updateSelections method to save and display the selected meal. Add this code to the end of the method:
[self updateCellIndex:0 withSubtitle:(self.selectedMeal ?
                                          self.selectedMeal : 
                                          @"Select One")];
Build and run the project to make sure it runs without errors. Once you see the menu selection, select the ''What are you eating?'' menu option. You should see a list of meals to choose from. Selecting a meal and tapping the Back button should display your meal in the ''What are you eating?'' row.

Step 5: Add the Photo Selection Flow

In this step, you'll let the user select a photo from their Photo Library and get that ready to upload. In later steps, you'll implement the photo upload as part of a batch request when publishing the action.
You'll make the following changes to the SCMealViewController implementation file. Use of Apple'sUIImagePickerController object to allow the user to select a photo from the Photo Library. First, addUIImagePickerControllerDelegate and UINavigationControllerDelegate to the list of protocols theSCViewController implementation class conforms to.
Next, define the properties you need to get the image: one property for the instance of UIImagePickerController, another for a UIPopoverController instance to present the image picker on iPads, and a property to hold the selected photo.
@property (strong, nonatomic) UIImagePickerController* imagePicker;
@property (strong, nonatomic) UIImage* selectedPhoto;
@property (strong, nonatomic) UIPopoverController *popover;
Synthesize these new properties:
@synthesize imagePicker = _imagePicker;
@synthesize selectedPhoto = _selectedPhoto;
@synthesize popover = _popover;
Remove the UIImagePickerController and UIPopoverController instances in the viewDidUnload method:
self.imagePicker = nil;
self.popover = nil;
Nil out the UIImagePickerController delegate in the dealloc method:
_imagePicker.delegate = nil;
Next, add logic to show the image picker when the user taps on the ''Got a picture?'' menu option. IntableView:didSelectRowAtIndexPath:, add a new case statement:
case 3: 
    if (!self.imagePicker) {
        self.imagePicker = [[UIImagePickerController alloc] init];
        self.imagePicker.delegate = self;

        // In a real app, we would probably let the user
        // either pick an image or take one using the camera.
        // For sample purposes in the simulator, the camera is not available.
        self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    }       
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        // Can't use presentModalViewController for image picker on iPad
        if (!self.popover) {
            self.popover = [[UIPopoverController alloc]
                initWithContentViewController:self.imagePicker];
        }       
        CGRect rect = [tableView rectForRowAtIndexPath:indexPath];
        [self.popover
            presentPopoverFromRect:rect 
            inView:self.view 
            permittedArrowDirections:UIPopoverArrowDirectionAny 
            animated:YES];
    } else {
        [self presentModalViewController:self.imagePicker 
            animated:true];
    }
    break;
Implement the UIImagePickerControllerDelegate delegate method to save the selected photo in theselectedPhoto property, dismiss the image picker and invoke a method to update the home menu UI to display that a photo was selected:
- (void)imagePickerController:(UIImagePickerController *)picker 
        didFinishPickingImage:(UIImage *)image
                  editingInfo:(NSDictionary *)editingInfo
{
    self.selectedPhoto = image;
    
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        [self.popover dismissPopoverAnimated:YES];
    } else {
        [self dismissModalViewControllerAnimated:true];
    }

    [self updateSelections];
}
Modify the updateSelections method to show the user a photo is selected. Add this code to the end of the method:
[self updateCellIndex:3 withSubtitle:(self.selectedPhoto ? @"Ready" : @"Take one")];
Build and run the project to make sure it runs without errors. Once you see the menu selection, select the ''Got a picture?'' menu item. Select a photo in the Photo Library. You should see the ''Ready'' label in the ''Got a picture?'' row:

Step 6: Add the Publish Action Button

Now, finish setting up the UI by adding a submit button that publishes the Open Graph action through the iOS app.
In the SCViewController NIB file add a Round Rect Button object with the following properties: Title: ''Announce''; State Config: Disabled; Text Color: CCCCCC; Control Content Enabled: NO. Center it to the view's width and move it towards the bottom. Wire this up to an action named announce and also add an outlet named announceButton. The outlet will be used to enable/disable the button. You will only show the button if the user selects a meal. All other publish parameters are optional.
Your UI should look like this in Interface Builder:

Now, add logic at the end of the updateSelections method to enable the button:
self.announceButton.enabled = (self.selectedMeal != nil);
Build and run the project to make sure it runs without errors. Check that the announce button is disabled at the start and enabled when you select a meal. Tapping on the button should do nothing at this point. Next, you'll add the logic to publish to a user's timeline.

Step 7: Implement the Publish Action

Now, let's set up the logic to allow the user to publish an Open Graph action. Publishing an action involves making a Graph API call to the me/<YOUR_APP_NAMESPACE>:<ACTION_NAME> endpoint with the object URL and any optional parameters. You'll also need to make sure you ask for the publish_actions permissions. Since you may also be uploading a user selected photo and retrieving the photo information, you also need to ask for the user_photos permissions. You'll ask for the read permission: user_photos when the session is opened initially. You'll ask for the write permission:publish_actions in context, when you're about to publish the user's action. This will be added later in the announce button logic.
To ask for the required permissions, open SCAppDelegate.m and modify the openSession: method. Change this:
[FBSession openActiveSessionWithReadPermissions:nil
                                   allowLoginUI:YES
                              completionHandler:
                                   ^(FBSession *session, 
                                     FBSessionState state, NSError *error) {
                  [self sessionStateChanged:session state:state error:error];
}];    
To this:
NSArray *permissions = [NSArray arrayWithObjects:@"user_photos",  
                                                 nil];

[FBSession openActiveSessionWithReadPermissions:permissions
                                   allowLoginUI:YES
                              completionHandler:
                                   ^(FBSession *session, 
                                     FBSessionState state, NSError *error) {
                  [self sessionStateChanged:session state:state error:error];
}];    
The Open Graph meal object will be implemented as a new protocol you define named SCOGMeal. This new protocol incorporates the FBGraphObject protocol.
The Open Graph action eat and its parameters, including the object URL, will be implemented as a new protocol you define named SCOGEatMealAction that incorporates the FBOpenGraphAction protocol. This new protocol has an additional property representing the SCOGMeal object.
To define the new protocols, add a new file to the project. Select the Objective-C protocol template and name the protocolSCProtocols:
Next, open up the newly added SCProtocols.h file. You'll create your own protocol definition, so delete the default one that was created:
@protocol SCProtocols <NSObject>

@end
Next, include the Facebook SDK header as the new protocols are based on defined Facebook objects:
#import <FacebookSDK/FacebookSDK.h>
Add code to define the SCOGMeal object protocol:
@protocol SCOGMeal<FBGraphObject>

@property (retain, nonatomic) NSString *id;
@property (retain, nonatomic) NSString *url;

@end
Add code to define the SCOGEatMealAction object protocol:
@protocol SCOGEatMealAction<FBOpenGraphAction>

@property (retain, nonatomic) id<SCOGMeal> meal;

@end
Now, open up the SCViewController implementation file and include the new protocols header:
#import "SCProtocols.h"
Also include the FBRequest header that allows you to reference FBGraphObject objects:
#import <FacebookSDK/FBRequest.h>
Now, define a new method that returns a representation of the Open Graph meal object. The meal object will be a URL built dynamically based on the backend code you installed earlier:
- (id<SCOGMeal>)mealObjectForMeal:(NSString*)meal 
{
    // This URL is specific to this sample, and can be used to
    // create arbitrary OG objects for this app; your OG objects
    // will have URLs hosted by your server.
    NSString *format =  
        @"https://<YOUR_BACK_END>/repeater.php?"
        @"fb:app_id=<YOUR_APP_ID>&og:type=%@&"
        @"og:title=%@&og:description=%%22%@%%22&"
        @"og:image=https://s-static.ak.fbcdn.net/images/devsite/attachment_blank.png&"
        @"body=%@";
    
    // We create an FBGraphObject object, but we can treat it as 
    // an SCOGMeal with typed properties, etc. See <FacebookSDK/FBGraphObject.h> 
    // for more details.
    id<SCOGMeal> result = (id<SCOGMeal>)[FBGraphObject graphObject];
    
    // Give it a URL that will echo back the name of the meal as its title, 
    // description, and body.
    result.url = [NSString stringWithFormat:format, 
                   @"<YOUR_APP_NAMESPACE>:meal", meal, meal, meal];
    
    return result;
}
Replace <YOUR_BACK_END> with your backend Site URL, <YOUR_APP_ID> with your app ID and <YOUR_APP_NAMESPACE> with your App Namespace.
Next, prepare the photo information that will be an optional part of the published action. If the user selects a photo, you'll upload the photo, get the source URL and pass it to the Graph API publish call. Define a new method to do this:
- (void)postPhotoThenOpenGraphAction
{
    FBRequestConnection *connection = [[FBRequestConnection alloc] init];

    // First request uploads the photo.
    FBRequest *request1 = [FBRequest 
        requestForUploadPhoto:self.selectedPhoto];
    [connection addRequest:request1
        completionHandler:
        ^(FBRequestConnection *connection, id result, NSError *error) {
            if (!error) {
            }
        }
            batchEntryName:@"photopost"
    ];

    // Second request retrieves photo information for just-created 
    // photo so we can grab its source.
    FBRequest *request2 = [FBRequest 
        requestForGraphPath:@"{result=photopost:$.id}"];
    [connection addRequest:request2
         completionHandler:
        ^(FBRequestConnection *connection, id result, NSError *error) {
            if (!error &&
                result) {
                NSString *source = [result objectForKey:@"source"];
                [self postOpenGraphActionWithPhotoURL:source];
            }
        }
    ];

    [connection start];
}
Don't worry about the error related to the undefined postOpenGraphActionWithPhotoURL: method. You'll define this next.
Once you have meal object and the optional photo source URL you can put the Graph API call together. Define a new method to do this:
- (void)postOpenGraphActionWithPhotoURL:(NSString*)photoURL
{
    // First create the Open Graph meal object for the meal we ate.
    id<SCOGMeal> mealObject = [self mealObjectForMeal:self.selectedMeal];
    
    // Now create an Open Graph eat action with the meal, our location, 
    // and the people we were with.
    id<SCOGEatMealAction> action = 
        (id<SCOGEatMealAction>)[FBGraphObject graphObject];
    action.meal = mealObject;
    if (self.selectedPlace) {
        action.place = self.selectedPlace;
    }
    if (self.selectedFriends.count > 0) {
        action.tags = self.selectedFriends;
    }
    if (photoURL) {
        NSMutableDictionary *image = [[NSMutableDictionary alloc] init];
        [image setObject:photoURL forKey:@"url"];

        NSMutableArray *images = [[NSMutableArray alloc] init];
        [images addObject:image];
        
        action.image = images;
    }
    
    // Create the request and post the action to the 
    // "me/<YOUR_APP_NAMESPACE>:eat" path.
    [FBRequestConnection startForPostWithGraphPath:@"me/<YOUR_APP_NAMESPACE>:eat"
                             graphObject:action
                       completionHandler:
     ^(FBRequestConnection *connection, id result, NSError *error) {
         NSString *alertText;
         if (!error) {
             alertText = [NSString stringWithFormat:
                             @"Posted Open Graph action, id: %@",
                             [result objectForKey:@"id"]];
         } else {
             alertText = [NSString stringWithFormat:
                             @"error: domain = %@, code = %d",
                             error.domain, error.code];
         }
         [[[UIAlertView alloc] initWithTitle:@"Result" 
                                     message:alertText 
                                    delegate:nil 
                           cancelButtonTitle:@"Thanks!" 
                           otherButtonTitles:nil] 
          show]; 
     }
     ];
}
Replace <YOUR_APP_NAMESPACE> with your App Namespace.
Finally, fill in the announce: method that is invoked when the user taps the announce button. This first checks ifpublish_actions had been previously granted for your app on the user's device. If not, the permissions are requested with the required audience selector defaulted to the user's friends. If the permissions have been granted then the logic checks if a photo is available and uploads the photo before publishing, or simply calls the publishing method:
- (IBAction)announce:(id)sender
{
    if ([FBSession.activeSession.permissions 
            indexOfObject:@"publish_actions"] == NSNotFound) {  
            
        [FBSession.activeSession 
            reauthorizeWithPublishPermissions:[NSArray arrayWithObject:@"publish_actions"]
                              defaultAudience:FBSessionDefaultAudienceFriends   
                            completionHandler:^(FBSession *session, NSError *error) {   
                         if (!error) {  
                             // re-call assuming we now have the permission 
                             [self announce:sender];    
                         }  
        }]; 
    } else {
        if (self.selectedPhoto) {
            [self postPhotoThenOpenGraphAction];
        } else {
            [self postOpenGraphActionWithPhotoURL:nil];
        }
    }
}
Build and run the project to make sure it runs without errors. If necessary, log out and log back into the app to make sure you authorize the app again and accept the updated permissions. Publish a simple action with only the meal specified. You should see an alert message with the published action id:

You should also check your timeline's Activity Log to ensure the action published successfully and has the correct story.
If you run into any issues, try turning on logging by adding this code before the request call you're interested in debugging:
[FBSettings setLoggingBehavior:[NSSet 
                                   setWithObjects:FBLoggingBehaviorFBRequests,
                                   FBLoggingBehaviorFBURLConnections, 
                                   nil]];
Test the different additional scenarios like adding friends, a place and a photo. View the different types of stories published by going to your timeline's Activity Log on the desktop. (Hint: to view the how the story will render, click on the date next to the story in the Activity Log.)
Congratulations! You've successfully completed the tutorial and walked through many of the key Facebook features.




No comments:

Post a Comment