Facebook Parse: Working with Parse API Objects

Facebook’s acquisition of Parse gives the social giant a big mobile boost by making it easier for developers to create apps for the Facebook platform. In a three-part series, we take a deep dive into Parse.

  • In the first installment of our series, we explain why this platform makes an excellent minimum viable product (MVP) cloud solution that enables developers to focus on code instead of server infrastructure. We show you how to create a new project on Parse and explore the various dashboard features.
  • In this, the second part of our series, we integrate Parse into a sample iOS project, explaining the essential concepts of working with Parse Objects and Relationships.
  • In the final part of our series, we work with Push Notifications using Parse, as well as with Parse Analytics and Crash Reporting, as we complete our sample iOS project.

In our previous article, we guided you through the features and benefits of Parse API, as well as the process of setting up your first Parse API account. In this article, you will actually work with Parse in Xcode, from integrating the SDK library in your project to working and interacting with the Parse objects.

For the sample code, we won’t create a new project from scratch but will “Parse-ify” an existing Apple project called The Elements, which can be downloaded here. The Elements is a sample application that provides access to the data contained in the Periodic Table of the Elements (which, of course, catalogs all the known atomic elements in the universe). The Elements provides this data in multiple formats, allowing users to sort the data by name, atomic number, symbol name, and the elements’ physical states at room temperature. We decided to use The Elements as a demonstration app because it is data-driven and uses localized plist files, which makes demonstrating the transition to cloud-based data access clearer and easier.  

Integrate Parse into your project

Before we get started, log onto your parse.com account and visit the Settings->Keys tab, as we will need the Application ID and Client Key.

Next, download the Parse SDK, from www.parse.com/docs/downloads, and make sure you choose iOS. When you finish downloading the files, unzip the downloaded file, then drag the folder with all the .framework files in Finder into the Xcode project. From within Xcode, right-click and select “Add files to TheElements” from the frameworks folder, and select the Parse frameworks folder and all the included framework files.
 

Parse

Next, we need to add the dependencies. Select the project file in Xcode, then the target app, then Build Phases, as illustrated below.
 

Parse

Click on the + button below “Link Binary with Libraries,” and add the following libraries:

  • AudioToolbox.framework
  • CFNetwork.framework
  • CoreGraphics.framework
  • CoreLocation.framework
  • MobileCoreServices.framework
  • QuartzCore.framework
  • Security.framework
  • StoreKit.framework
  • SystemConfiguration.framework
  • libz.dylib
  • libsqlite3.dylib

Finally, to link your specific account to the project, open up the file TheElementsAppDelegate.m and add the following, right before the final line in the method, making sure to insert the application ID and client key from the steps we did earler:

#import <Parse/Parse.h>
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- ....

- // [Optional] Power your app with Local Datastore. For more info, go to // https://parse.com/docs/ios_guide#localdatastore/iOS [Parse enableLocalDatastore];

// Initialize Parse.
[Parse setApplicationId:@"YourApplicationId"
              clientKey:@"YourClientKey"];

// [Optional] Track statistics around application opens.
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];

// ...
return YES;
}

And that’s it! Provided you can Build & Run the project without errors, you have successfully set up Parse.

The 101 of Parse Objects

Now you are ready to learn more about how Parse objects work.

All data in Parse is persisted in what is called PFObjects, and when we query the Parse database, we get back a set of PFObjects, which are key-value encapsulated objects like the data we defined when we created The Elements. Each element is a PFObject:

name: Ruthenium,
state: Solid,
symbol: Ru

Working with PFObjects, we can read, save, update, and delete PFObjects. To save an object, we would do something like the following:

