This is a continuation of our series on the best practices documentation for Android and iOS Facebook integration. Implementing discovery for your app via requests is a powerful way to increase social engagement. Diamond Dash (Wooga), currently on iOS, is a great example of an app that uses Facebook Requests effectively to enable app discovery and drive engagement. At a recent event Wooga stated that their user base of 10 million active users were grown virally through users sending invites and virtual gift to friends. This growth was achieved without dependence on App Store rankings and is a testament to the power of social discoverability.
-
To enable distribution back to your app you must configure your app settings in the Dev App:
Android:
iOS:
-
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
You can add the invitation check and trigger code in the onResume
method for the app activity:
public void onResume() {
super.onResume();
SharedPreferences mPrefs = getPreferences(MODE_PRIVATE);
// Check and increment app use counter for triggering friend invites
boolean appUseCheckEnabled = mPrefs.getBoolean("app_usage_check_enable", true);
int appUseCount = mPrefs.getInt("app_usage_count", 0);
appUseCount++;
// Trigger invite if over threshold, 3 in this example
if ((facebook != null) && facebook.isSessionValid() &&
appUseCheckEnabled && (appUseCount >= 3)) {
appUseCount = 0;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("If you enjoy using this app, would you mind " +
"taking a moment to invite a few friends that you think " +
"will also like it?")
.setCancelable(false)
.setPositiveButton("Tell Friends", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Show the friends invite
Bundle params = new Bundle();
params.putString("message", "Check out this awesome app");
facebook.dialog(App.this, "apprequests", params, new DialogListener() {
@Override
public void onComplete(Bundle values) {
final String returnId = values.getString("request");
if (returnId != null) {
Toast.makeText(getApplicationContext(),
"Request sent: " + returnId,
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFacebookError(FacebookError error) {}
@Override
public void onError(DialogError e) {}
@Override
public void onCancel() {
Toast.makeText(getApplicationContext(),
"Request cancelled",
Toast.LENGTH_SHORT).show();
}
});
}
})
.setNegativeButton("No Thanks", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Set the flag so the user is not prompted to
// invite friends again
mPrefs.edit().putBoolean("app_usage_check_enable", false).commit();
dialog.cancel();
}
})
.setNeutralButton("Remind Me", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
// Save app use counter
mPrefs.edit().putInt("app_usage_count", appUseCount).commit();
}
Step 2: Sending the request
Here is the code for how you would implement the user interface for the friend selection that sends out the request.
Bundle params = new Bundle();
params.putString("message", "Learn how to make your Android apps social");
facebook.dialog(context, "apprequests", params, new DialogListener() {
@Override
public void onComplete(Bundle values) {
final String requestId = values.getString("request");
if (requestId != null) {
Toast.makeText(getApplicationContext(),
"Request sent",
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFacebookError(FacebookError error) {}
@Override
public void onError(DialogError e) {}
@Override
public void onCancel() {
Toast.makeText(getApplicationContext(),
"Request cancelled",
Toast.LENGTH_SHORT).show();
}
});
The examples shown will allow the user to pick from a list of all their friends. You 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 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. The code below shows how you would modify the params
parameter that is passed to the dialog
method to add a data object. We will show you how to process this data
parameter later on.
Bundle params = new Bundle();
params.putString("message", "Learn how to make your Android apps social");
params.putString("data",
"{\"badge_of_awesomeness\":\"1\"," +
"\"social_karma\":\"5\"}");
facebook.dialog(App.this, "apprequests", params, new DialogListener() {
@Override
public void onComplete(Bundle values) {
final String requestId = values.getString("request");
if (requestId != null) {
Toast.makeText(getApplicationContext(),
"Request sent: " + requestId,
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFacebookError(FacebookError error) {}
@Override
public void onError(DialogError e) {}
@Override
public void onCancel() {
Toast.makeText(getApplicationContext(),
"Request cancelled",
Toast.LENGTH_SHORT).show();
}
});
Step 1: Triggering when the invitation or request is sent
You can put the app usage counter check code in the applicationDidBecomActive:
delegate method read any invite disable settings information when your app first launches.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Code not shown here, for initializing Facebook and
// setting up the application ...
// We will remember the user's setting if they do not wish to
// send any more invites.
self.appUsageCheckEnabled = YES;
if ([defaults objectForKey:@"AppUsageCheck"]) {
self.appUsageCheckEnabled = [defaults boolForKey:@"AppUsageCheck"];
}
return YES;
}
/*
* 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 ([facebook isSessionValid] && (appActiveCount = 3)) {
trigger = YES;
appActiveCount = 0;
}
// Save the updated counter
[defaults setInteger:appActiveCount forKey:@"AppUsedCounter"];
[defaults synchronize];
return trigger;
}
/*
* When the application becomes active we will check whether or not
* we should prompt the user to invite friends.
*/
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// 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];
[alert release];
}
}
/*
* 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(apiDialogInvitesSendToMany)
withObject:nil afterDelay:0.5];
}
}
Before you run this add UIAlertViewDelegate
to your app delegate header file as one of the protocols it conforms to.
Step 2: Sending the request
Here is the code for how you would implement the user interface for the friend selection that sends out the request.
- (void) apiDialogInvitesSendToMany {
// Show the UI Dialog
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
@"Learn how to make your iOS apps social.", @"message",
nil];
[facebook dialog:@"apprequests"
andParams:params
andDelegate:self];
}
As with the Android example, 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.
- (void) apiDialogInvitesSendToMany {
SBJSON *jsonWriter = [[SBJSON new] autorelease];
NSDictionary *gift = [NSDictionary dictionaryWithObjectsAndKeys:
@"5", @"social_karma",
@"1", @"badge_of_awesomeness",
nil];
NSString *giftStr = [jsonWriter stringWithObject:gift];
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
@"Learn how to make your iOS apps social.", @"message",<200b>
giftStr, @"data",
nil];
}
// Handle the request call back
- (void)dialogCompleteWithUrl:(NSURL *)url {
NSDictionary *params = [self parseURLParams:[url query]];
NSMutableArray *requestIDs = [[NSMutableArray alloc] init];
for (NSString *paramKey in params) {
if ([paramKey hasPrefix:@"request_ids"]) {
[requestIDs addObject:[params objectForKey:paramKey]];
}
}
if ([requestIDs count] 0) {
NSLog(@"Request ID(s): %@", requestIDs);
}
}
You can set up your code to handle any success or error conditions. You may want to display a success or error message to your users, or you may want to store the request ID parameters that may be returned in the case of a successful request.
The sample code simply prints out the request ID information. 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 to do this as a best practice instead of relying on the data information.
The user-generated notification will be visible to the friend using Facebook based on the Dev App settings you have.
--
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.
When the user taps on a notification in the Facebook for Android app the class you configured in the Dev App settings will be invoked with the request information passed in the Intent's data. The data will have the following information:
request_ids=[COMMA_SEPARATED_REQUESTIDs]&
app_request_type=user_to_user&ref=notification
You can process this information to direct users to the best experience based on the request ID.
Once you have processed the request you should delete it from the Facebook server by making an HTTP DELETE request to the /[REQUEST_ID]
Graph API endpoint.
Step 1: Handle the incoming notification
In the activity tied to your configured class you should process any incoming data to flag incoming requests
/**. Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAsyncRunner = new AsyncFacebookRunner(facebook);
mHandler = new Handler();
// Parse any incoming notifications and save
Uri intentUri = getIntent().getData();
if (intentUri != null) {
String requestIdParam = intentUri.getQueryParameter("request_ids");
if (requestIdParam != null) {
String array[] = requestIdParam.split(",");
requestId = array[0];
}
}
facebook.authorize(this, new DialogListener() {
@Override
public void onComplete(Bundle values) {
// Process any available request
if (requestId != null) {
Toast.makeText(getApplicationContext(), Incoming request",
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFacebookError(FacebookError error) {}
@Override
public void onError(DialogError e) {}
@Override
public void onCancel() {}
});
}
You can then customize the behavior by for example directing the app user to the relevant intent.
Step 2: Handle any additional data in the request
Your app may have passed along additional data in the request. You can get this data by making an HTTP GET request to the /[REQUEST_ID]
Graph API endpoint. The Graph API data returned will be in the form:
{
"id": "297759330273970",
"application": {
"name": "Requests Are King",
"canvas_name": "requestsareking",
"namespace": "requestsareking",
"id": "136490333137025"
},
"from": {
"name": "Christine Abernathy",
"id": "1424840234"
},
"data": "{\"badge_of_awesomeness\":\"1\",\"social_karma\":\"5\"}",
"message": "Learn how to make your apps social",
"created_time": "2012-02-01T19:38:40+0000",
"type": "apprequest"
}
So in the example below instead of displaying an alert that there is an incoming notification you can make a Graph API call:
Change:
if (requestId != null) {
Toast.makeText(getApplicationContext(), Incoming request",
Toast.LENGTH_SHORT).show();
}
To:
if (requestId != null) {
mAsyncRunner.request(requestId, new RequestIdGetRequestListener());
}
Then you can define a new RequestIdGetRequestListener
class to handle the Graph API response and display the request data:
public class RequestIdGetRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, Object state) {
try {
final String title;
final String message;
JSONObject jsonObject = new JSONObject(response);
String from = jsonObject.getJSONObject("from").getString("name");
if (jsonObject.getString("data") != null) {
String data = jsonObject.getString("data");
JSONObject dataObject = new JSONObject(data);
String badge = dataObject.getString("badge_of_awesomeness");
String karma = dataObject.getString("social_karma");
title = from+" sent you a gift";
message = "Badge: "+badge+" Karma: "+karma;
} else {
title = from+" sent you a request";
message = jsonObject.getString("message");
}
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), title + "\n\n" + message,
Toast.LENGTH_LONG).show();
}
});
} catch (JSONException e) {
// Could be due to a few things including reading a notification
// that has been deleted but still shows up in the Facebookapp.
e.printStackTrace();
}
};
}
You should see the following when you click through a notification from the Android Facebook app and the user has authenticated your app:
Step 3: 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.
As an example for Android you can call the request deletion code after showing the request data:
Toast.makeText(getApplicationContext(), title + "\n\n" + message,
Toast.LENGTH_LONG).show();
Bundle params = new Bundle();
params.putString("method", "delete");
mAsyncRunner.request(requestId, params, new RequestIdDeleteRequestListener());
Then you can define a new RequestIdDeleteRequestListener
class to handle the post-delete scenario:
public class RequestIdDeleteRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, Object state) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Request deleted",
Toast.LENGTH_SHORT).show();
}
});
};
}
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 method:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
openedURL = url;
return [facebook handleOpenURL:url];
}
Step 2: Handle the incoming notification
You should implement this in the fbDidLogin
method for the FBSessionDelegate
handler. In our example we are simply going to show an alert to the user if the app was activated by a notification.
- (void)fbDidLogin {
if (openedURL) {
NSString *query = [openedURL fragment];
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];
[alert release];
}
}
}
}
You can then customize the behavior by for example sending the app user to the relevant view.
Step 3: Handle any additional data in the request
To process the additional request data modify the fbDidLogin
method to make a call to the Graph API endpoint. Then implement the request:didLoad:
method to handle the Graph API call back and show the request data. Note that you should add the FBRequestDelegate
to the list of delegates the class handles.
- (void)fbDidLogin {
if (openedURL) {
NSString *query = [openedURL fragment];
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"]) {
NSString *requestIDParam = [targetParams
objectForKey:@"request_ids"];
NSArray *requestIDs = [requestIDParam
componentsSeparatedByString:@","];
// Process the first request id (there may be more than one)
[facebook requestWithGraphPath:[requestIDs objectAtIndex:0]
andDelegate:self];
}
}
}
}
- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSArray class]] && ([result count] 0)) {
result = [result objectAtIndex:0];
}
NSString *title;
NSString *message;
if ([result objectForKey:@"data"]) {
title = [NSString
stringWithFormat:@"%@ sent you a gift",
[[result objectForKey:@"from"] objectForKey:@"name"]];
SBJSON *jsonParser = [[SBJSON new] autorelease];
NSDictionary *requestData = [jsonParser
objectWithString:[result objectForKey:@"data"]];
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];
[alert release];
}
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.
You can put the following deletion code after the alert dialog has been shown:
[alert release];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
@"delete", @"method",nil];
[facebook requestWithGraphPath:[result objectForKey:@"id"]
andParams:params
andDelegate:nil];
-
To enable iOS push notification through the Facebook iOS app configure a mobile web URL in the Facebook Dev App.
If you do not configure your app's mobile web URL then users can still see app request notifications when they open up the Facebook app and tap on the notifications icon.
Get started with our step-by-step documentation and sample code available in our iOS Getting Started and Android Getting Started guides. The Hackbook sample code that is included in the SDK provides you with examples of how to send out requests and handle incoming request notifications.
The Android and iOS examples used in this blog can be found on GitHub.