Sync eases your every day job of parsing a JSON
response and getting it into Core Data. It uses a convention over configuration paradigm to facilitate your workflow.
- Handles operations in safe background threads
- Thread safe saving, we handle retrieving and storing objects in the right threads
- Diffing of changes, updated, inserted and deleted objects (which are automatically purged for you)
- Auto-mapping of relationships (one-to-one, one-to-many and many-to-many)
- Smart-updates, only updates your
NSManagedObject
s if the server values are different (useful when usingNSFetchedResultsController
delegates) - Uniquing, Core Data does this based on
objectID
s, we use your remote key (such asid
) for this
Sync.changes(
changes: [AnyObject]!,
inEntityNamed: String!,
dataStack: DATAStack!,
completion: ((NSError!) -> Void)!)
+ (void)changes:(NSArray *)changes
inEntityNamed:(NSString *)entityName
dataStack:(DATAStack *)dataStack
completion:(void (^)(NSError *error))completion
changes
: JSON responseentityName
: Core Data’s Model Entity Name (such as User, Note, Task)dataStack
: Your DATAStack
[
{
"id": 6,
"name": "Shawn Merrill",
"email": "shawn@ovium.com",
"created_at": "2014-02-14T04:30:10+00:00",
"updated_at": "2014-02-17T10:01:12+00:00",
"notes": [
{
"id": 0,
"text": "Shawn Merril's diary, episode 1",
"created_at": "2014-03-11T19:11:00+00:00",
"updated_at": "2014-04-18T22:01:00+00:00"
}
]
}
]
[Sync changes:JSON
inEntityNamed:@"User"
dataStack:dataStack
completion:^{
// New objects have been inserted
// Existing objects have been updated
// And not found objects have been deleted
}];
Alternatively if you only want to sync users that have been created in the last 24 hours, you could do this by using a NSPredicate
.
NSDate *now = [NSDate date];
NSDate *yesterday = [now dateByAddingTimeInterval:-24*60*60];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"createdAt > %@", yesterday];
[Sync changes:JSON
inEntityNamed:@"User"
predicate:predicate
dataStack:dataStack
completion:^{
//...
}];
Sync is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'Sync'
Replace your Core Data stack with an instance of DATAStack.
self.dataStack = [[DATAStack alloc] initWithModelName:@"Demo"];
Then add this to your App Delegate so everything gets persisted when you quit the app.
- (void)applicationWillTerminate:(UIApplication *)application {
[self.dataStack persistWithCompletion:nil];
}
Sync requires your entities to have a primary key, this is important for diffing otherwise Sync doesn’t know how to differentiate between entries.
By default Sync uses id
from the JSON and remoteID
from Core Data as the primary key. You can mark any attribute as primary key by adding hyper.isPrimaryKey
and the value YES
.
For example in our Designer News project we have a Comment
entity that uses body
as the primary key.
Your attributes should match their JSON counterparts in camelCase
notation instead of snake_case
. For example first_name
in the JSON maps to firstName
in Core Data and address
in the JSON maps to address
in Core Data.
There are two exceptions to this rule:
id
s should matchremoteID
- Reserved attributes should be prefixed with the
entityName
(type
becomesuserType
,description
becomesuserDescription
and so on). In the JSON they don't need to change, you can keeptype
anddescription
for example. A full list of reserved attributes can be found here
If you want to map your Core Data attribute with a JSON attribute that has different naming, you can do by adding hyper.remoteKey
in the user info box with the value you want to map.
To map arrays or dictionaries just set attributes as Binary Data
on the Core Data modeler.
{
"hobbies": [
"football",
"soccer",
"code"
]
}
NSArray *hobbies = [NSKeyedUnarchiver unarchiveObjectWithData:managedObject.hobbies];
// ==> "football", "soccer", "code"
{
"expenses" : {
"cake" : 12.50,
"juice" : 0.50
}
}
NSDictionary *expenses = [NSKeyedUnarchiver unarchiveObjectWithData:managedObject.expenses];
// ==> "cake" : 12.50, "juice" : 0.50
We went for just supporting ISO8601 out of the box because that's the most common format when parsing dates, also we have a quite performant way to parse this strings which overcomes the performance issues of using NSDateFormatter
.
{
"created_at": "2014-01-01T00:00:00+00:00",
"updated_at": "2014-01-02",
"number_of_attendes": 20
}
NSDate *createdAt = [managedObject valueForKey:@"createdAt"];
// ==> "2014-01-01 00:00:00 +00:00"
NSDate *updatedAt = [managedObject valueForKey:@"updatedAt"];
// ==> "2014-01-02 00:00:00 +00:00"
You are free to use any networking library.
iOS 7 or above
Sync wouldn’t be possible without the help of this fully tested components:
-
DATAStack: Core Data stack and thread safe saving
-
DATAFilter: Helps you purge deleted objects, internally we use it to diff inserts, updates and deletes. Also it’s used for uniquing Core Data does this based on objectIDs, DATAFilter uses your remote keys (such as id) for this
-
NSManagedObject-HYPPropertyMapper: Maps JSON fields with their Core Data counterparts, it does most of it’s job using the paradigm “convention over configuration”
Hyper made this. We’re a digital communications agency with a passion for good code and delightful user experiences. If you’re using this library we probably want to hire you (we consider remote employees too, the only requirement is that you’re awesome).
Sync is available under the MIT license. See the LICENSE file for more info.