Social Channels enable users to post to their friends' News Feed or send a Request to their friends. The iOS SDK provides a method for integrating social channels through Facebook Platform dialogs including the Requests Dialog that is used by a user to send out notifications to one or more friends.
The Requests dialog allows you to provide basic Facebook functionality in your app with a few lines of code -- no need to build native dialogs, make API calls, or handle responses.
Note: Notifications sent by your app is only received on mobile devices if your app is a game. If your app is not a game, notifications are still delivered to the App Center on the desktop.
In this How To we will walk you through the best practices around configuring social discovery via the Request channel.
Note: Before you begin this How To, make sure set up Facebook Login for your app. This will ensure that you have all the prerequisites installed and that your app is ready for additional Facebook integration.
This how-to will walk through the following topics:
You should design experiences into your app to allow users to send requests to friend to drive re-engagement. Some design experiences include giving users the ability to request gifts, accept gifts, or help them complete a mission in your app. For example in Diamond Dash you can send a life to a friend. If that friend has not been using the app for a while this could help re-engage them.
Another design experience to consider is to allow users invite their friends to use your app. For example, there are apps that ask users to rate the app after some time. If you wanted to build something similar to drive engagement you could ask users to invite their friends. Wherever you have a button or call to action to rate the app, think about adding a flow to invite friends.
These user-generated requests are initiated when the app enables the user to select one or more friends to send a request to.
We will walk you through the steps to send out an invite or a request:
You can set up your app to prompt the user to send out an invite after the user has used the app a certain number of times. You should also give the user the ability to invite friends to use the app at any time through the use of a menu button they can always get to. In this step we will show you a simple way of triggering the invitation request.
In our sample code we will show the user a message after they have used (or opened) the app three times. The user can choose to tell their friends, be reminded later, or dismiss the message and never be asked again. We will store the app usage counter and whether or not the user wants to be prompted in the future in the device's storage.
Step 1: Triggering when the invitation or request is sent
First, add a property that will hold a flag indicating if we can check the invite trigger. Add this in the app delegate implementation file:
@interface AppDelegate ()
@property (nonatomic, assign) BOOL appUsageCheckEnabled;
@end
Next, synthesize this new property:
@synthesize appUsageCheckEnabled = _appUsageCheckEnabled;
The trigger check will be added in the applicationDidBecomeActive:
app delegate method. When the user is first asked to send a request to friends they can choose to disable any such future notifications. You will save this preference in the user preferences. When you load the application you should read this setting:
To read the initial user preference, add this code to the end of the application:didFinishLaunchingWithOptions:
app delegate method:
// We will remember the user's setting if they do not wish to
// send any more invites.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.appUsageCheckEnabled = YES;
if ([defaults objectForKey:@"AppUsageCheck"]) {
self.appUsageCheckEnabled = [defaults boolForKey:@"AppUsageCheck"];
}
return YES;
}
Next add the code that will trigger the invite if the user has enabled this:
/*
* This private method will be used to check the app
* usage counter, update it as necessary, and return
* back an indication on whether the user should be
* shown the prompt to invite friends
*/
- (BOOL) checkAppUsageTrigger {
// Initialize the app active count
NSInteger appActiveCount = 0;
// Read the stored value of the counter, if it exists
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"AppUsedCounter"]) {
appActiveCount = [defaults integerForKey:@"AppUsedCounter"];
}
// Increment the counter
appActiveCount++;
BOOL trigger = NO;
// Only trigger the prompt if the facebook session is valid and
// the counter is greater than a certain value, 3 in this sample
if (FBSession.activeSession.isOpen && (appActiveCount >= 3)) {
trigger = YES;
appActiveCount = 0;
}
// Save the updated counter
[defaults setInteger:appActiveCount forKey:@"AppUsedCounter"];
[defaults synchronize];
return trigger;
}
Next, in the applicationDidBecomeActive:
app delegate method, add code at the end to trigger the checks:
// Check the flag for enabling any prompts. If that flag is on
// check the app active counter
if (self.appUsageCheckEnabled && [self checkAppUsageTrigger]) {
// If the user should be prompter to invite friends, show
// an alert with the choices.
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Invite Friends"
message:@"If you enjoy using this app, would you mind taking a moment to invite a few friends that you think will also like it?"
delegate:self
cancelButtonTitle:@"No Thanks"
otherButtonTitles:@"Tell Friends!", @"Remind Me Later", nil];
[alert show];
}
Finally, add code to detect the button clicked when the invitation alert view is shown to optionally save the user's choice to avoid future prompts:
/*
* When the alert is dismissed check which button was clicked so
* you can take appropriate action, such as displaying the request
* dialog, or setting a flag not to prompt the user again.
*/
- (void)alertView:(UIAlertView *)alertView
didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
// User has clicked on the No Thanks button, do not ask again
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:YES forKey:@"AppUsageCheck"];
[defaults synchronize];
self.appUsageCheckEnabled = NO;
} else if (buttonIndex == 1) {
// User has clicked on the Tell Friends button
[self performSelector:@selector(sendRequest)
withObject:nil afterDelay:0.5];
}
}
Before you run this add UIAlertViewDelegate
to your app delegate implementation header as one of the protocols it conforms to.
@interface AppDelegate () <UIAlertViewDelegate>
Step 2: Sending the request
The Facebook SDK's FBWebDialogs
class has convenience class methods that you can use to show the Requests Dialog. Add the code below to invoke the dialog that is used to send out the request:
- (void)sendRequest {
// Display the requests dialog
[FBWebDialogs
presentRequestsDialogModallyWithSession:nil
message:@"Learn how to make your iOS apps social."
title:nil
parameters:nil
handler:nil];
}
You can filter the suggested list down or target one user. To filter down the list you can pass on an additional parameter, suggestions
that should contain a comma-separated list of friends to show in the selection. To target one recipient, pass that user's ID to the to
parameter.
Step 3: Sending additional data with your request
You can also pass arbitrary data as an additional data
parameter. This data can later be retrieved through a Graph API call that includes the request ID that is generated when a request is sent out.
A string needs to be passed in the data
parameter. You can use a JSON serializer to convert your arbitrary data into a string. In this sample, you'll make use of the NSJSONSerialization
class to do this.
First, add the code for sending out and parsing incoming requests:
/**
* A function for parsing URL parameters.
*/
- (NSDictionary*)parseURLParams:(NSString *)query {
NSArray *pairs = [query componentsSeparatedByString:@"&"];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *pair in pairs) {
NSArray *kv = [pair componentsSeparatedByString:@"="];
NSString *val =
[[kv objectAtIndex:1]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[params setObject:val forKey:[kv objectAtIndex:0]];
}
return params;
}
/*
* Send a user to user request
*/
- (void)sendRequest {
NSError *error;
NSData *jsonData = [NSJSONSerialization
dataWithJSONObject:@{
@"social_karma": @"5",
@"badge_of_awesomeness": @"1"}
options:0
error:&error];
if (!jsonData) {
NSLog(@"JSON error: %@", error);
return;
}
NSString *giftStr = [[NSString alloc]
initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
giftStr, @"data",
nil];
// Display the requests dialog
[FBWebDialogs
presentRequestsDialogModallyWithSession:nil
message:@"Learn how to make your iOS apps social."
title:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
if (error) {
// Error launching the dialog or sending the request.
NSLog(@"Error sending request.");
} else {
if (result == FBWebDialogResultDialogNotCompleted) {
// User clicked the "x" icon
NSLog(@"User canceled request.");
} else {
// Handle the send request callback
NSDictionary *urlParams = [self parseURLParams:[resultURL query]];
if (![urlParams valueForKey:@"request"]) {
// User clicked the Cancel button
NSLog(@"User canceled request.");
} else {
// User clicked the Send button
NSString *requestID = [urlParams valueForKey:@"request"];
NSLog(@"Request ID: %@", requestID);
}
}
}
}];
}
Note that in the modified sendRequest
method, you've implemented the dialog handler to handle any success or error conditions. The sample code simply prints out the request ID information if a request is successfully sent out. In your app, you may want to store the request ID parameters that may be returned in the case of a successful request.
To see your code in action you can add a button to your view controller and call the sendRequest
method you have defined in the app delegate. First add the sendRequest
method to the app delegate header interface:
- (void) sendRequest;
Now, open the main nib file and add the button:
Round Rect Button
object to the view. Set the button title to ''Send Request''.When you've completed these steps, your implementation file should have one new outlet and an empty sendRequestButtonAction:
action method.
If you followed the Facebook Login doc, you should have a sessionStateChanged:
method defined in your view controller implementation file that controls the logged-in and logged-out UI. Modify this method to show the ''Send Request'' button only when the user is authenticated:
- (void)sessionStateChanged:(NSNotification*)notification {
if (FBSession.activeSession.isOpen) {
self.sendRequestButton.hidden = NO;
[self.authButton setTitle:@"Logout" forState:UIControlStateNormal];
} else {
self.sendRequestButton.hidden = YES;
[self.authButton setTitle:@"Login" forState:UIControlStateNormal];
}
}
Finally, add the code below to the button's action to invoke the app delegate send request code:
- (IBAction)sendRequestButtonAction:(id)sender {
AppDelegate *appDelegate =
(AppDelegate *) [[UIApplication sharedApplication] delegate];
if (FBSession.activeSession.isOpen) {
[appDelegate sendRequest];
}
}
Build and run your project. Tap ''Login'' and once authenticated, send out a request to a friend and check the Xcode console. You should see output similar to the following:
2012-06-11 23:23:21.874 RequestsV3[56935:f803] Request ID: 329711187109686
If you have access to the friend to whom you sent the request, have them check to ensure that they received a notification.
We previously mentioned that you could send a data object with your request. This data is stored on Facebook. If you wish to persistently handle the storage of request-related data you should use the request ID and store any relevant information on your back-end servers. It is considered a best practice to do that instead of relying on the data information carried in the request.
When a friend receives a notification on the Facebook native app and taps on it, they will be directed back to your app.
To ensure a relevant user experience you should process incoming notifications when your app is activated. For example, the user may have sent out a gift as part of the notification. Your app may contain a view that displays incoming gifts. You should direct the friend to that view.
Notifications are kept on Facebook for two weeks, after which they are automatically time out. You should proactively delete notifications after you are done processing them.
To implement request handling for iOS the key is to realize that a notification will call your app with the following URL depending on whether the user is logged in to your app:
Logged in:
fb[APP_ID]://authorize#expires_in=[ACCESS_TOKEN_EXPIRATION]
&access_token=[USER_ACCESS_TOKEN]
&target_url=http://apps.facebook.com/[APP_NAME_SPACE]/?request_ids=
[COMMA_SEPARATED_REQUESTIDs]&ref=notif&app_request_type=user_to_user
Not logged in:
fb[APP_ID]://authorize#target_url=http://apps.facebook.com/[APP_NAME_SPACE]/?
request_ids=[COMMA_SEPARATED_REQUESTIDs]&ref=notif&app_request_type=user_to_user
Step 1: Save the URL when the app is activated
You set this up in the app delegate implementation file. First define a new private property to hold the incoming notification URL:
@property (nonatomic, retain) NSURL *openedURL;
Synthesize this property:
@synthesize openedURL = _openedURL;
Then save the URL when the app is opened:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
self.openedURL = url;
return [FBSession.activeSession handleOpenURL:url];
}
Step 2: Handle the incoming notification
You should implement this in the applicationDidBecomeActive:
app delegate method that is always invoked at the end of any app opening. First create a helper method checkIncomingNotification
that you will call in the session callback handler:
/*
* Helper function to check incoming URL
*/
- (void) checkIncomingNotification {
if (self.openedURL) {
NSString *query = [self.openedURL fragment];
if (!query) {
query = [self.openedURL query];
}
NSDictionary *params = [self parseURLParams:query];
// Check target URL exists
NSString *targetURLString = [params valueForKey:@"target_url"];
if (targetURLString) {
NSURL *targetURL = [NSURL URLWithString:targetURLString];
NSDictionary *targetParams = [self parseURLParams:[targetURL query]];
NSString *ref = [targetParams valueForKey:@"ref"];
// Check for the ref parameter to check if this is one of
// our incoming news feed link, otherwise it can be an
// an attribution link
if ([ref isEqualToString:@"notif"]) {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Requests"
message:[NSString
stringWithFormat:@"Incoming ref: %@", ref]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil,
nil];
[alert show];
}
}
// Clean out to avoid duplicate calls
self.openedURL = nil;
}
}
Add the call to the notification check method at the end of the applicationDidBecomeActive:
app delegate code and only if the session is open:
if (FBSession.activeSession.isOpen) {
// Check for any incoming notifications
[self checkIncomingNotification];
}
You can then customize the behavior by for example, sending the user to the relevant view in your app.
Step 3: Handle any additional data in the request
To process the additional request data modify the checkIncomingNotification
method to make a call to the Graph API endpoint for that request. Then handle the API callback to show the request data.
/*
* Helper function to get the request data
*/
- (void) notificationGet:(NSString *)requestid {
[FBRequestConnection startWithGraphPath:requestid
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (!error) {
NSString *title;
NSString *message;
if ([result objectForKey:@"data"]) {
title = [NSString
stringWithFormat:@"%@ sent you a gift",
[[result objectForKey:@"from"]
objectForKey:@"name"]];
NSString *jsonString = [result objectForKey:@"data"];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
if (!jsonData) {
NSLog(@"JSON decode error: %@", error);
return;
}
NSError *jsonError = nil;
NSDictionary *requestData =
[NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:&jsonError];
if (jsonError) {
NSLog(@"JSON decode error: %@", error);
return;
}
message =
[NSString stringWithFormat:@"Badge: %@, Karma: %@",
[requestData objectForKey:@"badge_of_awesomeness"],
[requestData objectForKey:@"social_karma"]];
} else {
title = [NSString
stringWithFormat:@"%@ sent you a request",
[[result objectForKey:@"from"] objectForKey:@"name"]];
message = [NSString stringWithString:
[result objectForKey:@"message"]];
}
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:title
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil,
nil];
[alert show];
}
}];
}
/*
* Helper function to check incoming URL
*/
- (void) checkIncomingNotification {
if (self.openedURL) {
NSString *query = [self.openedURL fragment];
if (!query) {
query = [self.openedURL query];
}
NSDictionary *params = [self parseURLParams:query];
// Check target URL exists
NSString *targetURLString = [params valueForKey:@"target_url"];
if (targetURLString) {
NSURL *targetURL = [NSURL URLWithString:targetURLString];
NSDictionary *targetParams = [self parseURLParams:[targetURL query]];
NSString *ref = [targetParams valueForKey:@"ref"];
// Check for the ref parameter to check if this is one of
// our incoming news feed link, otherwise it can be an
// an attribution link
if ([ref isEqualToString:@"notif"]) {
// Get the request id
NSString *requestIDParam = [targetParams
objectForKey:@"request_ids"];
NSArray *requestIDs = [requestIDParam
componentsSeparatedByString:@","];
// Get the request data from a Graph API call to the
// request id endpoint
[self notificationGet:[requestIDs objectAtIndex:0]];
}
}
// Clean out to avoid duplicate calls
self.openedURL = nil;
}
}
Build and run your project. Send out a request to your test user. Log in as that test user and check for a notification. You should see the following when you click through a notification from the iOS Facebook app and the user has authenticated your app:
Step 4: Delete the notification
Once you have processed the request ID you should delete it from the Facebook server by making an HTTP DELETE request to the /[REQUEST_ID]
Graph API endpoint.
First define a helper method that will delete the request ID:
/*
* Helper function to delete the request notification
*/
- (void) notificationClear:(NSString *)requestid {
// Delete the request notification
[FBRequestConnection startWithGraphPath:requestid
parameters:nil
HTTPMethod:@"DELETE"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (!error) {
NSLog(@"Request deleted");
}
}];
}
Now add a call to the deletion method after the alert dialog has been shown:
[alert show];
// Delete the request notification
[self notificationClear:[result objectForKey:@"id"]];
Build and run your project. Send out a request to your test user. Log in as that test user and check for a notification. Click through the notification. You should see an alert with the request data. If you have Xcode running you should also see the following on the console log:
2012-06-12 23:16:36.027 RequestsV3[59982:b903] Request deleted
If you have a mobile app that is only supported on specific platforms you may want to restrict sending User to User Requests only to users that have your supported devices. Developers can query the set of devices a user has via the Graph API and the devices
field:
GET http://graph.facebook.com/USER_ID/?field=devices
This will return a devices
object that contains the set of devices the user has registered with Facebook:
{
"devices": [
{
"os": "iOS",
"hardware": "iPhone"
},
{
"os": "iOS",
"hardware": "iPad"
}
],
"id": "USER_ID",
"type": "user"
}
Since the field parameter can be appended to a Graph API request, you can use this to get the set of devices a users set of friends support. Then in your app, filter out only the ones that are eligible for your app. In the iOS Requests dialog you can pass in a comma-separated list of IDs to the suggestions
parameter to show a filtered list of users.
Now let's look at the code you can use to implement this. First create a new a method for sending the request. This new method should take in a targeted list of IDs to show in the Requests dialog:
/*
* Send a user to user request, with a targeted list
*/
- (void)sendRequest:(NSArray *) targeted {
NSError *error;
NSData *jsonData = [NSJSONSerialization
dataWithJSONObject:@{
@"social_karma": @"5",
@"badge_of_awesomeness": @"1"}
options:0
error:&error];
if (!jsonData) {
NSLog(@"JSON error: %@", error);
return;
}
NSString *giftStr = [[NSString alloc]
initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSMutableDictionary* params =
[NSMutableDictionary dictionaryWithObjectsAndKeys:giftStr, @"data",
nil];
// Filter and only show targeted friends
if (targeted != nil && [targeted count] > 0) {
NSString *selectIDsStr = [targeted componentsJoinedByString:@","];
[params setObject:selectIDsStr forKey:@"suggestions"];
}
// Display the requests dialog
[FBWebDialogs
presentRequestsDialogModallyWithSession:nil
message:@"Learn how to make your iOS apps social."
title:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
if (error) {
// Error launching the dialog or sending request.
NSLog(@"Error sending request.");
} else {
if (result == FBWebDialogResultDialogNotCompleted) {
// User clicked the "x" icon
NSLog(@"User canceled request.");
} else {
// Handle the send request callback
NSDictionary *urlParams = [self parseURLParams:[resultURL query]];
if (![urlParams valueForKey:@"request"]) {
// User clicked the Cancel button
NSLog(@"User canceled request.");
} else {
// User clicked the Send button
NSString *requestID = [urlParams valueForKey:@"request"];
NSLog(@"Request ID: %@", requestID);
}
}
}
}];
}
Next, create a new method that will make the Graph API call to get the user's friends, filter the result, and then invoke the targeted send request method sendRequest:
that you just defined:
- (void) requestFriendsUsingDevice:(NSString *)device {
NSMutableArray *deviceFilteredFriends = [[NSMutableArray alloc] init];
[FBRequestConnection startWithGraphPath:@"me/friends"
parameters:[NSDictionary
dictionaryWithObjectsAndKeys:
@"id,devices", @"fields",
nil]
HTTPMethod:nil
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (!error) {
// Get the result
NSArray *resultData = [result objectForKey:@"data"];
// Check we have data
if ([resultData count] > 0) {
// Loop through the friends returned
for (NSDictionary *friendObject in resultData) {
// Check if devices info available
if ([friendObject objectForKey:@"devices"]) {
NSArray *deviceData = [friendObject
objectForKey:@"devices"];
// Loop through list of devices
for (NSDictionary *deviceObject in deviceData) {
// Check if there is a device match
if ([device isEqualToString:
[deviceObject objectForKey:@"os"]]) {
// If there is a match, add it to the list
[deviceFilteredFriends addObject:
[friendObject objectForKey:@"id"]];
break;
}
}
}
}
}
}
// Send request
[self sendRequest:deviceFilteredFriends];
}];
}
Finally, modify the original sendRequest
method to get the friend list before showing the dialog. In the code you will only target friends using ''iOS'' devices:
- (void)sendRequest {
// Filter and only show friends using iOS
[self requestFriendsUsingDevice:@"iOS"];
}
Build and run your project. Click on the Send Request button and verify that the friends filtered on the list match your list of iOS friends from a Graph API call. The images below show sample results before filtering and after mobile device filtering: