Profile photo of Alex

by

Strong Typed Rows in the DataAbstract for Cocoa

September 1, 2014 in Uncategorized

Challenge

In DataAbstract for Cocoa, you can retrieve the values for a particular table field using the KVC approach, where the field name is passed as a string key:

DADataTableRow *row = [[self table] rowAtIndex:idx];
NSString *name = [row valueForKey:@"ClientName"];

Later, with help of the new indexing syntax for arrays and dictionaries, this code can be rewritten in a more simple manner:

DADataTableRow *row = self.table.rows[idx];
NSString *name = row[@"ClientName"];

But there is one flaw to that approach – the field name is passed as a string constant and thus can’t be checked in design time or selected in the code completion system.
The user still has to work with DADataTabeRow instances, where data is represented by the dictionary, rather than by business objects.

Solution

With the help of some “Magic” in Objective-C, we can now offer another approach for getting values for a particular column in a row.
You can just operate with a particular field in the table row as with a usual property.
This means that you can call any property in a row instance like this:

id fieldValue = [self.dataTable.rows[idx] MyFieldName];

If there is no such field in the table, you will get a proper exception, otherwise you will get its value.

It would also be great though if we could choose available fields using the Code Completion system inside the IDE.
This would simplify the code and allow to avoid various possible mistakes in specifying field names when working with data of the row.

And the answer is yes, we can do that.
All we need to do is to represent the available table fields as properties of a special protocol (interface) that the row can support.

This approach can be used in two ways:
The most simple way is to just generate protocols ad later cast the row instance to the row protocol. For example:

 
// Protocol
 
@protocol DepsTableRow_Protocol <NSObject>
@property (strong) NSString *DepName;
@property (strong) NSNumber *DepId;
@property (strong) NSString *DepPhone;
@end
 
 
// Code for obtaining data 
 
id row = self.dataTable.rows[0];
id<DepsTableRow_Protocol> typedRow = row;
NSNumber *depId = [typedRow DepId];
NSString *depName = [typedRow DepName];
NSString *depPhone = typedRow.DepName;// you also can use dot(.) for accessing field property
 
NSLog(@"%@(%@): Phone:%@:", depName, depId, depPhone);

Another approach is to provide a special row class for the particular table.
This class must be inherited from the base DADataTableRow class, but in addition, it can expose different properties:

//Protocol:
@protocol DepsTableRow_Protocol <NSObject>
@property (strong) NSString *DepName;
@property (strong) NSNumber *DepId;
@property (strong) NSString *DepPhone;
@end
 
// Custom row class 
@interface DepsTableRow : DADataTableRow <DepsTableRow_Protocol>
// you don't need to implement DepsTableRow_Protocol for declaring its properties
@end
 
// Code for obtaining data:
// First we need to register our custom row class for the particular table
// It should be done once per application start 
// The init method of the DataAccess module is usually a good place for doing that
[DADataTable registerRowClass:[DepsTableRow class] forTableName:@"Deps"];
 
// then later each row in the table rows array is the instance of our custom row
DepsTableRow *typedRow = self.dataTable.rows[0];
NSNumber *depId = [typedRow DepId];
NSString *depName = [typedRow DepName];
NSString *depPhone = typedRow.DepName; // You can use a dot(.) to access fields as properties
 
NSLog(@"%@(%@): Phone:%@:", depName, depId, depPhone);

Supporting Strong Typed Rows in Nougat

The same approaches are also available for Nougat projects and Oxygene:

// Protocol
DepsTableRow_Protocol= public interface
    property DepName: strong NSString read write;
    property DepId: strong NSNumber read write;
    property DepPhone: strong NSString read write;
end;
 
// Code for obtaining data 
var row: id := self.dataTable.rows[0];
var typedRow : DepsTableRow_Protocol :=  duck<DepsTableRow_Protocol>(row);
 
var depId := typedRow.DepId;
var depName := typedRow.DepName;
var depPhone := typedRow.DepPhone;
 
NSLog("%@(%@): Phone:%@:", depName, depId, depPhone);

and for RemObjects C# (Hydrogene):

// Protocol
public interface DepsTableRow_Protocol
{
    __strong NSString DepName { get; set; } 
    __strong NSNumber DepId { get; set; }
    __strong NSString DepPhone { get; set; }
}
 
// Code for obtaining data 
id row = self.dataTable.rows[0];
DepsTableRow_Protocol typedRow = duck<DepsTableRow_Protocol>(row);
 
NSNumber depId = typedRow.DepId;
NSString depName = typedRow.DepName;
NSString depPhone = typedRow.DepPhone;
 
NSLog("%@(%@): Phone:%@:", depName, depId, depPhone);

Generating protocols

The one remaining question is how to get those row protocols?

For Nougat projects created in MS Visual Studio, these protocols will be generated automatically by the New Project Wizard.
You can also update/regenerate these protocols any time you want – just run the wizard by clicking Create Table Definition Classes in the Solution Explorer window. TableDefinitions Wizard

For Xcode projects, you can generate the protocols directly from the schema node inside the Relativity Server in the Server Explorer for Cocoa tool. It allows to generate Objective-C, Oxygene and RemObjects C# protocols for Nougat projects. TableDefinitions Generator

Samples

Take a look at the StrongTypedRows (iOS) samples written in Nougat, Oxygene and RemObjects C#. We are providing them with the DataAbstract for Cocoa package.

Thanks for reading!

Leave a reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>