Clone this repo:

Branches

  1. 74217fa Project import by Igor Sarkisov ยท 3 years, 6 months ago master

Broker

Broker maps remote resources to local Core Data resources via JSON responses. Using a few simple design standards, you can automatically map JSON attributes to NSManagedObject attributes with little effort.

All this fun stuff is done with a few rules.

  1. Name your local object attributes the same as remote attributes. For example, if your remote Employee has a “firstName” attribute, don't name your local NSManagedObject Employee attribute “first_name”. If you want, Broker has the flexibility to map remote names to local ones, but why add the extra code?
  2. Use a unique object identifier. Each object you want to persist should have a unique attribute to easily identify it. For example, Employee might have an employeeId. Without this, there isn't a way to safely guarantee one single persisted object.

Installation

Broker follows standard patterns for iOS static library design.

  1. Add the Broker.xcodeproj file to your project.
  2. Add Broker as a target dependency in your targets Build Phase tab.
  3. Add libBroker.a to your targets Link Binary With Libraries build phase.
  4. Build your app.

Now you should be able to import Broker headers following this pattern.

#import <Broker/Broker.h>

Getting Started

Create and configure a new BKController. This should be a long lived object, perhaps stored on your App Delegate or an otherwise appropriate location.

BKController *controller = [BKController controller];

Register your NSManagedObject with the controller in your main Core Data context. What this does is temporarily creates an Employee object in your context, traverses all it's properties and relationships, and builds an internal description of what makes an Employee object based on NSEntityDescription and NSAttributeDescription. These descriptions are used to transform JSON into the registered entity. This example assumes you have an Emoloyee object in your Core Data model with at least an employeeId attribute.

[controller.entityMap registerEntityNamed:@"Employee"
                           withPrimaryKey:@"employeeId"
                  andMapNetworkProperties:@"id"
                        toLocalProperties:@"employeeId"
                                inContext:context];

Note that we are mapping a network property, id, to a local property, employeeId. Sometimes your API may use different attribute names than what you have in your Core Data. Broker allows you to map between the two.

Now you are ready to process some JSON. By passing in the main NSManagedObjectContext into the BKController, you are spinning up a child context in which all the work will be done. After the work is done, the context pushes it's changes to the main context. From there, you can choose to either save or not, but this example includes a proper save pattern.

- (void)processEmployeeJSON:(id)json 
	withController:(BKController *)controller
	 inContext:(NSManagedObjectContext *)context
{
		__weak NSManagedObjectContext *weakContext = context;
	    [controller processJSONObject:json
                    	asEntityNamed:@"Employee"
                      		inContext:context
                  	  completionBlock:^{
                       [weakContext performBlock:^{
   						 [context save:nil];
					   }];
                   }];
}

Broker and JSON API Design

Broker is built to handle specific styles of JSON responses.

List of things

Broker can process a list of similar things. For example, a JSON response containing a list of Employee objects.

[
    {
        "name": "Andrew",
        "department": "Engineering",
        "employeeId": 1
    },
    {
        "name": "Sarah",
        "department": "Engineering",
        "employeeId": 2
    },
    {
        "name": "Steve",
        "department": "Marketing",
        "employeeId": 3
    }
]

Broker cannot process a mixed list of things, like Employee's and Departments.

[
    {
        "name": "Andrew",
        "department": "Engineering",
        "employeeId": 1
    },
    {
        "name": "Engineering",
        "departmentId": 2
    },
]

Instead, you should return similar objects it as nested lists, which you can process separately.

[
    {
        "employees": [
            {
                "name": "Andrew",
                "department": "Engineering",
                "employeeId": 1
            }
        ]
    },
    {
        "departments": [
            {
                "name": "Engineering",
                "departmentId": 2
            }
        ]
    }
]

A Single Thing

Broker can process a single thing. For example, a single Employee.

{
    "name": "Andrew",
    "department": "Engineering",
    "employeeId": 1
}

A Nested Thing on a Thing

Broker can process a nested thing. For example, an Employee with a Department.

{
    "name": "Andrew",
    "employeeId": 1,
    "department": {
        "name": "Engineering",
        "departmentId": 1
    }
}

A Nested List of Things on a Thing

Broker can process a nested list of things on a thing. For example, a Department with a list of Employees.

{
    "name": "Engineering",
    "departmentId": 1,
    "employees": [
        {
            "name": "Andrew",
            "departmentId": 1
        },
        {
            "name": "Sarah",
            "employeeId": 2
        }
    ]
}

Unique Objects

Broker uses a “primary key” convention to enforce object uniqueness. NSManagedObjects must have a primary key to be registered with Broker. For example, an Employee could have a unique employeeId attribute. Once we have a primary key, we can use a simple find or create pattern to guarantee uniqueness. Without specifying a primary key, you could end up with duplicate objects.

DISCLAIMER: If you are working with JSON where you might have more than a few thousand entities at once, the find-or-create pattern in it‘s current form will be slow. I’m working on a faster pattern.