Integrating Facebook and Parse Tutorial: Part 1

Learn how to make an app to share photos with your Facebook friends, using Parse to easily create a back end for your app. By Toby Stephens.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Logging Out

Now that you can log into the app, take a moment to browse through the various views. The Upload navigation item takes you to the Upload screen – but this doesn’t work yet. The Logout navigation item takes you back to the login screen.

Hmm, that’s not really what you want to do, is it? Currently, tapping the Logout button simply pops the Image Wall view controller from the navigation stack. You’ll need to implement the calls to log the user out of Parse.

Open ImageWallViewController.m and add the following line to the top of logoutPressed:

[PFUser logOut];

Hey — that was much easier than expected. This is the only method you need to call to clean up your user on Parse and log out of Facebook.

Ordinarily, you would allow PFUser to persist between launches of the app so that the app would automatically log the user in when the app starts. However, since the login is an important part of this tutorial, you’ll need to call logOut as well when the app loads.

Open FBLoginViewController.m and add the following to the bottom of viewDidLoad:

// Ensure the User is Logged out when loading this View Controller
// Going forward, we would check the state of the current user and bypass the Login Screen
// but here, the Login screen is an important part of the tutorial
[PFUser logOut];

Build and run again; log in to the app as before and click Logout and you will return to the Login screen. When you run the app again, you’ll be prompted to re-login, since logOut was called when the main view was loaded.

Uploading Images

Your app now logs in and out of Facebook properly; now it’s time to upload some images.

In order to share an image with your friends, Parse needs to associate your Facebook ID with your Parse User. This way, if you have a list of Facebook IDs of friends, you can find the Parse Users to share the image with.

Unfortunately, when Parse stores the logged-in user in its Users table, it doesn’t store the Facebook user ID as a field; instead, it stores Facebook authentication data as an object. So you’ll have to put the user data into the model yourself.

Open up Comms.m and replace the following code in login:

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

…with this code:

[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
	if (!error) {
		NSDictionary<FBGraphUser> *me = (NSDictionary<FBGraphUser> *)result;
		// Store the Facebook Id
		[[PFUser currentUser] setObject:me.id forKey:@"fbId"];
		[[PFUser currentUser] saveInBackground];
	}
				
	// Callback - login successful
	if ([delegate respondsToSelector:@selector(commsDidLogin:)]) {
		[delegate commsDidLogin:YES];
	}
}];

The code above sends a request to Facebook for all of the user’s details including their Facebook id. If no error is received from the request, then take the FBGraphUser from the returned object and get the value of the Facebook user ID stored in the me.id element.

[PFUser currentUser] provides you with the currently logged-in Parse user. You then add the Facebook ID to the current user’s dictionary of fields and issue a saveInBackground command on the PFUser object. Your Parse app now has all the information about the logged-in user — including the user’s Facebook ID — needed to run future queries.

Build and run the app, and log in as usual.

To check that it worked, go to http://parse.com in your browser and choose your app name under the Dashboard entry in the drop down menu in the top right hand corner.

ts_FBParse_parse7

In the next screen, select Data Browser at the top and you will see the User’s data, which includes your new fbId field that contains your Facebook user ID:

ts_FBParse_parse8

Leave this browser page open as you will return to this page shortly to view your uploaded images.

Uploading Images with Comments

On a recent Sunday afternoon stroll, you were surprised to learn that the Loch Ness Monster was alive and well and living in a nearby stretch of canal. You jumped at the chance to snap this once in a lifetime photo, and now you can’t wait to share this experience with your Facebook friends through your new app.

To send the image and a comment to Parse you’ll use your Comms class. Open Comms.h and add the following method declaration:

+ (void) uploadImage:(UIImage *)image withComment:(NSString *)comment forDelegate:(id<CommsDelegate>)delegate;

Then add the following callback methods to the CommsDelegate protocol at the top of the file:

- (void) commsUploadImageProgress:(short)progress;
- (void) commsUploadImageComplete:(BOOL)success;

Parse provides you with an upload progress callback as your image uploads. You need to pass this on to the delegate using commsUploadImageProgress: so that your app can display the upload progress to the user. Once the upload is complete, you call commsUploadImageComplete: so that the delegate knows the image upload is complete.

Open up Comms.m and add the following method:

+ (void) uploadImage:(UIImage *)image withComment:(NSString *)comment forDelegate:(id<CommsDelegate>)delegate
{
    // 1
    NSData *imageData = UIImagePNGRepresentation(image);
	
    // 2
    PFFile *imageFile = [PFFile fileWithName:@"img" data:imageData];
    [imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        if (succeeded) {
	    // 3
            PFObject *wallImageObject = [PFObject objectWithClassName:@"WallImage"];
            wallImageObject[@"image"] = imageFile;
            wallImageObject[@"userFBId"] = [[PFUser currentUser] objectForKey:@"fbId"];
            wallImageObject[@"user"] = [PFUser currentUser].username;
			
            [wallImageObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                if (succeeded) {
			// 4
			PFObject *wallImageCommentObject = [PFObject objectWithClassName:@"WallImageComment"];
			wallImageCommentObject[@"comment"] = comment;
			wallImageCommentObject[@"userFBId"] = [[PFUser currentUser] objectForKey:@"fbId"];
			wallImageCommentObject[@"user"] = [PFUser currentUser].username;
			wallImageCommentObject[@"imageObjectId"] = wallImageObject.objectId;
					
			[wallImageCommentObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
				// 5
				if ([delegate respondsToSelector:@selector(commsUploadImageComplete:)]) {
					[delegate commsUploadImageComplete:YES];
				}
			}];
                } else {
			// 6
			if ([delegate respondsToSelector:@selector(commsUploadImageComplete:)]) {
				[delegate commsUploadImageComplete:NO];
			}
                }
            }];
        } else {
		// 7
		if ([delegate respondsToSelector:@selector(commsUploadImageComplete:)]) {
			[delegate commsUploadImageComplete:NO];
		}
        }
    } progressBlock:^(int percentDone) {
	// 8
	if ([delegate respondsToSelector:@selector(commsUploadImageProgress:)]) {
		[delegate commsUploadImageProgress:percentDone];
	}
    }];
}

There’s a fair bit of code here, but the points below describe what you do in each step:

  1. Get the image data for uploading.
  2. Convert the image data into a Parse file type PFFile and save the file asynchronously.
  3. If the save was successful, create a new Parse object to contain the image and all the relevant data (the user’s name and Facebook user ID). The timestamp is saved automatically with the object when it is sent to Parse. Save this new object asynchronously.
  4. If the save was successful, save the comment in another new Parse object. Again, save the user’s name and Facebook user ID along with the comment string.
  5. Once this is all done, report success back to the delegate class.
  6. If there was an error saving the Wall Image Parse object, report the failure back to the delegate class.
  7. If there was an error saving the image to Parse, report the failure back to the delegate class.
  8. During the image upload, report progress back to the delegate class.

Now you need to call your new upload method from the upload screen.

Open UploadImageViewController.m and add the following call to the end of uploadImage:

// Upload the image to Parse
[Comms uploadImage:self.imgToUpload.image withComment:_txtComment.text forDelegate:self];

You’ll notice when you add this method Xcode presents a warning. Can you figure out the source of the warning?

[spoiler]It’s because the UploadImageViewController class does not conform to the CommsDelegate protocol. The Comms method uploadImage:withComment:forDelegate: expects a CommsDelegate, which it’s not getting.[/spoiler]

To fix this, add CommsDelegate to the protocols in the class extension at the top of UploadImageViewController.m, like so:

@interface UploadImageViewController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextViewDelegate, CommsDelegate>

The warning is gone, but before you test this, you need to handle those callbacks from the Comms class.

Still in UploadImageViewController.m, add the following method:

- (void) commsUploadImageComplete:(BOOL)success
{
	// Reset the UI
	[_vProgressUpload setHidden:YES];
	[_btnUpload setEnabled:YES];
	[_lblChooseAnImage setHidden:NO];
	[_imgToUpload setImage:nil];
	
	// Did the upload work ?
	if (success) {
		[self.navigationController popViewControllerAnimated:YES];
	} else {
		[[[UIAlertView alloc] initWithTitle:@"Upload Error"
					    message:@"Error uploading image. Please try again."
					   delegate:nil
				  cancelButtonTitle:@"Ok"
				  otherButtonTitles:nil] show];
	}
}

The code above resets the UI to be ready for the next image upload. If the upload is successful, the view controller is popped and returns the user to the Image Wall screen. If the upload fails, the code alerts the user of the failure.

All that’s left to add is the progress indicator. Add the following method to UploadImageViewController.m:

- (void) commsUploadImageProgress:(short)progress
{
	NSLog(@"Uploaded: %d%%", progress);
	[_progressUpload setProgress:(progress/100.0f)];
}

The upload progress is displayed as a percentage, which fits nicely into the progress bar on the screen.

Build and run your project; select an image from the upload screen, add a comment and tap the Send To Image Wall button.

Upload in progress

Upload in progress

Upload in progress

You can either navigate to an image on the web using Mobile Safari, or simply drag and drop an image from Finder into the Simulator. Press and hold on the image in the simulator and you can then save it to the iOS simulator’s photo album.

Note:On the iPhone simulator, you won’t be able to take a picture with the camera for obvious reasons. You can use a device for this stage, but if that isn’t possible, then you will need to save some images to the simulator instead.

You can either navigate to an image on the web using Mobile Safari, or simply drag and drop an image from Finder into the Simulator. Press and hold on the image in the simulator and you can then save it to the iOS simulator’s photo album.

The image upload progress indicator will change as the image is uploaded; when the image is completely uploaded, the view controller is reset and popped and you will be returned to the Image Wall, safe in the knowledge that your images and comment are saved in your Parse app.

Or are they?

Toby Stephens

Contributors

Toby Stephens

Author

Over 300 content creators. Join our team.