• Stars
    star
    534
  • Rank 83,095 (Top 2 %)
  • Language
    Objective-C
  • License
    Other
  • Created over 10 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

From JSON to Core Data and back.

Groot

Carthage compatible

Groot provides a simple way of serializing Core Data object graphs from or into JSON.

It uses annotations in the Core Data model to perform the serialization and provides the following features:

  1. Attribute and relationship mapping to JSON key paths.
  2. Value transformation using named NSValueTransformer objects.
  3. Object graph preservation.
  4. Support for entity inheritance

Installing Groot

Using CocoaPods

Add the following to your Podfile:

use_frameworks!
pod 'Groot'

Or, if you need to support iOS 6 / OS X 10.8:

pod 'Groot/ObjC'

Then run $ pod install.

If you donโ€™t have CocoaPods installed or integrated into your project, you can learn how to do so here.

Using Carthage

Add the following to your Cartfile:

github "gonzalezreal/Groot"

Then run $ carthage update.

Follow the instructions in Carthageโ€™s README to add the framework to your project.

You may need to set Embedded Content Contains Swift Code to YES in the build settings for targets that only contain Objective-C code.

Getting started

Consider the following JSON describing a well-known comic book character:

{
    "id": "1699",
    "name": "Batman",
    "real_name": "Bruce Wayne",
    "powers": [
        {
            "id": "4",
            "name": "Agility"
        },
        {
            "id": "9",
            "name": "Insanely Rich"
        }
    ],
    "publisher": {
        "id": "10",
        "name": "DC Comics"
    }
}

We could translate this into a Core Data model using three entities: Character, Power and Publisher.

Model

Mapping attributes and relationships

Groot relies on the presence of certain key-value pairs in the user info dictionary associated with entities, attributes and relationships to serialize managed objects from or into JSON. These key-value pairs are often referred in the documentation as annotations.

In our example, we should add a JSONKeyPath in the user info dictionary of each attribute and relationship specifying the corresponding key path in the JSON:

  • id for the identifier attribute,
  • name for the name attribute,
  • real_name for the realName attribute,
  • powers for the powers relationship,
  • publisher for the publisher relationship,
  • etc.

Attributes and relationships that don't have a JSONKeyPath entry are not considered for JSON serialization or deserialization.

Value transformers

When we created the model we decided to use Integer 64 for our identifier attributes. The problem is that, for compatibility reasons, the JSON uses strings for id values.

We can add a JSONTransformerName entry to each identifier attribute's user info dictionary specifying the name of a value transformer that converts strings to numbers.

Groot provides a simple way for creating and registering named value transformers:

// Swift

func toString(_ value: Int) -> String? {
    return String(value)
}

func toInt(_ value: String) -> Int? {
    return Int(value)
}

ValueTransformer.setValueTransformer(withName: "StringToInteger", transform: toInt, reverseTransform: toString)
// Objective-C

[NSValueTransformer grt_setValueTransformerWithName:@"StringToInteger" transformBlock:^id(NSString *value) {
    return @([value integerValue]);
} reverseTransformBlock:^id(NSNumber *value) {
    return [value stringValue];
}];

Object graph preservation

To preserve the object graph and avoid duplicating information when serializing managed objects from JSON, Groot needs to know how to uniquely identify your model objects.

In our example, we should add an identityAttributes entry to the Character, Power and Publisher entities user dictionaries with the value identifier.

Adding the identityAttributes annotation to your entities can affect performance when serializing from JSON. For more information see Object Uniquing Performance.

For more information about annotating your model have a look at Annotations.

Serializing from JSON

Now that we have our Core Data model ready we can start adding some data.

// Swift

let batmanJSON: JSONDictionary = [
    "name": "Batman",
    "id": "1699",
    "powers": [
        [
            "id": "4",
            "name": "Agility"
        ],
        [
            "id": "9",
            "name": "Insanely Rich"
        ]
    ],
    "publisher": [
        "id": "10",
        "name": "DC Comics"
    ]
]

do {
    let batman: Character = try object(fromJSONDictionary: batmanJSON, inContext: context)
} catch let error as NSError {
    // handle error
}
// Objective-C

Character *batman = [GRTJSONSerialization objectWithEntityName:@"Character"
                                            fromJSONDictionary:batmanJSON
                                                     inContext:self.context
                                                         error:&error];

If we want to update the object we just created, Groot can merge the changes for us:

// Swift

let updateJSON: JSONDictionary = [
    "id": "1699",
    "real_name": "Bruce Wayne",
]

do {
    // This will return the previously created managed object
    let batman: Character = try object(fromJSONDictionary: updateJSON, inContext: context)
} catch let error as NSError {
    // handle error
}

Serializing relationships from identifiers

Suppose that our API does not return full objects for the relationships but only the identifiers.