PFObject *element = [PFObject objectWithClassName:@"Element"];
element[@“name”] = @“Ruthenium”; element[@“discoveryYear”] = @“1844 A.D.”; element[@“state”] = @“solid”; element[@“symbol”] = @“Ru”; element[@“atomicNumber”] = @44; [element saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (succeeded) { // The object has been saved. } else { // There was a problem, check error.description } }];

We only included a few of the properties of the elements, for brevity’s sake.

In our tutorial, we will only be reading from the Parse database, but we thought we’d give you a sample of how easy it is to even write to the database.

Let’s get coding…

Before we start fetching our objects, we will begin by making *AtomicElement.h a subclass of PFObject, along with implementing the PFSubclassing, as follows:

#import <Foundation/Foundation.h>
#import <Parse/Parse.h>
#import <Parse/PFSubclassing.h>

@interface AtomicElement : PFObject<PFSubclassing>

@property (nonatomic, strong) NSNumber *atomicNumber;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *symbol;
@property (nonatomic, strong) NSString *state;
@property (weak, readonly) UIImage *flipperImageForAtomicElementNavigationItem;
@property (weak, readonly) UIImage *stateImageForAtomicElementTileView;
@property (weak, readonly) UIImage *stateImageForAtomicElementView;
@property (nonatomic, strong) NSString *atomicWeight;
@property (nonatomic, strong) NSNumber *group;
@property (nonatomic, strong) NSNumber *period;
@property (nonatomic, strong) NSString *discoveryYear;

+ (NSString*)parseClassName;

In the implementation file AtomicElements.m, add the following two methods to the code, right after @dynamic radioactive:

...
+ (void)load {
    [self registerSubclass];
}

+ (NSString *)parseClassName {
    return @"AtomicElement";
}
...

Retrieving the Parse Objects

Now, we turn our attention to the PeriodicElements.h and PeriodicElements.m header/implementation files, which will be where we will make the conversion from using the .plist file to getting the elements from Parse. In the header file, we will make two insertions. Above the @interface declaration, add:

typedef void (^ParseDataReady)(void);

Just above @end, add:

-(void)getDataFromParseWithBlock:(ParseDataReady)block;

We will be using blocks so that we can notify the caller when we have successfully retrieved the Parse objects, enabling us to refresh our tableViews. Now, in the PeriodicElements.m implementation file, we will be doing most of the work. Make the following changes:

...
- (void)setupElementsArray {

    // create dictionaries that contain the arrays of element data indexed by
    // name
    self.elementsDictionary = [NSMutableDictionary dictionary];
    // physical state
    self.statesDictionary = [NSMutableDictionary dictionary];
    // unique first characters (for the Name index table)
    self.nameIndexesDictionary = [NSMutableDictionary dictionary];

    // create empty array entries in the states Dictionary or each physical state
    [self.statesDictionary setObject:[NSMutableArray array] forKey:@"Solid"];
    [self.statesDictionary setObject:[NSMutableArray array] forKey:@"Liquid"];
    [self.statesDictionary setObject:[NSMutableArray array] forKey:@"Gas"];
    [self.statesDictionary setObject:[NSMutableArray array] forKey:@"Artificial"];

    // read the element data from the plist --Deprecated in favor of Parse
//  NSString *thePath = [[NSBundle mainBundle]  pathForResource:@"Elements" ofType:@"plist"];
//  NSArray *rawElementsArray = [[NSArray alloc] initWithContentsOfFile:thePath];       
}

We separated the initialization method so that we won’t be calling the .plist file, just setting up the statesDictionary. We will now implement the (void)getDataFromParseWithBlock:(ParseDataReady)block method we defined in the header:

- (void)getDataFromParseWithBlock:(ParseDataReady)block{
    PFQuery *query = [AtomicElement query];
    //[query whereKey:@"courseName" isEqualTo:PFUser.currentUser.courseName];
    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if (!error) {
            // iterate over the values in the raw elements dictionary
            for (AtomicElement *anElement in objects)
            {
                // store that item in the elements dictionary with the name as the key
                [self.elementsDictionary setObject:anElement forKey:anElement.name];

                // add that element to the appropriate array in the physical state dictionary
                [[self.statesDictionary objectForKey:anElement.state] addObject:anElement];

                // get the element's initial letter
                NSString *firstLetter = [anElement.name substringToIndex:1];
                NSMutableArray *existingArray = [self.nameIndexesDictionary valueForKey:firstLetter];

                // if an array already exists in the name index dictionary
                // simply add the element to it, otherwise create an array
                // and add it to the name index dictionary with the letter as the key
                //
                if (existingArray != nil)
                {
                    [existingArray addObject:anElement];
                } else {
                    NSMutableArray *tempArray = [NSMutableArray array];
                    [self.nameIndexesDictionary setObject:tempArray forKey:firstLetter];
                    [tempArray addObject:anElement];
                }

                // create the dictionary containing the possible element states
                // and presort the states data
                self.elementPhysicalStatesArray = [NSArray arrayWithObjects:@"Solid", @"Liquid", @"Gas", @"Artificial", nil];
                [self presortElementsByPhysicalState];

                // presort the dictionaries now
                // this could be done the first time they are requested instead
                //
                [self presortElementInitialLetterIndexes];

                self.elementsSortedByNumber = [self presortElementsByNumber];
                self.elementsSortedBySymbol = [self presortElementsBySymbol];
                if (block)
                    block();
            }


        }
    }];
}

That’s pretty much all the changes we need to do for this class, but, following it, you can see we have taken out some of the lines from the setupElementsArray and added them here. We use block [query findObjectsInBackgroundWithBlock:^(NSArray objects, NSError error) to retrieve an array of objects, which we then loop through, instead of looping through the plist file. We keep everything else the same as it was previously, including assigning and sorting.

We have one more small change to make. In ElementsTableViewController.m, which is used to display the elements, we call the block method we created in PeriodicElements, getDataFromParseWithBlock. We will do that inside the viewWillAppear method, so change that method as follows:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:YES];

    [[PeriodicElements sharedPeriodicElements] getDataFromParseWithBlock:^{
        // force the tableview to load
        [self.tableView reloadData];
    }];

}

So with that block, we ensure that we reload the tableView only when we successfully get the Parse objects asynchronously, within the convenience of a block.

Download the complete project

You can download the complete project we just worked on, by going to https://github.com/doronkatz/TheElements.git and cloning the repository.

Conclusion

We modified Apple’s project to demonstrate a simple query call to Parse. Nothing too elaborate, but with this in mind, feel free to expand and work with some of the more advanced query features of Parse. We also recommend pulling the github repository above and adding your own contributions.

As extra credit, try implementing the Local Datastore features to allow the app to load the PFObjects even without a network connection.

In our final article of the series, we will demonstrate the powerful features of Push Notifications and Analytics.

Doron Katz A keen passion for emerging technologies, practices and methodologies, Doron embraces the vision of lean development with continuous customer development. Consultant for various startups, as a Project and Product Manager, with a mobile engineering background in iOS, and over 10 years of professional web development experience.

Comments