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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Integrating Facebook and Parse Tutorial: Part 2
45 mins
- Getting Started
- Retrieving and Storing the Friends List
- Retrieving Images from Parse
- Going to the Background
- Displaying Images
- Asynchronous Downloads and Notifications
- Retrieving User Profile Pictures
- Adding Comments to Images
- Saving Comments to Parse
- Using the New Comment Field
- Uploaded Image Notifications
- Polishing Your App
- Where To Go From Here?
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:
-
fbFriends
is a dictionary containing the FBGraphUser objects of you and all your friends, keyed on Facebook user ID. -
wallImage
is populated with the Wall’s image objects that are returned from Parse. -
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 ofWallImageComment
s.
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 toFBGraphUser
. -
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:
- Build a Facebook Request object to retrieve your friends from Facebook.
- Loop through the array of FBGraphUser objects data returned from the Facebook request.
- Add each friend’s FBGraphUser object to the friends list in the DataStore.
- 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