We don't need to change our model to support this situation:

// Swift

let batmanJSON: JSONDictionary = [
    "name": "Batman",
    "real_name": "Bruce Wayne",
    "id": "1699",
    "powers": ["4", "9"],
    "publisher": "10"
]

do {
    let batman: Character = try object(fromJSONDictionary: batmanJSON, inContext: context)
} catch let error as NSError {
    // handle error
}

The above code creates a full Character object and the corresponding relationships pointing to Power and Publisher objects that just have the identifier attribute populated.

We can import powers and publisher from different JSON objects and Groot will merge them nicely:

// Swift

let powersJSON: JSONArray = [
    [
        "id": "4",
        "name": "Agility"
    ],
    [
        "id": "9",
        "name": "Insanely Rich"
    ]
]

let publisherJSON: JSONDictionary = [
    "id": "10",
    "name": "DC Comics"
]

do {
    let _: [Power] = try objects(fromJSONArray: powersJSON, inContext: context)
    let _: Publisher = try object(fromJSONDictionary: publisherJSON, inContext: context)
} catch let error as NSError {
    // handle error
}

Note that serializing relationships from identifiers only works with entities specifying only one attribute as the value of identityAttributes annotation.

For more serialization alternatives check Groot.swift and GRTJSONSerialization.h.

Entity inheritance

Groot supports entity inheritance via the entityMapperName annotation.

If you are using SQLite as your persistent store, Core Data implements entity inheritance by creating one table for the parent entity and all child entities, with a superset of all their attributes. This can obviously have unintended performance consequences if you have a lot of data in the entities, so use this feature wisely.

Object Uniquing Performance

Object uniquing can affect performance when serialising from JSON, as it requires fetching data from the database before inserting.

If you take a look on how Groot is implemented, there are three serialization strategies:

  • Insert
  • Uniquing
  • Composite Uniquing

As you may guess, the first one is the most performant as it does not fetch from the database. If you know that there is no duplicate data in your data set, DO NOT set identityAttributes in your entity. This will make Groot use the Insert strategy.

Groot will pick the Uniquing strategy if the identityAttributes annotation has a single attribute, otherwise it will pick the Composite Uniquing strategy.

The Uniquing strategy requires one fetch for every array of JSON objects, whereas the Composite Uniquing strategy requires one fetch for every single JSON object (it is potentially the slowest of the three strategies).

Serializing to JSON

Groot provides methods to serialize managed objects back to JSON:

// Swift

let result = json(fromObject: batman)
// Objective-C

NSDictionary *JSONDictionary = [GRTJSONSerialization JSONDictionaryFromObject:batman];

For more serialization alternatives check Groot.swift and GRTJSONSerialization.h.

Contact

Guillermo Gonzalez
@gonzalezreal

License

Groot is available under the MIT license.

More Repositories

1

swift-markdown-ui

Display and customize Markdown text in SwiftUI
Swift
2,372
star
2

Vertigo

Simple full screen image viewer with image zoom custom view controller transition
Objective-C
355
star
3

DefaultCodable

A convenient way to handle default values with Swift Codable types
Swift
353
star
4

AttributedText

Render attributed strings in SwiftUI
Swift
122
star
5

Markup

Lightweight markup text formatting in Swift
Swift
95
star
6

AdaptiveCardUI

Snippets of UI, authored in JSON and rendered with SwiftUI
Swift
92
star
7

ReadingList

An example on using the Mantle Modeling Framework with Overcoat AFNetworking extension.
Objective-C
86
star
8

NetworkImage

Asynchronous image loading in SwiftUI
Swift
69
star
9

SimpleNetworking

Scalable and composable API Clients using Swift Combine
Swift
51
star
10

IndeterminateTypesWithCodable

Indeterminate Types with Codable in Swift
Swift
37
star
11

Borders

Sample code for Consuming Web Services with Swift and ReactiveX
Swift
33
star
12

TGRDataSource

Convenience UITableView and UICollectionView data sources
Objective-C
24
star
13

SwiftCommonMark

Parse and create CommonMark documents in Swift.
C
23
star
14

Gig

A great Twitter API client for Objective-C
Objective-C
19
star
15

Reusable

iOS cell registration and reusing with generics and protocol extensions
Swift
13
star
16

DirectLine

Swift client library for Microsoft Bot Framework's Direct Line protocol
Swift
11
star
17

rxswift-gentle-introduction

A gentle introduction to RxSwift http://www.vsmobile.tech
7
star
18

swift-any-value

A Swift Codable type that serves as a placeholder for any JSON value
Swift
3
star
19

NSSpain2016

From Design to Code
3
star
20

TwitterTimeline

Source code for my article 'Implementando una timeline de Twitter con Core Data'
Objective-C
2
star