Project import
diff --git a/Broker/BKAttributeDescription.h b/Broker/BKAttributeDescription.h
new file mode 100644
index 0000000..9b013fe
--- /dev/null
+++ b/Broker/BKAttributeDescription.h
@@ -0,0 +1,42 @@
+//
+//  BKEntityMap.h
+//  Broker
+//
+//  Created by Andrew Smith on 10/5/11.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy 
+// of this software and associated documentation files (the "Software"), to deal 
+// in the Software without restriction, including without limitation the rights 
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+// of the Software, and to permit persons to whom the Software is furnished to do so, 
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included 
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+
+@interface BKAttributeDescription : NSObject
+
+@property (nonatomic, strong, readonly) NSAttributeDescription *internalAttributeDescription;
+
++ (BKAttributeDescription *)descriptionWithAttributeDescription:(NSAttributeDescription *)description;
+
+/**
+ * Returns the correct object type with the given value
+ */
+- (id)objectForValue:(id)value;
+
+- (void)setDateFormat:(NSString *)dateFormat;
+
+@end
diff --git a/Broker/BKAttributeDescription.m b/Broker/BKAttributeDescription.m
new file mode 100644
index 0000000..43af478
--- /dev/null
+++ b/Broker/BKAttributeDescription.m
@@ -0,0 +1,124 @@
+//
+//  BKEntityMap.m
+//  Broker
+//
+//  Created by Andrew Smith on 10/5/11.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy 
+// of this software and associated documentation files (the "Software"), to deal 
+// in the Software without restriction, including without limitation the rights 
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+// of the Software, and to permit persons to whom the Software is furnished to do so, 
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included 
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "BKAttributeDescription.h"
+#import "BrokerLog.h"
+
+@interface BKAttributeDescription ()
+
+@property (nonatomic, strong, readwrite) NSAttributeDescription *internalAttributeDescription;
+
+@property (nonatomic, strong) NSDateFormatter *dateFormatter;
+
+@end
+
+@implementation BKAttributeDescription
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        _dateFormatter = [[NSDateFormatter alloc] init];
+    }
+    return self;
+}
+
++ (instancetype)descriptionWithAttributeDescription:(NSAttributeDescription *)description
+{
+    BKAttributeDescription *bkDescription = [self new];
+    bkDescription.internalAttributeDescription = description;
+    return bkDescription;
+}
+
+- (id)objectForValue:(id)value
+{    
+    NSAttributeType type = [self.internalAttributeDescription attributeType];
+    
+    // Check for null value
+    if (value == (id)[NSNull null] || value == nil) {
+        return nil;
+    }
+    
+    switch (type) {
+        case NSUndefinedAttributeType:
+            return nil;
+            break;
+        case NSInteger16AttributeType:
+            return @([value shortValue]);
+            break;
+        case NSInteger32AttributeType:
+            return @([value integerValue]);
+            break;
+        case NSInteger64AttributeType:
+            return @([value longLongValue]);
+            break;
+        case NSDecimalAttributeType:
+            return [NSDecimalNumber decimalNumberWithDecimal:[value decimalValue]];
+            break;
+        case NSDoubleAttributeType:
+            return @([value doubleValue]);
+            break;
+        case NSFloatAttributeType:
+            return @([value floatValue]);
+        case NSStringAttributeType:
+            return [NSString stringWithString:value];
+            break;
+        case NSBooleanAttributeType:
+            return @([value boolValue]);
+        case NSDateAttributeType:
+            if (!self.dateFormatter.dateFormat) {
+                BrokerWarningLog(@"NSDate attribute named \"%@\" on entity \"%@\" requires "
+                                 @"date format to be set.  Use [Broker setDateFormat:forProperty:onEntity:]",
+                                 self.internalAttributeDescription.name,
+                                 self.internalAttributeDescription.entity.name);
+                return nil;
+            }
+            
+            return [self.dateFormatter dateFromString:value];
+            break;
+        case NSBinaryDataAttributeType:
+            return [NSKeyedArchiver archivedDataWithRootObject:value];
+            break;
+        case NSTransformableAttributeType:
+            return value;
+            break;
+        case NSObjectIDAttributeType:
+            NSAssert(YES, @"Not implemented yet");
+            return nil;
+            break;
+        default:
+            return nil;
+            break;
+    }
+}
+
+#pragma mark - Date
+
+- (void)setDateFormat:(NSString *)dateFormat
+{
+    [self.dateFormatter setDateFormat:dateFormat];
+}
+
+@end
diff --git a/Broker/BKController.h b/Broker/BKController.h
new file mode 100644
index 0000000..7c7591b
--- /dev/null
+++ b/Broker/BKController.h
@@ -0,0 +1,88 @@
+//
+//  BKController.h
+//  Broker
+//
+//  Created by Andrew Smith on 1/8/14.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+
+@class BKEntityMap, BKJSONController;
+
+@interface BKController : NSObject
+
+@property (nonatomic, strong, readonly) BKEntityMap *entityMap;
+
++ (instancetype)controller;
+
+/**
+ Parses the JSON into an NSManagedObject matching the entity name in the context provided. For example, if you have 
+ registered an NSManagedObject of type Employee, and you pass in {"employeeId" : 1234}, this will find/create an
+ Employee object in the context with the given employeeId property set. If there isn't a primary key set, the find or 
+ create pattern is simply a create, and a new object is created.
+ */
+- (void)processJSONObject:(NSDictionary *)json
+            asEntityNamed:(NSString *)entityName
+                inContext:(NSManagedObjectContext *)context
+          completionBlock:(void (^)())completionBlock;
+
+/**
+ Parses the JSON into an NSManagedObject matching the entity name in the context provided. For example, if you have
+ registered an NSManagedObject of type Employee, and you pass in {"employeeId" : 1234}, this will find/create an
+ Employee object in the context with the given employeeId property set.
+ */
+- (void)processJSONObject:(NSDictionary *)json
+                 onObject:(NSManagedObject *)object
+          completionBlock:(void (^)())completionBlock;
+
+/**
+ 
+ */
+- (void)processJSON:(id)json
+    forRelationship:(NSString *)relationshipName
+           onObject:(NSManagedObject *)object
+    completionBlock:(void (^)())completionBlock;
+
+/**
+ 
+ */
+- (void)processJSONCollection:(NSArray *)json
+              asEntitiesNamed:(NSString *)entityName
+                    inContext:(NSManagedObjectContext *)context
+              completionBlock:(void (^)())completionBlock;
+
+/**
+ *  Allows more flexibility in processing JSON. The workBlock is executed in 
+ *  the background, with a childContext spun up fron the provided context. Use
+ *  this if you want to do some custom stuff while working with your JSON.
+ *
+ *  @param json            JSON input
+ *  @param workBlock       The work to be performed in the background
+ *  @param context         The main context
+ *  @param completionBlock The completion block to be run after everything is complete
+ */
+- (void)processJSON:(id)json
+          workBlock:(void (^)(id jsonCopy, NSManagedObjectContext *childContext, BKJSONController *jsonController))workBlock
+          inContext:(NSManagedObjectContext *)context
+    completionBlock:(void (^)())completionBlock;
+
+@end
diff --git a/Broker/BKController.m b/Broker/BKController.m
new file mode 100644
index 0000000..391eb82
--- /dev/null
+++ b/Broker/BKController.m
@@ -0,0 +1,229 @@
+//
+//  BKController.m
+//  Broker
+//
+//  Created by Andrew Smith on 1/8/14.
+//  Copyright (c) 2014 Andrew B. Smith. All rights reserved.
+//
+
+#import "BKController.h"
+
+#import "BKEntityMap.h"
+#import "BKJSONController.h"
+#import "BKEntityDescription.h"
+#import "BKAttributeDescription.h"
+#import "BrokerLog.h"
+
+static NSString * const BROKER_INTERNAL_QUEUE = @"com.broker.queue";
+
+@interface BKController ()
+
+@property (nonatomic, strong, readwrite) BKEntityMap *entityMap;
+
+/**
+ Internal serial queue for core data processing off the main thread.
+ */
+@property (nonatomic, strong) NSOperationQueue *queue;
+
+@end
+
+@implementation BKController
+
++ (instancetype)controller
+{
+    BKController *controller = [self new];
+    
+    //
+    // Entity Map
+    //
+    BKEntityMap *entityMap = [BKEntityMap entityMap];
+    controller.entityMap = entityMap;
+    
+    //
+    // Serial queue
+    //
+    NSOperationQueue *queue = [NSOperationQueue new];
+    queue.maxConcurrentOperationCount = 1;
+    controller.queue = queue;
+    
+    return controller;
+}
+
+#pragma mark BKJSONController
+
+- (void)processJSONObject:(NSDictionary *)json
+            asEntityNamed:(NSString *)entityName
+                inContext:(NSManagedObjectContext *)context
+          completionBlock:(void (^)())completionBlock
+{
+    NSOperation *operation = [self operationForContext:context
+                                                  JSON:json
+                                         withJSONBlock:^(id jsonCopy,
+                                                         NSManagedObjectContext *backgroundContext,
+                                                         BKJSONController *jsonController) {
+                                             [jsonController processJSONObject:jsonCopy
+                                                                 asEntityNamed:entityName];
+                                         }];
+    
+    // Finish
+    operation.completionBlock = completionBlock;
+    
+    // Queue it up
+    [self.queue addOperation:operation];
+}
+
+- (void)processJSONObject:(NSDictionary *)json
+                 onObject:(NSManagedObject *)object
+          completionBlock:(void (^)())completionBlock
+{
+    NSManagedObjectID *objectId = object.objectID;
+
+    NSOperation *operation = [self operationForContext:object.managedObjectContext
+                                                  JSON:json
+                                         withJSONBlock:^(id jsonCopy,
+                                                         NSManagedObjectContext *backgroundContext,
+                                                         BKJSONController *jsonController) {
+                                             //
+                                             // Use a newly fetched object from the background context so we don't modify
+                                             // the passed in object, which could come from god knows where.
+                                             //
+                                             NSManagedObject *backgroundObject = [backgroundContext objectWithID:objectId];
+                                             
+                                             [jsonController processJSONObject:jsonCopy
+                                                                      onObject:backgroundObject];
+                                         }];
+    
+    // Finish
+    operation.completionBlock = completionBlock;
+    
+    // Queue it up
+    [self.queue addOperation:operation];
+
+}
+
+- (void)processJSON:(id)json
+    forRelationship:(NSString *)relationshipName
+           onObject:(NSManagedObject *)object
+    completionBlock:(void (^)())completionBlock
+{
+    NSManagedObjectID *objectId = object.objectID;
+    
+    // Operation
+    NSOperation *operation = [self operationForContext:object.managedObjectContext
+                                                  JSON:json
+                                         withJSONBlock:^(id jsonCopy,
+                                                         NSManagedObjectContext *backgroundContext,
+                                                         BKJSONController *jsonController) {
+                                             //
+                                             // Use a newly fetched object from the background context so we don't modify
+                                             // the passed in object, which could come from god knows where.
+                                             //
+                                             NSManagedObject *backgroundObject = [backgroundContext objectWithID:objectId];
+                                             
+                                             [jsonController processJSON:jsonCopy
+                                                         forRelationship:relationshipName
+                                                                onObject:backgroundObject];
+                                         }];
+    
+    // Finish
+    operation.completionBlock = completionBlock;
+    
+    // Queue it up
+    [self.queue addOperation:operation];
+}
+
+- (void)processJSONCollection:(NSArray *)json
+              asEntitiesNamed:(NSString *)entityName
+                    inContext:(NSManagedObjectContext *)context
+              completionBlock:(void (^)())completionBlock
+{
+    // Operation
+    NSOperation *operation = [self operationForContext:context
+                                                  JSON:json
+                                         withJSONBlock:^(id jsonCopy,
+                                                         NSManagedObjectContext *backgroundContext,
+                                                         BKJSONController *jsonController) {
+                                             [jsonController processJSONCollection:jsonCopy
+                                                                   asEntitiesNamed:entityName];
+                                         }];
+    
+    // Finish
+    operation.completionBlock = completionBlock;
+    
+    // Queue it up
+    [self.queue addOperation:operation];
+}
+
+- (void)processJSON:(id)json
+          workBlock:(void (^)(id jsonCopy, NSManagedObjectContext *childContext, BKJSONController *jsonController))workBlock
+          inContext:(NSManagedObjectContext *)context
+    completionBlock:(void (^)())completionBlock
+{
+    // Operation
+    NSOperation *operation = [self operationForContext:context
+                                                  JSON:json
+                                         withJSONBlock:workBlock];
+    
+    // Finish
+    operation.completionBlock = completionBlock;
+    
+    // Queue it up
+    [self.queue addOperation:operation];
+}
+
+#pragma mark -
+
+- (NSOperation *)operationForContext:(NSManagedObjectContext *)context
+                                JSON:(id)json
+                       withJSONBlock:(void (^)(id jsonCopy,
+                                               NSManagedObjectContext *childContext,
+                                               BKJSONController *jsonController))block
+{
+    __weak typeof(self) weakSelf = self;
+    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+        // Background context
+        NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
+        backgroundContext.parentContext = context;
+        
+        // Memory optimization
+        backgroundContext.undoManager = nil;
+        
+        // Grab a new JSON controller
+        BKJSONController *jsonController = [BKJSONController JSONControllerWithContext:backgroundContext
+                                                                             entityMap:weakSelf.entityMap];
+        
+        // Copy JSON to make sure it doesn't get mutated underneath us
+        id jsonCopy = [json copy];
+        
+        // Queue up a blocking call to make sure we play super nice with Apples Core Data concurrency
+        [backgroundContext performBlockAndWait:^{
+            /**
+             
+             Here is why an autorelease pool is necessary, per apple docs.
+             
+             https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW18
+             
+             "If your block creates more than a few Objective-C objects, you might want to enclose parts
+             of your block’s code in an @autorelease block to handle the memory management for those
+             objects. Although GCD dispatch queues have their own autorelease pools, they make no
+             guarantees as to when those pools are drained. If your application is memory constrained,
+             creating your own autorelease pool allows you to free up the memory for autoreleased
+             objects at more regular intervals."
+             
+             */
+            @autoreleasepool {
+                // Do the actual work
+                if (block) block(jsonCopy, backgroundContext, jsonController);
+                // Save background context. Does not automatically save parent context.
+                NSError *error;
+                [backgroundContext save:&error];
+                if (error) {
+                    BrokerLog(@"%@", error.localizedDescription);
+                }
+            }
+        }];
+    }];
+    return operation;
+}
+
+@end
diff --git a/Broker/BKEntityDescription.h b/Broker/BKEntityDescription.h
new file mode 100644
index 0000000..a54d133
--- /dev/null
+++ b/Broker/BKEntityDescription.h
@@ -0,0 +1,115 @@
+//
+//  BKEntityPropertiesDescription.h
+//  Broker
+//
+//  Created by Andrew Smith on 10/6/11.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy 
+// of this software and associated documentation files (the "Software"), to deal 
+// in the Software without restriction, including without limitation the rights 
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+// of the Software, and to permit persons to whom the Software is furnished to do so, 
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included 
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "BKAttributeDescription.h"
+
+@interface BKEntityDescription : NSObject
+
+/**
+ The internal NSEntityDescription that is used to help map JSON to local properties.
+ */
+@property (nonatomic, strong, readonly) NSEntityDescription *internalEntityDescription;
+
+/**
+ The name of the property used as the entity's primary key.  This needs to be
+ a guaranteed unique property, like "employeeID," which can be used to
+ positively identify a specific entity in the store.
+ */
+@property (nonatomic, strong) NSString *primaryKey;
+
+/**
+ Dictionary used for fast key finding
+ */
+@property (nonatomic, strong, readonly) NSMutableDictionary *networkToLocalPropertiesMap;
+
+/**
+ Dictionary used for fast key finding
+ */
+@property (nonatomic, strong, readonly) NSMutableDictionary *localToNetworkPropertiesMap;
+
+/**
+ @returns A new BKEntityPropertiesDescription
+ */
++ (instancetype)descriptionForObject:(NSManagedObject *)object;
+
+/**
+ Map several network properties to a local properties for an entity that is already 
+ registered with Broker.
+ 
+ @param networkProperties An array of network property names 
+ @param localProperties An array of local property names that match with the
+ networkProperties
+ 
+ @see [Broker registerEntityNamed:withPrimaryKey:andMapNetworkProperties:toLocalProperties]
+ */
+- (void)mapNetworkProperties:(NSArray *)networkProperties
+           toLocalProperties:(NSArray *)localProperties;
+
+/**
+  Returns the entity property description given the property name
+ */
+- (NSPropertyDescription *)descriptionForProperty:(NSString *)property;
+
+/**
+ @returns the entity's attribute description for the property
+ */
+- (BKAttributeDescription *)attributeDescriptionForProperty:(NSString *)property;
+
+/**
+  @returns the relationship description for the local property relationship name
+  on the entity.  Returns nil if property is not in the model, or if property is
+  not a relationship.
+ */
+- (NSRelationshipDescription *)relationshipDescriptionForProperty:(NSString *)property;
+
+/**
+  @returns true if the property is a relationship
+ */
+- (BOOL)isPropertyRelationship:(NSString *)property;
+
+/**
+ @returns the object from the input value for the given property. For instance, if you have an NSString property, "firstName"
+ on an Employee object, the value that gets passed in will return the proper NSString object. This is a necessary step
+ due to the fact that the value could be another nested object, or a relationship, or an NSSNumber that requires the
+ correct type.
+ */
+- (id)objectFromValue:(id)value
+          forProperty:(NSString *)property;
+
+/**
+ @returns the primary key value for the JSON. For instance, if you mapped set a primary key of "employeeId" on your Employee object
+ when you registered it with a broker controller, this would return the "employeeId" field for the json, if present.
+ */
+- (id)primaryKeyForJSON:(NSDictionary *)JSON;
+
+/**
+ @returns the local property name for the property. This looks at the map you created of network property names to local
+ properties and returns the local one. For instance, you may register "id" for your local property "employeeId". Passing
+ in "id" would return "employeeId", allowing you to map remote JSON to local NSManagendObject properties.
+ */
+- (NSString *)localPropertyNameForProperty:(NSString *)property;
+
+
+@end
diff --git a/Broker/BKEntityDescription.m b/Broker/BKEntityDescription.m
new file mode 100644
index 0000000..e1192e6
--- /dev/null
+++ b/Broker/BKEntityDescription.m
@@ -0,0 +1,215 @@
+//
+//  BKEntityPropertiesDescription.m
+//  Broker
+//
+//  Created by Andrew Smith on 10/6/11.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy 
+// of this software and associated documentation files (the "Software"), to deal 
+// in the Software without restriction, including without limitation the rights 
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+// of the Software, and to permit persons to whom the Software is furnished to do so, 
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included 
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "BKEntityDescription.h"
+#import "BKAttributeDescription.h"
+#import "BrokerLog.h"
+
+@interface BKEntityDescription ()
+
+@property (nonatomic, strong, readwrite) NSEntityDescription *internalEntityDescription;
+
+/**
+ Keys are property names, values are BKAttributeDescription
+ */
+@property (nonatomic, strong) NSMutableDictionary *propertiesDescriptions;
+
+/**
+ Dictionary used for fast key finding
+ */
+@property (nonatomic, strong, readwrite) NSMutableDictionary *networkToLocalPropertiesMap;
+
+/**
+ Dictionary used for fast key finding
+ */
+@property (nonatomic, strong, readwrite) NSMutableDictionary *localToNetworkPropertiesMap;
+
+@end
+
+@implementation BKEntityDescription
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        _networkToLocalPropertiesMap = [[NSMutableDictionary alloc] init];
+        _localToNetworkPropertiesMap = [[NSMutableDictionary alloc] init];
+    }
+    return self;
+}
+
++ (instancetype)descriptionForObject:(NSManagedObject *)object
+{
+    BKEntityDescription *entityDescription = [BKEntityDescription new];
+    entityDescription.internalEntityDescription = object.entity;
+    
+    //
+    // Iterate through all properties and add to properties description.
+    //
+    NSMutableDictionary *tempPropertiesDescriptions = [[NSMutableDictionary alloc] init];
+    NSDictionary *propertiesByName = entityDescription.internalEntityDescription.propertiesByName;
+    
+    for (NSString *propertyName in propertiesByName) {
+        
+        //
+        // It's either an NSAttributeDescription or an NSRelationshipDescription
+        //
+        id description = [propertiesByName objectForKey:propertyName];
+        
+        //
+        // Attribute
+        //
+        if ([description isKindOfClass:[NSAttributeDescription class]]) {
+            BKAttributeDescription *attrDescription = [BKAttributeDescription descriptionWithAttributeDescription:description];
+            [tempPropertiesDescriptions setObject:attrDescription
+                                           forKey:propertyName];
+        }
+        
+        //
+        // Relationship
+        //
+        if ([description isKindOfClass:[NSRelationshipDescription class]]) {
+            NSRelationshipDescription *relationshipDescription = description;
+            [tempPropertiesDescriptions setObject:relationshipDescription
+                                           forKey:propertyName];
+        }
+    }
+    
+    //
+    // Set property descriptions
+    //
+    entityDescription.propertiesDescriptions = tempPropertiesDescriptions;
+        
+    return entityDescription;
+}
+
+#pragma mark - Modifiers
+
+- (void)mapNetworkProperties:(NSArray *)networkProperties
+           toLocalProperties:(NSArray *)localProperties
+{
+    NSAssert((networkProperties.count == localProperties.count),
+             @"Mapping network properties to local properties expects arrays of the same size");
+    
+    if (!networkProperties || !localProperties) return;
+    if (networkProperties.count != localProperties.count) return;
+    
+    for (int i = 0; i < localProperties.count; i++) {
+        
+        NSString *localProperty   = [localProperties objectAtIndex:i];
+        NSString *networkProperty = [networkProperties objectAtIndex:i];
+        
+        if (localProperties && networkProperties) {
+            [self.networkToLocalPropertiesMap setValue:localProperty forKey:networkProperty];
+            [self.localToNetworkPropertiesMap setValue:networkProperty forKey:localProperty];
+        }
+    }
+}
+
+#pragma mark - Accessors
+
+- (NSPropertyDescription *)descriptionForProperty:(NSString *)property
+{
+    NSPropertyDescription *desc = self.propertiesDescriptions[property];
+    if (!desc) {
+        BrokerLog(@"No description for property \"%@\" found on entity \"%@\"!  It's not in your data model.",
+                  property,
+                  self.internalEntityDescription.name);
+    }
+    return desc;
+}
+
+- (BKAttributeDescription *)attributeDescriptionForProperty:(NSString *)property
+{
+    id description = [self descriptionForProperty:property];
+    if (description && [description isKindOfClass:[BKAttributeDescription class]]) {
+        return description;
+    } else {
+        BrokerLog(@"No description for property \"%@\" found on entity \"%@\"!  It's not in your data model.",
+                  property,
+                  self.internalEntityDescription.name);
+        return nil;
+    }
+}
+
+- (NSRelationshipDescription *)relationshipDescriptionForProperty:(NSString *)property
+{
+    id description = [self.propertiesDescriptions objectForKey:property];
+    
+    if (description && [description isKindOfClass:[NSRelationshipDescription class]]) {
+        return description;
+    } else {
+        return nil;
+    }
+}
+
+- (BOOL)isPropertyRelationship:(NSString *)property
+{
+    id description = [self.propertiesDescriptions objectForKey:property];
+    return (description && [description isKindOfClass:[NSRelationshipDescription class]]);
+}
+
+#pragma mark - Transform
+
+- (id)objectFromValue:(id)value
+          forProperty:(NSString *)property
+{
+    // Get the property description
+    NSPropertyDescription *propertyDescription = [self descriptionForProperty:property];
+    
+    // Test to see if networkProperty is relationship, attribute, or neither
+    if ([self isPropertyRelationship:property] || propertyDescription == nil) {
+        // Pass the value through for later processing
+        return value;
+    } else {
+        // transform it using the attribute desc
+        id valueAsObject = [(BKAttributeDescription *)propertyDescription objectForValue:value];
+        return valueAsObject;
+    }
+}
+
+- (id)primaryKeyForJSON:(NSDictionary *)JSON
+{
+    if (!self.primaryKey) return nil;
+    NSString *networkPrimaryKey = [self networkPropertyNameForProperty:self.primaryKey];
+    id value = JSON[networkPrimaryKey];
+    id object = [self objectFromValue:value
+                          forProperty:self.primaryKey];
+    return object;
+}
+
+- (NSString *)localPropertyNameForProperty:(NSString *)property
+{
+    NSString *localPropertyName = self.networkToLocalPropertiesMap[property];
+    return localPropertyName ? localPropertyName : property;
+}
+
+- (NSString *)networkPropertyNameForProperty:(NSString *)property
+{
+    NSString *networkPropertyName = self.localToNetworkPropertiesMap[property];
+    return networkPropertyName ? networkPropertyName : property;
+}
+
+@end
diff --git a/Broker/BKEntityMap.h b/Broker/BKEntityMap.h
new file mode 100644
index 0000000..83e45cc
--- /dev/null
+++ b/Broker/BKEntityMap.h
@@ -0,0 +1,74 @@
+//
+//  BKEntityController.h
+//  Broker
+//
+//  Created by Andrew Smith on 6/6/13.
+//  Copyright (c) 2013 Andrew B. Smith. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+
+@class BKEntityDescription;
+
+@interface BKEntityMap : NSObject
+
+/**
+ The dictionary containing all BKEntityPropertiesDescriptions for registered objects.
+ */
+@property (nonatomic, strong, readonly) NSMutableDictionary *entityDescriptions;
+
++ (instancetype)entityMap;
+
+/**
+ Register object. Map local attribute names to the remote resource. A common excpetion for "MyObject" 
+ might be mapping a network attribute 'id' to local attribute of 'myObjectID.'
+ 
+ @param entityName The entity name of the NSManagedObject.
+ @param primaryKey The designated primary key of the entity. A nil primaryKey
+ may result in duplicate objects created when working with collections.
+ @param networkProperties An array of network property names
+ @param localProperties An array of local property names that match with the
+ networkProperties
+ 
+ @see [BKEntityPropertiesDescription primaryKey]
+ */
+- (void)registerEntityNamed:(NSString *)entityName
+             withPrimaryKey:(NSString *)primaryKey
+    andMapNetworkProperties:(NSArray *)networkProperties
+          toLocalProperties:(NSArray *)localProperties
+                  inContext:(NSManagedObjectContext *)context;
+
+/**
+ After registering an object you can set the expected date format to be used
+ when transforming JSON date strings to NSDate objects
+ 
+ @param dateFormat String representation of the date format
+ @param property The name of the property for the given entity that is an NSDate
+ @param entity The name of the entity, previously registered with Broker,
+ to set the date format on
+ */
+- (void)setDateFormat:(NSString *)dateFormat
+          forProperty:(NSString *)property
+             onEntity:(NSString *)entity;
+
+- (BKEntityDescription *)entityDescriptionForEntityName:(NSString *)entityName;
+
+@end
diff --git a/Broker/BKEntityMap.m b/Broker/BKEntityMap.m
new file mode 100644
index 0000000..fa31aaa
--- /dev/null
+++ b/Broker/BKEntityMap.m
@@ -0,0 +1,109 @@
+//
+//  BKEntityController.m
+//  Broker
+//
+//  Created by Andrew Smith on 6/6/13.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "BKEntityMap.h"
+#import "BKEntityDescription.h"
+
+// Catagories
+#import "NSManagedObjectContext+Broker.h"
+
+@interface BKEntityMap ()
+
+@property (nonatomic, strong, readwrite) NSMutableDictionary *entityDescriptions;
+
+@end
+
+@implementation BKEntityMap
+
++ (instancetype)entityMap
+{
+    BKEntityMap *map = [self new];
+    map.entityDescriptions = [NSMutableDictionary dictionary];
+    return map;
+}
+
+- (void)registerEntityNamed:(NSString *)entityName
+             withPrimaryKey:(NSString *)primaryKey
+    andMapNetworkProperties:(NSArray *)networkProperties
+          toLocalProperties:(NSArray *)localProperties
+                  inContext:(NSManagedObjectContext *)context
+{
+    __block BKEntityDescription *description;
+    [context performBlockAndWait:^{
+        //
+        // Create new temp object
+        //
+        NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:entityName
+                                                                inManagedObjectContext:context];
+        
+        //
+        // Build description of entity properties
+        //
+        description = [BKEntityDescription descriptionForObject:object];
+        
+        //
+        // Map property names
+        //
+        [description mapNetworkProperties:networkProperties
+                        toLocalProperties:localProperties];
+                
+        //
+        // Set primary key
+        //
+        description.primaryKey = primaryKey;
+        
+        //
+        // Cleanup
+        //
+        [context deleteObject:object];
+    }];
+    
+    @synchronized (self.entityDescriptions) {
+        [self.entityDescriptions setObject:description
+                                    forKey:entityName];
+    }
+}
+
+- (void)setDateFormat:(NSString *)dateFormat
+          forProperty:(NSString *)property
+             onEntity:(NSString *)entity
+{
+    BKEntityDescription *description = [self entityDescriptionForEntityName:entity];
+    BKAttributeDescription *attributeDescription = [description attributeDescriptionForProperty:property];
+    [attributeDescription setDateFormat:dateFormat];
+}
+
+#pragma mark -
+
+- (BKEntityDescription *)entityDescriptionForEntityName:(NSString *)entityName
+{
+    BKEntityDescription *entityDescription = [self.entityDescriptions objectForKey:entityName];
+    NSAssert(entityDescription,
+             @"Could not find entityDescription for entity named \"%@\". Did you\
+             register it with this controller?", entityName);
+    return entityDescription;
+}
+
+@end
diff --git a/Broker/BKJSONController.h b/Broker/BKJSONController.h
new file mode 100644
index 0000000..1675d72
--- /dev/null
+++ b/Broker/BKJSONController.h
@@ -0,0 +1,79 @@
+//
+//  BKJSONController.h
+//  Broker
+//
+//  Created by Andrew Smith on 10/24/13.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+
+@class BKEntityMap;
+
+@interface BKJSONController : NSObject
+
+@property (nonatomic, strong, readonly) NSManagedObjectContext *context;
+
+@property (nonatomic, strong, readonly) BKEntityMap *entityMap;
+
+/**
+ Build a new JSON controller from the supplied context and entity map. The entity
+ map must be previously built.
+ */
++ (instancetype)JSONControllerWithContext:(NSManagedObjectContext *)context
+                                entityMap:(BKEntityMap *)entityMap;
+
+/**
+ Process the json as the entity name indicated. This will look up the registered
+ entity description and attempt to map the JSON key/value to an NSManagedObject.
+ The NSManaged object will be found by looking up the registerd primary key. If
+ no object is found, a new one will be created.
+ @returns The object created or updated from the input json
+ */
+- (NSManagedObject *)processJSONObject:(NSDictionary *)json
+                         asEntityNamed:(NSString *)entityName;
+
+/**
+ @returns The object object you passed in will be returned with the properties set
+ from the JSON
+ */
+- (NSManagedObject *)processJSONObject:(NSDictionary *)json
+                              onObject:(NSManagedObject *)managedObject;
+
+/**
+ Process the JSON as a list of objects of the type indicated by the entity name.
+ 
+ @returns An array of created or updated objects from the JSON array.
+ */
+- (NSArray *)processJSONCollection:(NSArray *)json
+                   asEntitiesNamed:(NSString *)entityName;
+
+/**
+ Proces the JSON as a relationship object, which is either a to-one or to-many. 
+ @param json the JSON to process
+ @oaram relationshipName the name of the relationship on the object
+ @param object the destination object for which the processed JSON is targeted for.
+ */
+- (void)processJSON:(id)json
+    forRelationship:(NSString *)relationshipName
+           onObject:(NSManagedObject *)object;
+
+@end
diff --git a/Broker/BKJSONController.m b/Broker/BKJSONController.m
new file mode 100644
index 0000000..e42ecb4
--- /dev/null
+++ b/Broker/BKJSONController.m
@@ -0,0 +1,242 @@
+//
+//  BKJSONController.m
+//  Broker
+//
+//  Created by Andrew Smith on 10/24/13.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "BKJSONController.h"
+#import "BrokerLog.h"
+
+// Models
+#import "BKEntityDescription.h"
+
+// Controllers
+#import "BKEntityMap.h"
+
+// Cats
+#import "NSManagedObjectContext+Broker.h"
+#import "NSString+Broker.h"
+
+@interface BKJSONController ()
+
+@property (nonatomic, strong, readwrite) NSManagedObjectContext *context;
+
+@property (nonatomic, strong, readwrite) BKEntityMap *entityMap;
+
+@end
+
+@implementation BKJSONController
+
++ (instancetype)JSONControllerWithContext:(NSManagedObjectContext *)context
+                                entityMap:(BKEntityMap *)entityMap
+{
+    BKJSONController *controller = [self new];
+    controller.context = context;
+    controller.entityMap = entityMap;
+    return controller;
+}
+
+#pragma mark - Processing
+
+- (NSManagedObject *)processJSONObject:(NSDictionary *)json
+                         asEntityNamed:(NSString *)entityName
+{
+    //
+    // We expect a dictionary
+    //
+    if (json == nil || entityName == nil) {
+        return nil;
+    }
+    NSAssert([json isKindOfClass:[NSDictionary class]],
+             @"Expected JSON to be an NSDictionary");
+    if (![json isKindOfClass:[NSDictionary class]]) {
+        BrokerLog(@"Expected JSON to be an NSDictionary for entity \"%@\"", entityName);
+        return nil;
+    }
+    
+    //
+    // Get the entity description
+    //
+    BKEntityDescription *entityDescription = [self.entityMap entityDescriptionForEntityName:entityName];
+    
+    //
+    // Get the primary key. If there is no primary key, then duplicate objects may
+    // be created.
+    //
+    id primaryKey = [entityDescription primaryKeyForJSON:json];
+    
+    //
+    // Find target object, or create a new one if it doesn't exist
+    //
+    NSManagedObject *managedObject = [self.context bkr_findOrCreateObjectForEntityDescription:entityDescription
+                                                                              primaryKeyValue:primaryKey];
+    
+    return [self processJSONObject:json
+                          onObject:managedObject];
+}
+
+- (NSManagedObject *)processJSONObject:(NSDictionary *)json
+                              onObject:(NSManagedObject *)managedObject
+{
+    //
+    // We expect a dictionary
+    //
+    if (json == nil || managedObject == nil) {
+        return nil;
+    }
+    NSAssert([json isKindOfClass:[NSDictionary class]],
+             @"Expected JSON to be an NSDictionary");
+    if (![json isKindOfClass:[NSDictionary class]]) {
+        BrokerLog(@"Expected JSON to be an NSDictionary for entity \"%@\"", entityName);
+        return nil;
+    }
+    
+    //
+    // Get the entity description
+    //
+    BKEntityDescription *entityDescription = [self.entityMap entityDescriptionForEntityName:managedObject.entity.name];
+    
+    //
+    // For each property in the JSON, if it is a relationship, process the relationship.
+    // Otherwise it's just a flat attribute, so set the value.
+    //
+    for (NSString *jsonProperty in json) {
+        //
+        // Get the local NSManagedObject property name.
+        //
+        NSString *localProperty = [entityDescription localPropertyNameForProperty:jsonProperty];
+        
+        //
+        // Skip if this property is not yet implemented
+        //
+        NSString *setter = [NSString stringWithFormat:@"set%@:", [localProperty bkr_uppercaseFirstLetterOnlyString]];
+        if (![managedObject respondsToSelector:NSSelectorFromString(setter)]) {
+            BrokerLog(@"No description for property \"%@\" found on entity \"%@\"!\
+                      Tried to use setter named \"%@\".",
+                      localProperty,
+                      entityDescription.internalEntityDescription.name,
+                      setter);
+        } else {
+            //
+            // Get the NSObject value
+            //
+            id value = json[jsonProperty];
+            id object = [entityDescription objectFromValue:value
+                                               forProperty:localProperty];
+            
+            if ([entityDescription isPropertyRelationship:localProperty]) {
+                //
+                // Process as a relationship on parent object.
+                //
+                [self processJSON:object
+                  forRelationship:localProperty
+                         onObject:managedObject];
+            } else {
+                //
+                // Flat attribute. Simply set the value.
+                //
+                [managedObject setValue:object
+                                 forKey:localProperty];
+            }
+        }
+    }
+    return managedObject;
+}
+
+- (NSArray *)processJSONCollection:(NSArray *)json
+                   asEntitiesNamed:(NSString *)entityName
+{
+    //
+    // We expect an array
+    //
+    if (json == nil || entityName == nil) {
+        return nil;
+    }
+    NSAssert([json isKindOfClass:[NSArray class]],
+             @"Expected JSON to be an NSArray");
+    if (![json isKindOfClass:[NSArray class]]) {
+        BrokerLog(@"Expected JSON to be an NSArray for entity \"%@\"", entityName);
+        return nil;
+    }
+    //
+    // An array of entities. Find or create each.
+    //
+    NSMutableArray *managedObjects = [NSMutableArray arrayWithCapacity:json.count];
+    for (NSDictionary *object in json) {
+        NSManagedObject *managedObject = [self processJSONObject:object
+                                                   asEntityNamed:entityName];
+        [managedObjects addObject:managedObject];
+    }
+    return managedObjects;
+}
+
+- (void)processJSON:(id)json
+    forRelationship:(NSString *)relationshipName
+           onObject:(NSManagedObject *)object
+{
+    //
+    // Get the entity description
+    //
+    BKEntityDescription *entityDescription = [self.entityMap entityDescriptionForEntityName:object.entity.name];
+    
+    //
+    // Grab the relationship description from the entity description
+    //
+    NSRelationshipDescription *relationshipDescription = [entityDescription relationshipDescriptionForProperty:relationshipName];
+    if (!relationshipDescription) return;
+    
+    //
+    // Sanity check. Can't be a toMany without an array.
+    //
+    NSAssert(!(relationshipDescription.isToMany && ![json isKindOfClass:[NSArray class]]),
+             @"Looks like your JSON is malformed. The registerd relationship is\
+             expecting an array for the attribute named \"%@\"", relationshipName);
+    
+    if (relationshipDescription.isToMany) {
+        //
+        // Fetch the objects relationship objects. We add the found or created
+        // objects to this mutable set.
+        //
+        NSMutableSet *existingRelationshipObjects = [object mutableSetValueForKey:relationshipDescription.name];
+        
+        //
+        // Find or create relationship objects to add
+        //
+        NSArray *objectsToAdd = [self processJSONCollection:json
+                                            asEntitiesNamed:relationshipDescription.destinationEntity.name];
+        
+        //
+        // Merge existing and new
+        //
+        [existingRelationshipObjects unionSet:[NSSet setWithArray:objectsToAdd]];
+    } else {
+        //
+        // Single object. Find or create, then set value.
+        //
+        NSManagedObject *destinationObject = [self processJSONObject:json
+                                                       asEntityNamed:relationshipDescription.destinationEntity.name];
+        [object setValue:destinationObject
+                  forKey:relationshipName];
+    }
+}
+
+@end
diff --git a/Broker/Broker.h b/Broker/Broker.h
new file mode 100644
index 0000000..e93be92
--- /dev/null
+++ b/Broker/Broker.h
@@ -0,0 +1,37 @@
+//
+//  BrokerHeaders.h
+//  Broker
+//
+//  Created by Andrew Smith on 10/25/13.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef Broker_BrokerHeaders_h
+#define Broker_BrokerHeaders_h
+
+#import "BKController.h"
+#import "BKJSONController.h"
+#import "BKAttributeDescription.h"
+#import "BKEntityDescription.h"
+#import "BKEntityMap.h"
+#import "NSManagedObject+Broker.h"
+#import "NSManagedObjectContext+Broker.h"
+
+#endif
diff --git a/Broker/BrokerLog.h b/Broker/BrokerLog.h
new file mode 100644
index 0000000..5fe4716
--- /dev/null
+++ b/Broker/BrokerLog.h
@@ -0,0 +1,26 @@
+//
+//  BrokerLog.h
+//  Broker
+//
+//  Created by Andrew Smith on 6/13/14.
+//  Copyright (c) 2014 Andrew B. Smith. All rights reserved.
+//
+
+#ifndef Broker_BrokerLog_h
+#define Broker_BrokerLog_h
+
+#define BROKER_LOG 0
+
+#if DEBUG && BROKER_LOG
+    #define BrokerLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
+    #define BrokerWarningLog(...) NSLog(@"\n!!!!\n%s %@\n!!!!\n", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
+#else
+    #define BrokerLog(...) do { } while (0)
+    #define BrokerWarningLog(...) do { } while (0)
+#endif
+
+#ifndef NS_BLOCK_ASSERTIONS
+    #define NS_BLOCK_ASSERTIONS
+#endif
+
+#endif
diff --git a/Broker/NSManagedObject+Broker.h b/Broker/NSManagedObject+Broker.h
new file mode 100644
index 0000000..1023e2f
--- /dev/null
+++ b/Broker/NSManagedObject+Broker.h
@@ -0,0 +1,50 @@
+//
+//  NSManagedObject+Broker.h
+//  Broker
+//
+//  Created by Andrew Smith on 6/25/12.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <CoreData/CoreData.h>
+
+@class BKController;
+
+@interface NSManagedObject (Broker)
+
+/**
+ Registers the entity with the Broker instance. Any custom NSManagedObject that
+ registers with broker should override this method if you want to do things like
+ map local property names to network property names. By default this will register
+ the primary key as lowercase claseNameID. So for an Employee object, the
+ primary key will be employeeID.
+ 
+ @param broker The Broker instance to register the entity with
+ @param context The main thread NSManagedObjectContext;
+ */
++ (void)bkr_registerWithBroker:(BKController *)controller
+                     inContext:(NSManagedObjectContext *)context;
+
+/**
+ The absolute truth as to whether the object has been deleted in its context.
+ */
+- (BOOL)bkr_hasBeenDeleted;
+
+@end
diff --git a/Broker/NSManagedObject+Broker.m b/Broker/NSManagedObject+Broker.m
new file mode 100644
index 0000000..b21ffd9
--- /dev/null
+++ b/Broker/NSManagedObject+Broker.m
@@ -0,0 +1,59 @@
+//
+//  NSManagedObject+Broker.m
+//  Broker
+//
+//  Created by Andrew Smith on 6/25/12.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#import "NSManagedObject+Broker.h"
+
+#import "BKController.h"
+#import "BKEntityMap.h"
+
+@implementation NSManagedObject (Broker)
+
++ (void)bkr_registerWithBroker:(BKController *)controller
+                     inContext:(NSManagedObjectContext *)context
+{
+    [controller.entityMap registerEntityNamed:NSStringFromClass(self)
+                               withPrimaryKey:nil
+                      andMapNetworkProperties:nil
+                            toLocalProperties:nil
+                                    inContext:context];
+}
+
+- (BOOL)bkr_hasBeenDeleted
+{    
+    /**
+     Sometimes CoreData will fault a particular instance, while there is still
+     the same object in the store.  Check to see if there is a clone.
+     */
+    NSManagedObjectID *objectID           = [self objectID];
+    NSManagedObject   *managedObjectClone = [[self managedObjectContext] existingObjectWithID:objectID 
+                                                                                        error:NULL];
+    
+    if (!managedObjectClone || [self isDeleted]) {
+        return YES;
+    } else {
+        return NO;
+    }
+}
+
+@end
diff --git a/Broker/NSManagedObjectContext+Broker.h b/Broker/NSManagedObjectContext+Broker.h
new file mode 100644
index 0000000..1bdf4aa
--- /dev/null
+++ b/Broker/NSManagedObjectContext+Broker.h
@@ -0,0 +1,35 @@
+//
+//  NSManagedObjectContext+Broker.h
+//  Broker
+//
+//  Created by Andrew Smith on 7/24/12.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <CoreData/CoreData.h>
+
+@class BKEntityDescription;
+
+@interface NSManagedObjectContext (Broker)
+
+- (NSManagedObject *)bkr_findOrCreateObjectForEntityDescription:(BKEntityDescription *)description
+                                            primaryKeyValue:(id)value;
+
+@end
diff --git a/Broker/NSManagedObjectContext+Broker.m b/Broker/NSManagedObjectContext+Broker.m
new file mode 100644
index 0000000..643d338
--- /dev/null
+++ b/Broker/NSManagedObjectContext+Broker.m
@@ -0,0 +1,62 @@
+//
+//  NSManagedObjectContext+Broker.m
+//  Broker
+//
+//  Created by Andrew Smith on 7/24/12.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "NSManagedObjectContext+Broker.h"
+#import "BKEntityDescription.h"
+
+@implementation NSManagedObjectContext (Broker)
+
+- (NSManagedObject *)bkr_findOrCreateObjectForEntityDescription:(BKEntityDescription *)description
+                                            primaryKeyValue:(id)value
+{
+    NSFetchRequest *request = [[NSFetchRequest alloc] init];
+    [request setEntity:description.internalEntityDescription];
+    request.includesSubentities = NO;
+    
+    NSArray *fetchedObjects;
+    
+    if (description.primaryKey) {
+        [request setPredicate:[NSPredicate predicateWithFormat:@"self.%@ = %@", description.primaryKey, value]];
+        
+        NSError *error;
+        fetchedObjects = [self executeFetchRequest:request error:&error];
+        if (error) {
+            NSLog(@"Fetch Error: %@", error);
+        }
+    }
+    
+    if (fetchedObjects.count == 0) {
+        NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:description.internalEntityDescription.name
+                                                                inManagedObjectContext:self];
+        return object;
+    } else if (fetchedObjects.count >= 1) {
+        return (NSManagedObject *)[fetchedObjects objectAtIndex:0];
+    }
+    
+    return nil;
+
+}
+
+@end
diff --git a/Broker/NSString+Broker.h b/Broker/NSString+Broker.h
new file mode 100644
index 0000000..9b6cbed
--- /dev/null
+++ b/Broker/NSString+Broker.h
@@ -0,0 +1,32 @@
+//
+//  NSString+Broker.h
+//  Broker
+//
+//  Created by Andrew Smith on 1/8/14.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSString (Broker)
+
+- (NSString *)bkr_uppercaseFirstLetterOnlyString;
+
+@end
diff --git a/Broker/NSString+Broker.m b/Broker/NSString+Broker.m
new file mode 100644
index 0000000..1f30534
--- /dev/null
+++ b/Broker/NSString+Broker.m
@@ -0,0 +1,36 @@
+//
+//  NSString+Broker.m
+//  Broker
+//
+//  Created by Andrew Smith on 1/8/14.
+//  Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "NSString+Broker.h"
+
+@implementation NSString (Broker)
+
+- (NSString *)bkr_uppercaseFirstLetterOnlyString
+{
+    NSString *firstCharacterInString = [[self substringToIndex:1] uppercaseString];
+    return [self stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:firstCharacterInString];
+}
+
+@end
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6aaf5e1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Andrew B. Smith (http://github.com/Drewsmits)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3d8a650
--- /dev/null
+++ b/README.md
@@ -0,0 +1,166 @@
+# 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.
+
+