Back to News for Developers

Android and iOS: Discovery via Requests Best Practices

April 26, 2012ByChristine Abernathy

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.

-

Pro-tip 1: Configure your Dev App settings

To enable distribution back to your app you must configure your app settings in the Dev App:

Android:

  1. Enable the "Configured for Android SSO" setting
  2. Enter your app signature in the "Android Key Hash" setting (more details)
  3. Enter the "Android Package Name" for your app
  4. Enter the "Android Class Name" for your app
  5. Enter the Google Play URL for your app

iOS:

  1. Enter an iOS Bundle ID that corresponds to your app
  2. Enter an iPhone and/or iPad App Store ID
  3. Enable the "Configured for iOS SSO" setting
  4. Enable the "iOS native deep linking" setting
  5. Enter an App Domain (optional)
  6. Enter a Mobile Web URL (optional)

-

Pro-tip 2: Send requests to friends

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:

  1. Triggering when the invitation or request is sent
  2. Sending the request
  3. Sending additional data with the request, such as a virtual gift

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.

Android

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();
    }
});

iOS

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.

--

Pro-tip 3: Handle the incoming notifications to provide a more relevant user experience

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.

Android

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]&amp;
    app_request_type=user_to_user&amp;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();
                }
            });
    };
}

iOS

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]
    &amp;access_token=[USER_ACCESS_TOKEN]
    &amp;target_url=http://apps.facebook.com/[APP_NAME_SPACE]/?request_ids=
    [COMMA_SEPARATED_REQUESTIDs]&amp;ref=notif&amp;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]&amp;ref=notif&amp;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]] &amp;&amp; ([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];

-

Pro-tip 4: Setup Facebook app push notifications (iOS)

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.


Tags: