Integrating Facebook and Parse Tutorial: Part 2

In part 2 of our Parse + Facebook tutorial series, you’ll wrap up the image sharing app with image wall and comments features. By Toby Stephens.

Leave a rating/review
Save for later
Share

Welcome to the second part of this two-part Parse tutorial series that focuses on integrating both Facebook and Parse into your apps.

  • In Part 1, you learned how how to set up a project with Facebook and Parse SDKs, authenticate a user with Facebook using Parse’s Facebook toolkit and retrieve Facebook user information using the Facebook Graph API.
  • In Part 2 (you are here!), you’ll build a data store for your images and comments in Parse, display uploaded images on an image wall and add a comment system to your app.

The completed project from the first half is available here. If you’re going to use this starter project rather than using your own project from Part 1, there are two things to note:

  • Make sure to modify the FacebookAppID in the project’s plist file as well as the Parse App ID and Parse Client Key in the AppDelegate to match the keys you generated for your Facebook and Parse apps.
  • Be aware that URL Scheme section for adding your Facebook URL will be in a different location in the Info tab.

Let’s get back to wrapping up this app!

Getting Started

The first requirement for your image wall is a data structure for the images and their respective comments.

You’ll store the data in a singleton class named DataStore. Create a new Objective-C class that is a subclass of NSObject. Name the class DataStore.

This class will operate as a singleton, so therefore you need some code to retrieve a single instance of this class. Replace the code in DataStore.h with the following:

@interface DataStore : NSObject

@property (nonatomic, strong) NSMutableDictionary *fbFriends;
@property (nonatomic, strong) NSMutableArray *wallImages;
@property (nonatomic, strong) NSMutableDictionary *wallImageMap;

+ (DataStore *) instance;
- (void) reset;

@end

Now replace the contents of DataStore.m with the following code:

#import "DataStore.h"

@implementation DataStore

static DataStore *instance = nil;
+ (DataStore *) instance
{
    @synchronized (self) {
        if (instance == nil) {
            instance = [[DataStore alloc] init];
        }
    }
    return instance;
}

- (id) init
{
    self = [super init];
    if (self) {
        _fbFriends = [[NSMutableDictionary alloc] init];
        _wallImages = [[NSMutableArray alloc] init];
        _wallImageMap = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void) reset
{
    [_fbFriends removeAllObjects];
    [_wallImages removeAllObjects];
    [_wallImageMap removeAllObjects];
}

@end

The code above serves as your singleton DataStore. Calling [DataStore instance] anywhere in your code will return the shared instance of DataStore so everyone is working with the same data.

There are three collections in the DataStore above:

  1. fbFriends is a dictionary containing the FBGraphUser objects of you and all your friends, keyed on Facebook user ID.
  2. wallImage is populated with the Wall’s image objects that are returned from Parse.
  3. wallImageMap is a dictionary of Wall images, keyed on the object ID returned from Parse. This allows you to look up a specific Wall Image and update the comments if Parse notifies you of a new comment on an image.

Finally, the init and reset methods initialize and empty the collections.

Now it’s time to define your data objects. Add the following two new interface definitions at the top of DataStore.h:

@interface WallImage : NSObject
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) id objectId;
@property (nonatomic, strong) NSDictionary<FBGraphUser> *user;
@property (nonatomic, strong) NSDate *createdDate;
@property (nonatomic, strong) NSMutableArray *comments;
@end

@interface WallImageComment : NSObject
@property (nonatomic, strong) NSString *comment;
@property (nonatomic, strong) NSDictionary<FBGraphUser> *user;
@property (nonatomic, strong) NSDate *createdDate;
@end

These are your objects for the images and comments to display in the Image Wall.

Here’s a quick look at the WallImage properties:

  • image: Contains the uploaded image.
  • objectId: Contains the objectId generated by Parse to uniquely identify each image.
  • user: A dictionary that contains all pertinent Facebook data about the user that uploaded this image; it conforms to Facebook’s FBGraphUser protocol.
  • createdDate: Hold the timestamp of when the image was uploaded.
  • comments: A mutable collection of WallImageComments.

Now would be a perfect time to discuss the properties of WallImageComment:

  • comment: A string containing the submitted comment.
  • user: A dictionary of the user information detailing who submitted the comment; again, it conforms to FBGraphUser.
  • createdDate: Holds the timestamp of when the comment was submitted.

Open DataStore.m and add the implementation for both classes at the top of the file, just below the #import statement:

@implementation WallImage
- (id) init
{
    self = [super init];
    if (self) {
        // Init array of comments
        _comments = [[NSMutableArray alloc] init];
    }
    return self;
}
@end

@implementation WallImageComment

@end

As you can see, both of these classes provide simple ways to store data, and require very little implementation. All you’ve done is add the instantiation and initialization of the comments collection in WallImage.

You want your DataStore class to be accessible from anywhere in the app, so once again, it makes sense for the import to be in the pre-compile header. Open Supporting Files\FBParse-Prefix.pch and add the following import just before the import statement for Comms.h:

#import "DataStore.h"

The DataStore.h import MUST come before the Comms.h import since you’ll use the DataStore class in your Comms class later on.

Before you go any further, you need to ensure you reset the DataStore each time the user logs in, since you don’t want a user to have stale data from a previous user’s login.

Open FBLoginViewController.m and add the following code to loginPressed:, just before you issue the login: call to the Comms class:

// Reset the DataStore so that we are starting from a fresh Login
// as we could have come to this screen from the Logout navigation
[[DataStore instance] reset];

Build and run the project to confirm that you have no compilation issues with your new DataStore singleton. Nothing has changed with the UI — but you’ll get to that real soon!

Retrieving and Storing the Friends List

Before you can retrieve the images for the Image Wall from Parse, you need to store the FBGraphUser details of you and your friends in the DataStore. This is so you can know who wall images should be shared with.

Open Comms.m add the following code to login:, directly after the line that saves the PFUser object with saveInBackground:

// Add the User to the list of friends in the DataStore
[[DataStore instance].fbFriends setObject:me forKey:me.id];

This stores the user’s FBGraphUser object into the DataStore’s fbFriends list. Yes, you’re not really your own friend, but it’s much easier to retrieve all the images from Parse when everyone is grouped into one list.

Now to add your list of friends. Remove the following lines from the end of login:

// Callback - login successful
if ([delegate respondsToSelector:@selector(commsDidLogin:)]) {
	[delegate commsDidLogin:YES];
}

Now, add the following lines immediately after the point where you save the PFUser object:

// 1
FBRequest *friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
		NSDictionary* result,
		NSError *error) {
	// 2
	NSArray *friends = result[@"data"];
	for (NSDictionary<FBGraphUser>* friend in friends) {
		NSLog(@"Found a friend: %@", friend.name);
		// 3
		// Add the friend to the list of friends in the DataStore
		[[DataStore instance].fbFriends setObject:friend forKey:friend.id];
	}
						
	// 4
	// Callback - login successful
	if ([delegate respondsToSelector:@selector(commsDidLogin:)]) {
		[delegate commsDidLogin:YES];
	}
}];

Take a look at the code above in detail, comment by comment:

  1. Build a Facebook Request object to retrieve your friends from Facebook.
  2. Loop through the array of FBGraphUser objects data returned from the Facebook request.
  3. Add each friend’s FBGraphUser object to the friends list in the DataStore.
  4. The success callback to the delegate is now only called once the friends request has been made.

Build and run your project; login as usual and you’ll see a list of your friends in the log output, as shown below:

FBParse[2171:907] Found a friend: Steve Anthony
FBParse[2171:907] Found a friend: Swetha Shekhar
FBParse[2171:907] Found a friend: David Edi
FBParse[2171:907] Found a friend: Julian Meyer
Toby Stephens

Contributors

Toby Stephens

Author

Over 300 content creators. Join our team.