The Facebook SDK for iOS provides a caching feature that lets you pre-fetch data for friends and nearby places. This data is cached and available to your app before you need to present it to the users. Whenever the friend or nearby place selector displays, any cached data appears before fresh info is fetched from the server. Pre-fetching the data at the correct time allows you to provide a better user experience, because users don't have to wait for that first set of data to display.
We'll walk through how to pre-fetch data for friends and nearby places, and then discuss scenarios where pre-fetching is useful.
This document walks through the following topics:
Before you begin, make sure you've already set up Facebook Login. This ensures you have the prerequisites and your app is ready for additional Facebook integration.
The completed sample allows users to log in with Facebook and select a friend or place near Facebook headquarters. When a friend is selected, the friend's first name displays with the person's bio, if available. When a place is selected, the place name, city and state display.
The implementation builds on top of Facebook Login, adding two buttons. One button shows the friend selector and the second button shows the nearby place selector. When the user authenticates, friend and place data is pre-fetched and cached.
|
|
|
In this step, you'll add a button in the initial view controller. When the user clicks the button, the friend selector displays.
Make the following changes in your main view controller's nib file:
Round Rect Button object to the view. Set the button title to ''Show Friends''.When you've completed these steps, your implementation file should have the defined outlet and an empty showFriendsAction: 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 friend selector button only when the user is authenticated:
- (void)sessionStateChanged:(NSNotification*)notification {
if (FBSession.activeSession.isOpen) {
self.showFriendsButton.hidden = NO;
[self.authButton setTitle:@"Logout" forState:UIControlStateNormal];
} else {
self.showFriendsButton.hidden = YES;
[self.authButton setTitle:@"Login" forState:UIControlStateNormal];
}
}
In this step, you setup a basic friend selector without taking advantage of the friend data caching features.
Open the view controller implementation file and add the FBFriendPickerDelegate as one of the protocols the view controller implementation conforms to. Add this delegate to the view controller implementation.
@interface ViewController ()
<FBFriendPickerDelegate>
Next, fill out the showFriendsAction: method implementation to trigger the friend selector display. In the code you'll initialize a FBFriendPickerViewController object, configure the properties, set the delegate to the current class, load the data and then show the friend selector modally.
- (IBAction)showFriendsAction:(id)sender {
// Initialize the friend picker
FBFriendPickerViewController *friendPickerController =
[[FBFriendPickerViewController alloc] init];
// Configure the picker ...
friendPickerController.title = @"Show Friends";
// Set this view controller as the friend picker delegate
friendPickerController.delegate = self;
// Allow only a single friend to be selected
friendPickerController.allowsMultipleSelection = NO;
// Fetch the data
[friendPickerController loadData];
// Present view controller modally.e the deprecated
if ([self
respondsToSelector:@selector(presentViewController:animated:completion:)]) {
// iOS 5+
[self presentViewController:friendPickerController
animated:YES
completion:nil];
} else {
[self presentModalViewController:friendPickerController animated:YES];
}
}
Then, implement the FBFriendPickerDelegate delegate methods to handle the done and cancel buttons. You'll simply dismiss the modal view controller:
- (void)facebookViewControllerCancelWasPressed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (void)facebookViewControllerDoneWasPressed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
Build and run the project to make sure it runs without errors. Tap the ''Login'' button to log in with Facebook. Once you're authenticated, you should see the ''Show Friends'' button. Tap ''Show Friends'' to show the friend selector. Test that the cancel and done buttons close the friend selector. You can also test pre-fetching that should not be set up at this point. If you can, turn off your internet connection and tap ''Show Friends''. Data should not display; you should see an error in the Xcode console.
In this step, you'll pre-fetch friend data when the user is authenticated. Modify the sessionStateChanged: method to make a call to the FBFriendPickerViewController class method cacheDescriptor to pre-fetch the data.
Copy this code:
FBCacheDescriptor *friendCacheDescriptor =
[FBFriendPickerViewController cacheDescriptor];
[friendCacheDescriptor prefetchAndCacheForSession:FBSession.activeSession];
And then paste it at the end of this clause:
if (FBSession.activeSession.isOpen) {
Build and run the project to make sure it runs without errors. Once authenticated, tap the ''Show Friends'' button to check the friend selector functionality. To test caching, rerun your app, wait a minute and then turn off internet access again. This ensures the app has a chance to fetch the data in the background.
Tap the ''Show Friends'' button. You should see friend names and may or may not see their profile pictures. You should also see an error in the Xcode console as it fails to fetch fresh data. Turn internet access on again and verify that triggering the friend selector shows all the friend info.
In this step, you'll pre-fetch friend info and display it when the user selects a friend.
The FBFriendPickerViewController class fetches the following friend info by default: id, name, first_name, middle_name, last_name and picture. You can fetch additional data by passing the additional required field info to the fieldsForRequest property of the FBFriendPickerViewController instance. You can also pre-fetch additional info by calling the cacheDescriptorWithUserID:fieldsForRequest: class method instead of cacheDescriptor.
To make sure your app can retrieve friend bio info, your app needs the friends_about_me permission. If you followed the Facebook Login doc, you should have a openSessionWithAllowLoginUI: method defined in your app delegate implementation file. Open the app delegate implementation file and modify the FBSession initialization section to request this permission:
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
NSArray *permissions = [[NSArray alloc] initWithObjects:
@"friends_about_me",
nil];
return [FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
Now, you need to define a new protocol to represent friend info that includes bio data. This new protocol should also conform to FBGraphUser, the protocol that defines basic friend info. To define the new protocol, add a new file to the project. Select the Objective-C protocol template and name the protocol CacheProtocols.
Next, open up the newly added CacheProtocols.h file. You'll create your own protocol definition, so replace the contents of the file with the following:
#import <Foundation/Foundation.h>
#import <FacebookSDK/FacebookSDK.h>
@protocol CacheGraphFriend <FBGraphUser>
@property (nonatomic, retain) NSString *bio;
@end
Now, open up your view controller implementation file and include your new protocol:
#import "CacheProtocols.h"
Add a property to hold the extra field info we want for a friend:
@property (readwrite, copy, nonatomic) NSSet *extraFieldsForFriendRequest;
Synthesize this new property:
@synthesize extraFieldsForFriendRequest = _extraFieldsForFriendRequest;
Set the property value by adding the following code to the viewDidLoad method, just before the call to the app delegate's openSessionWithAllowLoginUI: method:
self.extraFieldsForFriendRequest = [NSSet setWithObjects:@"bio", nil];
In the showFriendsAction: method, add the following statement just before the call to loadData:
friendPickerController.fieldsForRequest = self.extraFieldsForFriendRequest;
That takes care of getting the extra field info when fetching a fresh copy of friends. You'll need to request this extra info when the friend data is pre-fetched. To do that, go to the sessionStateChanged: method and replace the following statement:
FBCacheDescriptor *cacheDescriptor = [FBFriendPickerViewController cacheDescriptor];
With the following statement:
FBCacheDescriptor *friendCacheDescriptor = [FBFriendPickerViewController
cacheDescriptorWithUserID:nil
fieldsForRequest:self.extraFieldsForFriendRequest];
Now, let's add code to display the additional friend data, so we can test that it's fetched correctly. Implement the FBFriendPickerDelegate method that detects a selection and shows an alert with friend data:
- (void)friendPickerViewControllerSelectionDidChange:
(FBFriendPickerViewController *)friendPicker
{
[self dismissModalViewControllerAnimated:YES];
if (friendPicker.selection) {
NSArray *friends = friendPicker.selection;
id<CacheGraphFriend> friend = [friends objectAtIndex:0];
NSString *message = @"";
if (friend.bio && ![friend.bio isEqualToString:@""]) {
message = [message stringByAppendingString:friend.bio];
}
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:friend.first_name
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];
}
}
Build and run the project to make sure it runs without errors. Once authenticated, tap the ''Show Friends'' button and select a friend. The friend selector should close and friend info displayed, including any available bio info:
|
|
|
In this step, you'll add a button in the initial view controller. When the user clicks the button, the nearby place selector displays.
Make the following changes in your main view controller's nib file:
Round Rect Button object to the view. Set the button title to ''Show Nearby''.When you've completed these steps, your implementation file should have the defined outlet and an empty showNearbyAction: action method.
Modify the sessionStateChanged: method in the view controller implementation file to show the nearby place selector button only when the user is authenticated. Add the following statement in the if clause of the session open check:
self.showNearbyButton.hidden = NO;
Add the following statement in the else clause of the session open check:
self.showNearbyButton.hidden = YES;
In this step, you setup a basic nearby place selector without taking advantage of the place data caching features. We'll simplify this sample even further by hard coding the user's current location to Facebook headquarters in Menlo Park. Feel free to replace this with your location. You'll use the Core Location Framework help define this hard-coded current location.
Add the Core Location Framework to the project:

Open up the view controller implementation file and import the Core Location framework:
#import <CoreLocation/CoreLocation.h>
Define a property to hold the location info:
@property (assign, nonatomic) CLLocationCoordinate2D searchLocation;
Synthesize the property:
@synthesize searchLocation = _searchLocation;
Set the location in the viewDidLoad method, just before the call to the app delegate's openSessionWithAllowLoginUI: method:
self.searchLocation = CLLocationCoordinate2DMake(37.483253, -122.150037);
Add the FBPlacePickerDelegate as one of the protocols the view controller implementation conforms to. Add this delegate to the view controller implementation.
@interface ViewController ()
<FBFriendPickerDelegate,
FBPlacePickerDelegate>
Next, fill out the showNearbyAction: method implementation to trigger the nearby place selector display. In the code, you'll initialize a FBPlacePickerViewController object, configure the properties, set the delegate to the current class, load the data and then show the nearby place selector modally.
- (IBAction)showNearbyAction:(id)sender {
// Initialize the place picker
FBPlacePickerViewController *placePickerController =
[[FBPlacePickerViewController alloc] init];
// Configure the picker ...
placePickerController.title = @"Show Nearby";
// Set this view controller as the place picker delegate
placePickerController.delegate = self;
// Set the search criteria
placePickerController.locationCoordinate = self.searchLocation;
placePickerController.radiusInMeters = 1000;
placePickerController.resultsLimit = 20;
// Fetch the data
[placePickerController loadData];
// Present view controller modally.
if ([self
respondsToSelector:@selector(presentViewController:animated:completion:)]) {
// iOS 5+
[self presentViewController:placePickerController animated:YES completion:nil];
} else {
[self presentModalViewController:placePickerController animated:YES];
}
}
Implement the FBPlacePickerDelegate method that notifies the delegate of a place selection. Dismiss the nearby place selector when a place is selected and display the selected place's name, city and state:
- (void)placePickerViewControllerSelectionDidChange:
(FBPlacePickerViewController *)placePicker
{
[self dismissModalViewControllerAnimated:YES];
if (placePicker.selection) {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:placePicker.selection.name
message:[NSString
stringWithFormat:@"%@, %@",
placePicker.selection.location.city,
placePicker.selection.location.state]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];
}
}
Build and run the project to make sure it runs without errors. Once you're authenticated, you should see the ''Show Nearby'' button. Tap ''Show Nearby'' to show the nearby place selector. Test that selecting a place dismisses the selector and displays the place's name, city and state:
|
|
|
If you can, rerun your app from the start and turn off your internet connection. When you tap on ''Show Nearby'', no data should display;you should see an error logged in the Xcode console.
In this step, you'll pre-fetch nearby place data when the user is authenticated. Modify the sessionStateChanged: method to make a call to the FBPlacePickerViewController class method cacheDescriptorWithLocationCoordinate:radiusInMeters:searchText:resultsLimit:fieldsForRequest: to pre-fetch the data. At the end of the following if clause:
if (FBSession.activeSession.isOpen) {
Add this code:
FBCacheDescriptor *placeCacheDescriptor =
[FBPlacePickerViewController
cacheDescriptorWithLocationCoordinate:self.searchLocation
radiusInMeters:1000
searchText:nil
resultsLimit:20
fieldsForRequest:nil];
[placeCacheDescriptor prefetchAndCacheForSession:FBSession.activeSession];
Build and run the project to make sure it runs without errors. Once authenticated, tap the ''Show Nearby'' button to check the nearby place selector functionality. To test caching, rerun your app, wait a minute, and then turn off internet access. This ensures the app has a chance to fetch the data in the background.
Tap the ''Show Nearby'' button You should see the place names and you may or may not see the place profile pictures. Test that selecting a place dismisses the selector and displays the place's name, city and state. Turn internet access on again and verify that triggering the nearby place selector shows all the place info.
If you're using Facebook SDK 3.0.6 for iOS and your app crashes when the nearby place selector is being displayed, then check this migration setting:

Until a fix is in place you'll want to keep this migration setting to Disabled. Alternatively, you could disable place picture loading by adding the following line of code before loading the nearby place selector's data:
placePickerController.itemPicturesEnabled = NO;
Scrumptious: sample included in the SDK that shows caching for friend and place data.