iOS Programming Recipe 32: Easy Web Back-End with Parse

As a developer you quickly find out that many of the more useful apps require a web backend of some sort. This presents a problem for developers looking to build something without wanting to create a website and a RESTful API service to support their app. There are a number of platforms that seek to solve this problem, but the one I’ve been playing with lately is Parse. Parse so far has impressed me for a number of reasons, but mainly because it’s free for quite a few requests and really easy to use. This tutorial will walk you through getting started with parse and demonstrate some of the nifty features.

Assumptions

  • You are fairly comfortable with API’s
  • You can create outlets and actions
  • You can add a framework to a project
  • You have a basic understanding of block methods

Getting The Application Setup with Parse

For this demonstration we’ll create a simple map application that will come complete with a login. So create a new single view application and name it whatever you like.

Since Parse already has really good instruction on getting started. I recommend going there first to create an account and set up your project with the Parse framework. Go to the Parse Homepage and create a new account and then find the quick start area. There are many options such as push notifications, but for this tutorial we’ll focus on data. Figure 1 shows the quick start install guide that you should be presented with. Parse gives you the framework. You can drag and drop it into your framework folder. Also make sure you choose the existing project option in the quick start.

Figure_1

Figure 1: The Parse quick start install guide

Once you are all set up with the parse framework, you can start using parse. To get started we’re going to add some login functionality.

Using Parse for Dirt Simple Login Functionality

Open up the ViewController.m file and start by importing the Parse framework and Mapkit as well as declaring the login and signup delegates. Code Chunk 1 shows this step. Since we’ll be using a keyboard that we want to dismiss, we’ll also declare the keyboard delegate. You will need to add the Mapkit framework to the project as well.

Code Chunk 1: Declaring the Parse login and signup delegates


1
2
3
4
5
6
7
8
9
10
11
12
#import "ViewController.h"
#import <Parse/Parse.h>
#import <MapKit/MapKit.h>


@interface ViewController () <PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, UITextFieldDelegate>

@end

@implementation ViewController

//...

Next we’ll want to create a couple properties. One property will be for the Login Controller. Parse actually gives us a nice login control to use. They also give us a class for handling users. Code Chunk 2 shows these new properties added to the ViewController.m interface section.

Code Chunk 2: Adding properties for users and the login view controller


1
2
3
4
5
6
@interface ViewController () <PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, UITextFieldDelegate>

@property (strong, nonatomic) PFLogInViewController *login;
@property (strong, nonatomic) PFUser *user;

@end

Now a logical place to present the user with a login would be when the view appears. Add a new method called “presentLoginViewController” and fill it with the code as shown in *Code Chunk 3**. This bit of code first checks to see if the current user is available, if not it will create a login view controller. The login fields are configurable, for this example we have chosen to show the email and password login, but you can easily add more buttons like a Facebook login by adding “PFLogInFieldsFacebook”. You can find more of these options in Parse’s very good documentation here. Next we present the view controller as well as set delegates for both the login and the signUpViewController.

Code Chunk 3: Setting up the login and signup view controller


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-(void)viewDidAppear:(BOOL)animated
{
    [self presentLoginViewController];
}

//...

-(void)presentLoginViewController
{
    //Present login view controller if user not logged in
    if(![PFUser currentUser])
    {
        self.login = [[PFLogInViewController alloc] init];
        self.login.fields = PFLogInFieldsUsernameAndPassword | PFLogInFieldsSignUpButton | PFLogInFieldsLogInButton;

        self.login.delegate = self;
        self.login.signUpController.delegate = self;

        [self presentViewController:self.login animated:YES completion:nil];

    }
}

Don’t forget to call this method from the viewDidAppear method a shown in Code Chunk 4.

Code Chunk 4: Calling the method created in Code Chunk 4 from the viewDidAppear method


1
2
3
4
-(void)viewDidAppear:(BOOL)animated
{
    [self presentLoginViewController];
}

At this point you could run the application and a nice login will be presented. Once you create a user name and password however, there would be nothing but a blank screen. Let’s fix that now.

Building the Interface

Switch over to the storyboard and drag out 3 labels, 3 buttons, a text field, and a map view and arrange them as shown in Figure 2.

Figure_2

Now you will need to create outlets for the map view, the userName label, and the text field. The three buttons will need actions assigned to them. Label them “mapView”,”userFullNameLabel”,”userFullNameInput,” “refreshMap,” “logout,” and “updateUserFullName” respectively. Code Chunk 4 show these changes to the implementation file. Notice in the code here that we also imported the MapKit framework.

Code Chunk 4: Adding outlets and actions to the UI elements


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#import "ViewController.h"
#import <Parse/Parse.h>
#import <MapKit/MapKit.h>


@interface ViewController () <PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, UITextFieldDelegate>

@property (strong, nonatomic) PFLogInViewController *login;
@property (strong, nonatomic) PFUser *user;


@property (weak, nonatomic) IBOutlet UILabel *userFullNameLabel;
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (weak, nonatomic) IBOutlet UITextField *userFullNameInput;

@end

//... code omitted

- (IBAction)refreshMap:(id)sender
{
    //Todo
}
- (IBAction)updateUserFullName:(id)sender
{
   //Todo
}
- (IBAction)logout:(id)sender
{
   //Todo
}

Now that we have some interface objects to work with, we can start coding some functionality. We’ll start by filling in the viewDidLoad method.

Accessing Information stored on Parse

In the viewDidLoad method we’ll first check to see if the user is logged in. Then if the user is logged in, we’ll set the user property to the logged in user. Using the nifty Parse class block method, geoPointForCurrentLocationInBackground, we’ll get the users location. The location returns a PFGeoPoint which is a special object that parse uses to save location. Once the location is received, we’ll save this location in the Parse database under the key “currentLocation” as well as set the map view with a specified region centered on this location. The last thing we’ll do is check the data base for the user’s full name and set the welcome label to this value. Code Chunk 5 shows the modification to the viewDidLoad method.

It’s worth pointing out here that if you use the Parse class method saveInBackground, new columns get added to your table if it does not already exist (such as “currentLocation”). Of course, you should already have your database structure for deployment, but it’s a handy feature for development. It’s also worth pointing out that we’re not actually accessing a database here so much as an online user class that holds a collection of objects similarly to an NSDictionary.

Code Chunk 5: Updating the viewDidLoad method to include mapping and retrieval of location and user full name


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (void)viewDidLoad
{
    [super viewDidLoad];

    if([PFUser currentUser])
    {
    self.user = [PFUser currentUser];

        [PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
            NSLog(@"User is currently at %f, %f", geoPoint.latitude, geoPoint.longitude);


            [self.user setObject:geoPoint forKey:@"currentLocation"];
            [self.user saveInBackground];
            [self.mapView setRegion:MKCoordinateRegionMake(CLLocationCoordinate2DMake(geoPoint.latitude, geoPoint.longitude),MKCoordinateSpanMake(0.01, 0.01))];

            [self refreshMap:nil];
        }];

        self.userFullNameLabel.text = [self.user objectForKey:@"userFullName"];

    }

    self.userFullNameInput.delegate = self;
}

In Code Chunk 5 we call the refreshMap method, but we have not filled it in yet. Let’s do that now.

Code Chunk 6 shows the completion of the refreshMap method. First you’ll see we create a PFGeoPoint using the current map view center coordinate. Then we query the user class for all users that are near that location. Finally we use the query to find those users using the findObjectsInBackgroundWithBlock method. Inside the block method we check for errors and log them if present. For every returned object (IE users near the map view center) we create a map annotation. The annotation will use the user’s full name as the title.

Code Chunk 6: Implementation of the refreshMap action method


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (IBAction)refreshMap:(id)sender
{

    PFGeoPoint *geoPoint = [PFGeoPoint geoPointWithLatitude:self.mapView.centerCoordinate.latitude longitude:self.mapView.centerCoordinate.longitude];
    PFQuery *query = [PFUser query];

    //query parse database for user
    [query whereKey:@"currentLocation" nearGeoPoint:geoPoint withinMiles:10.0f];

    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
     {
         if(error)
         {
             NSLog(@"%@",error);
         }
         for (id object in objects)
         {

             MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
             annotation.title = [object objectForKey:@"userFullName"];
             PFGeoPoint *geoPoint= [object objectForKey:@"currentLocation"];
             annotation.coordinate = CLLocationCoordinate2DMake(geoPoint.latitude,geoPoint.longitude);

             [self.mapView addAnnotation:annotation];

         }



     }];

}

Now let’s fill in the other two action methods. The updateUserFullName method will simply check to see if there is a user property. If there is it will set the user’s full name on the Parse server as well as update the user full name label. We also call the saveInBackGround method to ensure the column is created if not already. Code Chunk 7 shows this full implementation.

Code Chunk 7: Implementing the updateUserFullName method


1
2
3
4
5
6
7
8
9
10
11
12
13
- (IBAction)updateUserFullName:(id)sender
{
    if(self.user)
    {

        [self.user setObject:self.userFullNameInput.text forKey:@"userFullName"];

        self.userFullNameLabel.text = self.userFullNameInput.text;

        [self.user saveInBackground];

    }
}

The last action method, logout, will be pretty simple. All you do here is call the user logout class method and then present the login view controller again by calling the presentLoginViewController method we made earlier. Code Chunk 8 shows this bit of code.

Code Chunk 8: Implementing the logout method


1
2
3
4
5
- (IBAction)logout:(id)sender
{
    [PFUser logOut];
    [self presentLoginViewController];
}

Adding the Delegate Methods

The last thing we need to do is add a couple of delegate methods. One delegate method will be used to dismiss the login view controller. The other delegate method will be needed to dismiss the keyboard when you press the done button. Code Chunk 9 shows these two methods.

Code Chunk 9: Implementing the keyboard and login view controller delegate methods


1
2
3
4
5
6
7
8
9
10
11
- (void)logInViewController:(PFLogInViewController *)logInController didLogInUser:(PFUser *)user
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{

    [textField resignFirstResponder];
    return NO;
}

If you haven’t already you will need to select the text field and change the keyboard return key to “Done” from the attributes inspector as shown in Figure 3.

Figure_3

Figure 3: Setting the keyboard return key from the attributes inspector

Now you should be set to run your application. Make sure you open your simulator and set the device location from the Debug->Location menu. For this example maybe an Apple bike ride is ok. If you would like you can also logout and create another user to see if more than location shows up on the map. Figure 4 shows the complete application.

Figure_4

Figure 4: The Complete Application

A few more things

Throughout this recipe I’ve been showing you how to manipulate the user class, which is a special class in Parse that is already included. You can create your own class if you have some other non-user related data you would like to save. Code Chunk 10 is a copy of the example given on Parse’s docs for creating a new object using PFObject. Again, You can just simply create the object in your project and the object will be created on the server when you save.

Code Chunk 10: The Parse.com copied example for creating a PFObject


1
2
3
4
5
 PFObject *gameScore = [PFObject objectWithClassName:@"GameScore"];
gameScore[@"score"] = @1337;
gameScore[@"playerName"] = @"Sean Plott";
gameScore[@"cheatMode"] = @NO;
[gameScore saveInBackground];

Querying is a little different when it’s an object other than the user. The user has a special class method, [PFUser query]. Normal objects are queried a little differently. Again , I’ll just restate a copied Parse example here in Code Chunk 11.

Code Chunk 11 The Parse.com copied example for retrieving objects


1
2
3
4
5
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query getObjectInBackgroundWithId:@"xWMyZ4YEGZ" block:^(PFObject *gameScore, NSError *error) {
    //Do something with the returned PFObject in the gameScore variable.
    NSLog(@"%@", gameScore);
}];

Accessing these individual object values is pretty much the same as we’ve shown you in this recipe. Again, I encourage you to go take a look at the parse documentation.

The last thing I’ll briefly mention is that you can create objects and object keys directly from parse’s web UI using the dashboard once you are signed in. The Data browser for this example project looks something like Figure 5

Figure_5

Figure 5: The Parse.com data browser

Now Ya’ll have no excuses for not getting your web-based app up and running. Hope everyone enjoyed this tutorial.

The source for this code is posted on our Github account, Be sure to replace the ApplicationId and clientKey with your own. You may also need to make sure you give the project the same name in Parse.

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. Could you write a whole series about using the other Parse products for iOS development? You could cover topics like Push Notifications, Cloud Code, Cloud Modules, REST API, Social. I have not seen any iOS tutorials (other than the ones by Parse) about how to implement the other Parse products with iOS.

  2. Hey Joe great tutorial! Thumbs up

    • Important: There is a bug on parse quick start guide:

      following the guide you have to add these frameworks… BUT you have to add 2 more
      AudioToolbox.framework
      CFNetwork.framework
      CoreGraphics.framework
      CoreLocation.framework
      libz.dylib
      MobileCoreServices.framework
      QuartzCore.framework
      Security.framework
      StoreKit.framework
      SystemConfiguration.framework

      AND the missing two frameworks:
      – Social.framework
      – Accounts.framework

      It works for me 🙂

  3. Just found this site and tutorial. Thanks it’s pretty cool, keep up the good work.

  4. Joe, tutorials like yours make people like me (beginners) want to try harder. Thank you.
    I have been trying very hard to simply populate a map with parse geopoints without user location, geopoints nearby or other sorts of things. Just geopoints on a map. However, after trying many many things by grabbing ideas here and there on the web, I can’t do it. Would you know about any tutos that would help me do that? or maybe some advice?
    Thank you

    • Joe Hoffman says:

      Hi Terry,

      My Assumption is you already have a bunch of geopoints in your parse data store. Since you won’t be concerned with the user, you’ll have to query the for the object you want that contains the location (See Code Chunk 11). From there you should be able to obtain the geopoints and then use those geopoints to create annotations. You’ll notice in code chunk 6 I do something similar to what you’re asking for. The only difference is you’ll want to create your query a little different. IE

      PFQuery *query = [PFQuery queryWithClassName:@"YourDataStoreClassName"];

      instead of

      PFQuery *query = [PFUser query];

Speak Your Mind

*

css.php
Privacy Policy