Chapter    3

Parse Fundamentals

Parse has numerous features and functionality to help make a developer’s life easier. These features are covered throughout the book. This chapter focuses on the Parse iOS SDK.

In this chapter you learn about:

  • Interacting with the Parse PFObject class and subclasses
  • Retrieving data with the PFQuery class
  • Creating relations between objects with pointers, arrays, Parse relations, and joining tables
  • Loading and displaying images with the PFImageView class
  • Displaying images with the PFTableViewCell class
  • Querying and showing data with the PFQueryTableViewController class

Interacting with Objects: PFObject

If you’re an iOS developer, you are familiar with NSObject. NSObject is the root class of most Objective-C class hierarchies. PFObject is the root Parse iOS class. When developing an iOS app, you start with PFObject:

@interface PFObject : NSObject

What makes PFObject so easy to work with Parse’s back end is that each PFObject contains key/value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each PFObject—you simply set whatever key/value pairs you want, and Parse’s back end will store it.

Another nice feature is that Parse doesn’t limit how a new class is created. You go to your project home on the Parse web site, and add a new class from the Data browser, by importing data, or from a client. To create a new class from a client, remember to turn on client class creation in the settings page of your project home (see Figure 3-1).

9781484213186_Fig03-01.jpg

Figure 3-1. Enable “Allow client class creation” in the Parse console

For example, to create a Product class with properties such as title, subtitle, unit price, and price per unit (each, lbs, etc.), write the following code in Xcode:

PFObject *product = [PFObject objectWithClassName:@"Product"];
[product setObject:@"iOS eCommerce App Development with Parse" forKey:@"title"];
[product setObject:@(19.99) forKey:@"unitPrice"];
[product setObject:@"ea" forKey:@"priceUnit"];
[product setObject:@"A real world iOS app development with Parse" forKey:@"subtitle"];
[product save];

To save the class, you call a PFObject save method. If there is no class called “Product” on the Parse back end, Parse will create one for you. In the meantime, add a row of data for this class, as shown in Figure 3-2.

9781484213186_Fig03-02.jpg

Figure 3-2. Parse created a Product class with a row of records

If a class called “Product” already exists on the back end, executing the same code will add another row of data.

Although creating classes is easy, you can run into problems quickly. For example, each class object has little to do with the real business object you try to mimic. In this case, the Product is the real object you want to represent, but you only call it PFObject, not Product. Also, you have to remember all keys and set those keys with the proper values; it’s kind of like working with NSDictionary. Luckily, the Parse native PFObject subclass comes to the rescue.

Working with PFObject subclassing is even easier than working with classes. Here is how. In Xcode, create a new file and select Cocoa Touch Class; then name the file Product and make it a subclass of PFObject, as shown in Figure 3-3.

9781484213186_Fig03-03.jpg

Figure 3-3. Create a Product subclass of PFObject with Xcode

This creates two files: Product.h and Product.m.

In Product.h, you need to declare that the Product class needs to conform to the PFSubclassing protocol; then, create an NSObject with title, subtitle, unit price, and price unit properties as usual.

#import <Parse/Parse.h>
@interface Product : PFObject<PFSubclassing>
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property (nonatomic, assign) double unitPrice;
@property (nonatomic, copy) NSString *priceUnit;
@end

In Product.m, you need to import <Parse/PFSubclassing.h>:

#import "Product.h"
#import <Parse/PFObject+Subclass.h>

For this implementation, use the @dynamic keyword for your properties:

@dynamic title, subtitle, priceUnit, unitPrice;
And there is another required method to implement
+(NSString *)parseClassName
{
    return @"Product";
}

That’s it. Here is the complete code for the Product.m file:

#import "Product.h"
#import <Parse/PFObject+Subclass.h>
@implementation Product
@dynamic title, subtitle, priceUnit, unitPrice;

+(NSString *)parseClassName
{
    return @"Product";
}
@end

There is one more thing you need to do. In the AppDelegate.m file, declare the Product class before declaring anything else Parse related:

#import "AppDelegate.h"
#import <Parse/Parse.h>
#import "Product.h"

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [Product registerSubclass];
    [Parse setApplicationId:@"YOUR-PARSE-APPLICATION-ID"  clientKey:@"YOUR-PARSE-CLIENT-KEY"];
    return YES;
}

Basically, this is how you create a PFObject subclass. Using the Product class is now much easier and intuitive:

Product *product = [Product object];
product.title = @"iOS eCommerce App Development with Parse";
product.subtitle = @"A real world iOS app development with Parse";
product.priceUnit = @"ea";
product.unitPrice = 19.99;
[product save];

Working with an object constantly involves four types of action queries: create, read, update, and delete. Parse makes it easier for developers with its subclasses. Take the following Product subclass uses cases, for example.

  • Create—As you have seen, it’s easy to create a product object instance:
    Product *product = [Product object];
  • Read—Read properties of a product object can be done as usual, using the standard way to work with NSObject. For example, if you want to print the product’s title to the console, use:
    NSLog(@"%@", product.title);
  • Edit—Editing a product could mean changing the properties of a product, say, setting a new price for this product:
    product.unitPrice = 21.99;
  • It is very important to save the updated product into Parse, so you call:
    [product save];
  • Alternatively, you can also call the save method asynchronously—always the recommended approach for a better user experience:
    [product saveInBackground];
  • Delete—Like an NSObject instance, set this object to nil. However, you also need to let Parse know this object no longer exists, so call the delete method:
    [product delete];
  • Or call it asynchronously:
    [product deleteInBackground];

Note  For more information about PFObject and its APIs, got to https://parse.com/docs/ios/api/Classes/PFObject.html.

Retrieving Data: PFQuery

Since all of your data is stored on Parse, getting the data out of Parse is important. To do so, you query Parse using the PFQuery helper class.

PFQuery is a subclass of PFObject, and it makes the common query-related API calls easier. Say you want to create a PFQuery instance, you can use:

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

The built-in class method (+PFQuery *)queryWithClassName with a parameter of the class name indicates that the Product class is queried.

Parse provides many easy-to-remember and easy-to-use methods to help keep the query developer friendly.

Say, you want to retrieve all of your products, you can use:

NSArray *products = [query findObjects];

Or you can use an asynchronous method:

__block NSArray *products;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
        if (!error) {
            products = objects;
        }
 }];

By default, Parse will return the first 200 items of products stored on Parse. If you want to customize the number of items retrieved, use the PFQuery method: -(void)setLimit: (NSInteger)limit

[query setLimit:100];

If you only want to get a product with the title of “iOS eCommerce App Development with Parse,” use query constraint methods:

[query whereKey:@"title" equalTo:@"iOS eCommerce App Development with Parse"];

You can also customize the sort order of your returned objects. The default result is sorted with the createdAt property in ascending order. If you want to sort the returned items alphabetically by product title and in ascending order, use this:

[query orderByAscending:@"title"];

Combining those constraints, you can perform very powerful queries.

Note  For more information about PFQuery and its APIs, go to https://parse.com/docs/ios/api/Classes/PFQuery.html.

Creating Relations between Objects

Objects can have multiple relationships with other objects. A product normally belongs to a product category. This book is normally put in the Computer Programming category in Barnes & Noble. The Computer Programming category has many different programming books: a typical one-to-many relationship scenario.

Probably you have heard of other relationship types such as one-to-one and many-to-many. Parse provides four different ways to build relationships: pointers, arrays, Parse relations, and joining tables.

Using Pointers

Pointers are used for one-to-one and one-to-many relationships. For example, suppose you have a book and book category with a Product class and a ProductCategory class; this is how you can define the relationship using the PFObject subclasses in Product.h:

#import <Parse/Parse.h>
@class ProductCategory;
@interface Product : PFObject<PFSubclassing>
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property (nonatomic, assign) double unitPrice;
@property (nonatomic, copy) NSString *priceUnit;
@property (nonatomic, strong) ProductCategory *category;
@end

Say you have a Product Category object with title “Programming Books,” and you want the book to belong to this category, use this:

ProductCategory *category = [ProductCategory object];
category.title = @"Programming Book";

Product *product = [Product object];
product.category = category;
[product saveInBackground];

The nice thing about Parse is that if you save this product now, the category object of ProductCategory will automatically be saved to Parse without explicitly calling a save method.

Once you have built the one-to-many relationship between this book and the book category, and you want to show all books in the programming book category, use this query:

PFQuery *query = [PFQuery queryWithClassName:@"Product"];
[query whereKey:@"category" equalTo:category];
 [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
        if (!error) {

        }
 }];

Note  If you have a few custom query methods, it’s better to put them in the subclass instead of in the main view controller you will work with.

Using Arrays

Arrays are used in many-to-many relationships in much the same way that they are for one-to-many relationships. All objects on one side of the relationship will have an Array column containing several objects on the other side of the relationship. In the case of a book product, several authors can write a book. This is how to declare this relationship in the Product class:

#import <Parse/Parse.h>
@class ProductCategory;
@interface Product : PFObject<PFSubclassing>
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property (nonatomic, assign) double unitPrice;
@property (nonatomic, copy) NSString *priceUnit;
@property (nonatomic, strong) ProductCategory *category;
@property (nonatomic, strong) NSArray *authors;
@end

Suppose you have two Author objects:

product.authors = @[author1, author2];

When performing a Product query, if you also want to get the related authors, use a -(void)include PFQuery method. The following code shows how you can get all authors from a Product query, and print the first author’s name:

PFQuery *query = [PFQuery queryWithClassName:@"Product"];
[query includeKey:@"authors"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
        if (!error) {
            Product *product = objects[0];
            NSArray *authors = product.authors;
            PFUser *author = authors[0];
            NSLog(@"%@", [author objectForKey:@"name"]);
        }
 }];

Note  It’s important to use the includeKey method when you want to get the object data that the array contains.

Using Parse Relations

Parse relations, or PFRelation, works similarly to an NSArray of PFObjects. The difference is that you don’t need to download all the objects in a relation at once. This allows PFRelation to scale many more objects than the NSArray of the PFObject approach. One example is that a user may like many products. To save the products the user has liked, you can use PFRelation:

PFObject *user = [PFObject object];
PFRelation *relation = [user relationForKey:@"likes"];
[relation addObject:product];
[user saveInBackground];

To remove a product from the user’s favorites list, use:

[relation removeObject:product];

By default, the list of products that the user has collected is not downloaded. You can get the list of products in this way:

[[relation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
        if (!error) {
            Product *product = objects[0];
            NSLog(@"%@", product.title);
        }
 }];

Joining Tables

Using the join tables method helps you get more information besides the relationship. Take the following use case, for example: if you want to build a relationship between a user and the product he reviewed, you care about who writes the review, which product is reviewed, and the review content. The ProductReview PFObject subclass for this use case looks like this:

#import <Parse/Parse.h>
@class Product;
@interface ProductReview : PFObject
@property (nonatomic, strong) PFUser *user;
@property (nonatomic, strong) Product *product;
@property (nonatomic, copy) NSString *content;
@end

Introducing a Special Parse Object: PFUser

The PFUser class is a local representation of a user persisted to the Parse Data. This class is a subclass of a PFObject and has all the same features, such as flexible schema, automatic persistence, and a key value interface. The difference is that PFUser has some special additions specific to user accounts, including authentication, sign up, and validation uniqueness.

Notably, username and password are two required properties for PFUser. The e-mail property of PFUser is always there even though you might want to set a value for it.

To sign up a user with a username and password, start by creating a PFUser instance:

PFUser *user = [PFUser user];

Next, get the username and password from two UITextField instances:

user.username =usernameTextField.text;
user.password = passwordTextField.text;

Then call the -(BOOL)signUp method, or asynchronically the signUp method,

[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error){
        if (!error){

        }
}];

It is quite common nowadays to require a user’s e-mail address as username. You can use a UITextField to collect the user’s e-mail:

user.username = emailTextField.text;
user.password = passwordTextField.text;
user.email = emailTextField.text;

Next, you need to verify the text in emailTextField is a valid e-mail address. Parse provides the emailVerified property for PFUser. The value of emailVerified can only be set to true while this user clicks a link in the verification e-mail sent by Parse (this process is discussed in Chapter 19).

You log in a user with the PFUser class method logInWithUsernameInBackground:password:

[PFUser logInWithUsernameInBackground:emailTextField.text password:passwordTextField.text block:^(PFUser *user, NSError *error){
        if (!error) {

        }
}];

Once this user has logged in, use the class method [PFUser currentUser] to get an instance of the current user. The user is persisted on the client. Unless the user has logged out, you can always use this method to get the current user.

To log the user out, simply call this method:

[PFUser logOut];

Parse also provides an Anonymous Users feature. An anonymous user can be created without a username and password while still retaining all of the same capabilities as any other PFUser. After logging out, an anonymous user is abandoned, and the user’s data is no longer accessible. A typical use case is to create an anonymous user when your app is launched for the first time; you then let the user add products to the shopping bag, and keep track of what the user has liked. You won’t ask the user to sign up with an e-mail and password until later or the user’s data will be discarded.

To enable this feature, call it in application:didFinishLaunchingWithOptions: in AppDelegate.m:

[PFUser enableAutomaticUser];

Once this method is called, when check [PFUser currentUser], it won’t return a nil.

To check whether the current user is an anonymous user, use the PFAnonymousUtils method + (BOOL)isLinkedWithUser:(nullable PFUser *)user:

if ([PFAnonymousUtils isLinkedWithUser:[PFUser currentUser]]){
        NSLog(@"It is an anonymous user.");
}

You can create a subclass of PFUser the same way you created a PFObject subclass. For example, if you have a Customer object, the Customer.h file will look like this:

#import <Parse/Parse.h>
@interface Customer : PFUser<PFSubclassing>
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, copy) NSString *address1;
@property (nonatomic, copy) NSString *city;
@property (nonatomic, copy) NSString *state;
@property (nonatomic, copy) NSString *zipcode;
@end

Customer.m will look like this:

#import "Customer.h"
#import <Parse/PFObject+Subclass.h>
@implementation Customer
@dynamic firstName, lastName, address1, city, state, zipcode;
@end

The +(NSString *)parseClassName method is not required to implement for the subclass of PFUser because the Parse class name will always be “_User.”

Note  To read more about PFUser, visit https://parse.com/docs/osx/api/Classes/PFUser.html.

Loading and Displaying Images: PFImageView

PFImageView is a subclass of UIimageView. It is a helper class that makes downloading image files from the Parse server easier. You use it the same way you use UIimageView. To display an image, write the following:

PFImageView *imageView = [[PFImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 180.0)];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.file = product.image;
[imageView loadInBackground];

PFImageview has a file property that is an instance of PFFile. Your Product instance has a property image, which is also the instance of PFFile.

Note  You can read more about PFFile at https://parse.com/docs/osx/api/Classes/PFFile.html.

Displaying an Image in a Cell: PFTableViewCell

PFTableViewCell is a subclass of UITableViewCell. It is a helper class to display any image downloaded from the Parse server more easily.

-(PFTableViewCell *)tableView:(UITableView * __nonnull)tableView cellForRowAtIndexPath:(NSIndexPath * __nonnull)indexPath object:(nullable Product *)object
{
    PFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ProductCell"];
    cell.imageView.file = object.image;
    [cell.imageView loadInBackground];
    return cell;
}

Querying and Showing Data: PFQueryTableViewController

When you develop an iOS application, you commony use UITableViewController. In a typical network-based app, you use UITableViewController to display the content that is fetched from a remote server. Often you also need to implement some features such as a loading indicator, pull-to-refresh, pagination, tap a cell to load and show next page, and load and show an image on each UITableViewCell. Parse creates this PFQueryTableViewController, a subclass of UITableViewController, to make a developer’s life even easier.

Here is an example of how you could use PFQueryTableViewController; let’s say you want to implement a feature that shows all products and name it ProductsTableViewController class, as shown in Figure 3-4.

9781484213186_Fig03-04.jpg

Figure 3-4. Create a PFQueryTableViewController subclass

This is what ProductsTableViewController.h looks like:

#import "PFQueryTableViewController.h"

@interface ProductsTableViewController : PFQueryTableViewController

@end

This is what the ProductsTableViewControllerm looks like:

#import "ProductsTableViewController.h"
#import "Product.h"
#import <ParseUI/PFTableViewCell.h>
#import <ParseUI/PFImageView.h>
@implementation ProductsTableViewController
-(void)awakeFromNib
{
    [super awakeFromNib];
    self.parseClassName = @"Product";
    self.pullToRefreshEnabled = YES;
    self.paginationEnabled = YES;
    self.objectsPerPage = 10;
}

- (PFQuery *)queryForTable {
    PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
    if ([self.objects count] == 0) {
        query.cachePolicy = kPFCachePolicyCacheThenNetwork;
    }
    return query;
}

#pragma mark
-(PFTableViewCell *)tableView:(UITableView * __nonnull)tableView cellForRowAtIndexPath:(NSIndexPath * __nonnull)indexPath object:(nullable Product *)object
{
    PFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ProductCell"];
    cell.textLabel.text = object.title;
        cell.imageView.file = object.image;
    [cell.imageView loadInBackground];
    return cell;
}
@end

First, you implement the -(void)awakeFromNib method if you are using the XIB file in a UIStoryboard or a separate XIB file for the ProductsTableViewController. Specify the Parse class name you will use for this controller: in this case, Product class. If you want to use the pull-to-fresh or pagination features, set self.pullToRefreshEnabled and self.paginationEnabled to YES. Also set 10 objects for each page. Depending on the cell height, you can set your number to provide a better user experience.

The second method you need to implement is +(PFQuery *)queryForTable. Specify what Parse class you will query, and the cache policy. In this case, query the Product Parse class and for your cache policy use kPFCachePolicyCacheThenNetwork.

The third method is to display your objects on each PFTableViewCell. In this example, set the product title to the cell’s textLabel and the product image to the cell’s imageView.

Summary

In this chapter, I have gone over the Parse fundamentals related to building an e-commerce iOS app. I started with the PFObject class and its subclasses, and then introduced four types of relationships in PFObjects and PFUser. I also covered three basic Parse UIs, namely, PFImageView, PFTableViewCell, and PFQueryTableViewController.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.60.63