How to Use Active Record in iOS Apps - dummies

How to Use Active Record in iOS Apps

By Rajiv Ramnath

Active Record is a simple pattern, in which the class of the in-memory model object is extended with methods that handle the persistence of the class. Following is an example that uses a table named PERSONTABLE to persist objects of a Person class in a relational database as the data-store. This is the interface file of the Person class:

@interface Person : NSObject <Persistable> {
 // Model attributes
 @private NSString * Name;
 @private NSString * SSNo;
}
 -(id) init;
 -(id) initWithOIDNameAndSSNo :(NSString *) OID :(NSString *) aName
             :(NSString *) aSSNo;
 -(NSString *) getName;
 -(NSString *) getSSNo;
 -(void) setName :(NSString *) aName;
 -(void) setSsno :(NSString *) aSSNo;
 -(void) setAsRetired;
 // Other operations, if any
–d

Only the domain methods are shown in this interface. The additional methods needed for persistence are defined in the Persistable protocol:

typedef enum {
 NEW, EXISTS, CHANGED, DELETED
} STATE;
@protocol Persistable
 // Properties required by the protocol
 @required
 @property (copy) NSString *OID; // Unique ID in the Database
 @required
 @property (readonly) STATE state;
 + (id) findByOID :(NSString *) OID;
 - (void) insert;
 - (void) update;
 - (void) delete;
 - (void) persist;
–d

This protocol declares two required attributes:

  • The OID attribute: This attribute holds the persistent ID of the object that will uniquely identify the object within a persistent data-store.

  • The state attribute: This attribute indicates whether the object

    • Was newly created in memory (state is NEW).

    • Exists in the database (state is EXISTING).

    • Was modified in memory after being fetched from the data-store (state is CHANGED).

    • Is marked for deletion (state is DELETED).

The protocol then declares a collection of methods that the class needs to implement. You use the class method FindByOid to bring an object from the database into memory. (Such methods are known as finder methods, and you can have many different kinds of them, such as a findByName, findBySSNo, and so on.) Pseudocode for implementing this method in the Person class is shown here:

+ (id) findByOID :(NSString *) anOID {
 Person *returnedPerson;
 // SQL statement to retrieve object from the database
 // Save retrieved NAME and SSNO in local variables aName and anSSNo
 … SELECT NAME, SSNO, from PERSONTABLE
   where OID = :anOID INTO :aName, :anSSNo;
 aPerson = [[Person alloc] initWithOIDNameAndSSNo :anOID :anSSNo];
 aPerson->state = EXISTING;
 return returnedPerson;
}

The logic of this method is as follows:

  • The embedded SQL SELECT statement fetches the Name and Social Security number attributes of the object from the database and saves these values in the local variables aName and anSSNo.

  • These variables along with the OID instantiate and initialize an object of the Person class and return it.

Next, you see the implementation of the other methods from the Persistable protocol, in the Person class. The persist method is called whenever the object needs to be saved to the database, as shown here:

- (void) persist {
 // Check the state of the object
 // If it is a new object i.e. state = NEW call [self insert]
 // to create an entry in PERSONTABLE
 // If state = EXISTS do nothing
 // If state = CHANGED, call update to update the database
 // If state - DELETED, call delete to remove the information
 // in the database
}

The persist method uses the state variable to invoke the right methods for the object’s persistence.

  • If the object is newly created, a record is inserted in the object’s table using the insert method, as shown here:

    - (void) insert {
     INSERT INTO PERSONTABLE (OID, NAME, SSNO) VALUES :OID :Name :SSNo
     ...
    }
  • If the object is retrieved from the database and then modified in memory, the update method is called.

    - (void) update {    
     UPDATE PERSONTABLE SET VALUES NAME = :Name, SSNO=:SSNo
      WHERE OID = :OID
     ...
    }
  • If the object is retrieved from the database and then marked for deletion in memory, the delete method is called.

- (void) delete {
 DELETE FROM PERSONTABLE WHERE OID = :OID;
 ...
}

Note that the domain methods as well as the finder methods should set the state of the object appropriately. As a result, init sets the state of the object to NEW, findByOID sets the state to EXISTING, setAsRetired could potentially set the state to DELETED, setName, and setSSNo set the state to CHANGED (if the original state was EXISTING), and so on.

The domain methods and the persistence methods are, therefore, coupled to some degree.

When you use Active Record to store objects in a relational database, it typically maps an object to a database table, with the object’s persistent variables mapped to fields in that table.

All of the lifecycle-management logic of the object — deciding when to fetch it from the database, whether to allow duplicate instance in memory corresponding to the same object in the database, when to save, when to delete objects permanently— are the application’s responsibilities.