Skip to content

Stay Classy, Objective-C: Introducing Native Subclasses for Parse Objects

Thank you Developer! But our Business Logic is in Another Class

You wake up in a strange land. Somebody has stolen the princess and the town is looking to you to save her. Why don't the townsfolk build their own army and fight the wizard themselves? Apparently they're busy. For years, these townsfolk have perfected the art of Business Objects. They create two classes for every concept: one class contains business logic and the other is a SAX parser or some other delegate to manage storage and retrieval. These townsfolk aren't warriors, they're artisans. Each project requires new brilliant innovations in data mappings, and this has left them too busy to save the beloved princess.

"I need a weapon," you say to yourself. Luckily, Parse has just the tool for you.

It's Dangerous To Go Alone! Take This

Objective-C is a dynamic language, and dynamic languages offer interesting possibilities. Today we are releasing support for native subclasses in Objective-C. With proper subclassing, your PFObjects are your business objects. Your subclasses can have the properties and methods that are unique to a particular business function. Your code becomes terser, easier to read, and supports autocomplete in Xcode. Imagine turning the following code:

PFObject *shield = [PFObject objectWithClassName:@"Armor"];
[shield setObject:@"Wooden Shield" forKey:@"displayName"];
[shield setObject:[NSNumber numberWithBool:NO] forKey:@"fireproof"];
[object setObject:[NSNumber numberWithInt:50] forKey:@"rupees"];

into this:

Armor *shield = [Armor object];
shield.displayName = @"Wooden Shield";
shield.fireproof = NO;
shield.rupees = 50;

It's Super Effective!

It's easy to set up PFObject subclasses, and it greatly simplifies app development. There are built-in helpers for creating and querying for objects. You can better encapsulate logic for a particular class by adding custom properties and methods. Consider the complete implementation of the armor class:

Armor.h

@interface Armor : PFObject<PFSubclassing>
+ (NSString *)parseClassName;
@property (retain) NSString *displayName;
@property int rupees;
@property BOOL fireproof;
@end

Armor.m

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

@implementation Armor
@dynamic displayName;
@dynamic rupees;
@dynamic fireproof;
+ (NSString *)parseClassName {
return @"Armor";
}
@end

We can now create objects using [Armor object] and query for Armor objects with [Armor query]. The definition of parseClassName ensures that [PFObject objectWithClassName:@"Armor"] will create an Armor object. PFObject supports dynamic synthesizers; anArmor.displayName is now equivalent to [anArmor objectForKey:@"displayName"] but is terser, supports code complete, and is a compiler error when mistyped. As an NSString, we can even declare displayName to be a copy property as well.

The rupees and fireproof properties here are extra special: PFObjects require object members, but the rupees property is an int and fireproof is a BOOL. PFObject properties support unboxing. [anArmor objectForKey:@"fireproof"] returns an NSNumber with a boolValue, but setting the fireproof property automatically wraps the BOOL in an NSNumber, and getting the fireproof property automatically extracts the boolValue from the NSNumber.

(Nearly) All Your Implementation Are Belong To Base Class

This change is made possible through great new features in PFObject which enable subclassing. To keep code clean, the methods which only make sense in subclasses (e.g. initializers without a class name) are exposed in the new PFSubclassing protocol. Everything but the name of the class you want to implement are already implemented in the PFObject Subclass category, so setup is simple. Read more about our setup in the iOS/OS X guide.

Good luck saving the princess!