Modeling JSON Mappings – Part 1

Jonathan Lehr


8 min read
A screenshot of a JSON mapping of country codes with a dark vignette

Back to the Future

Mandatory Capabilities

  • Map a given JSON dictionary to a specific class
  • Construct a model object on decode
  • Construct a dictionary on encode
  • Map JSON data values to object properties
  • Associate JSON element names with object property names
  • Allow specification of value transformations, and automatically apply them during encode/decode
  • Populate model object properties with JSON values on decode
  • Populate dictionary with model object property values on encode
  • Model to-one, and to-many relationships
  • Store type information for related objects
  • Construct child objects and arrays of child objects on decode
  • Construct child dictionaries and arrays of child dictionaries on encode

Nice to Haves

  • Flattened attributes
  • Inverse relationships
  • Core Data directly supports model versioning, allowing earlier versions of a given data model to be accessed at runtime, making it easier for apps to handle API version differences.
  • External tools can leverage the metadata to, for example, generate base classes (via Xcode’s built-in class generation facilities, as well as third-party tools such as mogenerator)
  • An entire data model can be version controlled as a single unit, making differences between versions more apparent.
  • The model can be presented and edited in a visual tool such as Xcode’s Model Editor

The Modelmatic Framework

  "version" : "2",
  "batchSize" : "10",
  "authors" : [
      "firstName" : "William",
      "lastName" : "Shakespeare",
      "born" : "1564-04-01",
      "author_id" : "101",
      "imageURL" : "https:\/\/",
      "books" : [
          "tags" : "drama,fantasy",
          "title" : "The Tempest",
          "year" : "2013",
          "book_id" : "3001"

Step 1: Defining the Model

To use Modelmatic, you start by modeling your data using Xcode’s Core Data Model Editor. Don’t worry, you’re not going to need to use other aspects of Core Data, just the data model — and just a subset of it’s capabilities.

A screenshot of a modelmatic diagram

Step 2: Create Swift Classes

If your model is complex, and/or changes frequently, consider using mogenerator to generate model classes (and update them as needed) from the metadata you specified in the model editor. Otherwise, it’s simplest to just create the classes you need from scratch. Here’s an example:

import Foundation
import Modelmatic

@objc (MDLAuthor)
 class Author: ModelObject
    // Name of the Core Data entity
    static let entityName = "Author"

    // Mapped to 'author_id' in the corresponding attribute's User Info dictionary
    var authorId: NSNumber!
    var firstName: String?
    var lastName: String?
    var dateOfBirth: NSDate?
    var imageURL: UIImage?

    // Modeled relationship to 'Book' entity
    var books: [Book]?

Key points:

  • import Modelmatic.
  • Subclass ModelObject.
  • Use @objc() to avoid potential namespacing issues.
  • Define a static let constant named entityName to specify the name of the associated entity in the Core Data model file.
  • authorId is mapped to author_id in the model (see the attribute definition’s User Info dictionary).
  • Modelmatic automatically maps all the other properties, included the nested books property.

Customizing Mappings

Modelmatic automatically matches names of properties you specify as attributes or relationships in your Core Data model to corresponding keys in the JSON dictionary. For example, given an attribute named firstName, Modelmatic will try to use firstName as a key in the JSON dictionary, and map it to a firstName property in Author.

However, the framework also allows you to specify custom mappings as needed. For instance, the Author class has the following property:

var authorId: NSNumber!
A screenshot of part of a code editor

A custom mapping is provided in the model file, binding the authorId attribute to the JSON key path author_id, as shown below:

To add a custom mapping, select an attribute or relationship in the model editor, and add an entry to it’s User Info dictionary. The key should be jsonKeyPath, and the value should be the key or key path (dot-separated property path) used in the JSON dictionary. During encoding and decoding, Modelmatic will automatically map between your object’s property, as defined by its attribute or relationship name, and the custom key path you specified to access JSON values.

Defining Relationships

Core Data allows you to define to-one and to-many relationships between entities. Modelmatic will automatically create and populate nested objects for which you’ve defined relationships. For instance, the Modelmatic example app defines a to-many relationship from the Author entity to the Book entity. To create an Author instance along with its nested array of books, you simply initialize an Author with a JSON dictionary as follows:

let author = Author(dictionary: $0, entity: entity)

For example, given the following JSON, the previous call would create and populate an instance of Author containing an array of two Book objects, with their author properties set to point back to the Author instance):

      "author_id" : "106"
      "firstName" : "Mark",
      "lastName" : "Twain",
      "books" : [
          "book_id" : "3501",
          "title" : "A Connecticut Yankee in King Arthur's Court",
          "year" : "2014"
          "book_id" : "3502",
          "title" : "The Prince and the Pauper",
          "year" : "2015"

Property Types

Modelmatic uses methods defined in the NSKeyValueCoding (KVC) protocol to set model object property values. KVC can set properties of any Objective-C type, but has limited ability to deal with pure Swift types, particularly struct and enum types. However bridged Standard Library types, such as StringArrayDictionary, as well as scalar types such as IntDoubleBool, etc. are handled automatically by KVC with one notable issue: Swift scalars wrapped in Optionals. For example, KVC would be unable to set the following property:

var rating: Int?

If your ModelObject subclasses uses a Swift type that KVC can’t directly handle, you can provide a computed property of the same name, prefixed with kvc_, to provide your own custom handling. For example, to make the rating property work with Modelmatic, add the following:

var kvc_rating: Int {
        get { return rating ?? 0 }
        set { rating = Optional(newValue) }

If Modelmatic is unable to set a property directly (in this case the rating property), it will automatically call the kvc_prefixed variant (kvc_rating, in this example).

Specifying Value Transformations

In your Core Data model file, you can specify a property type as Transformable. If you do so, you can then provide the name of a custom transformer. For example, the Author class in the Modelmatic example app has a transformable property, dateOfBirth, of type NSDate. Modelmatic automatically uses an instance of the specified NSValueTransformersubclass to transform the value when accessing the property.

Here’s the code of the Example app’s DateTransformer class in its entirety:

import Foundation

@objc (MDLDateTransformer)
class DateTransformer: NSValueTransformer
    static let transformerName = "Date"

    override class func transformedValueClass() -> AnyClass { return NSString.self }
    override class func allowsReverseTransformation() -> Bool { return true }

    override func transformedValue(value: AnyObject?) -> AnyObject? {
        guard let date = value as? NSDate else { return nil }
        return serializedDateFormatter.stringFromDate(date)

    override func reverseTransformedValue(value: AnyObject?) -> AnyObject? {
        guard let stringVal = value as? String else { return nil }
        return serializedDateFormatter.dateFromString(stringVal)

private let serializedDateFormatter: NSDateFormatter = {
    let formatter = NSDateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    return formatter

The date transformer is registered by the following line of code in the Example app’s AuthorObjectStore class:

NSValueTransformer.setValueTransformer(DateTransformer(), forName: String(DateTransformer.transformerName))

Step 3: Loading the Model

Somewhere in your app (you only need to do this once during the app’s lifecycle), do something like the following to load the Core Data model file into memory:

let modelName = "Authors"

guard let modelURL = NSBundle(forClass: self.dynamicType).URLForResource(modelName, withExtension: "momd"),
    model = NSManagedObjectModel(contentsOfURL: modelURL) else {
        print("Unable to load model \(modelName)")

You’ll most likely want to store the reference to the model in a class property.

Step 4: Encoding and Decoding Model Objects

Once you’ve obtained JSON data, you can deserialize it as follows (Note that deserializeJson wraps a call to NSJSONSerialization):

guard let data = data, dict = try? data.deserializeJson() else { 

To construct an instance of your model class, simply provide the dictionary of deserialized values, along with the entity description:

let authors = Author(dictionary: $0, entity: entity)

This will construct and populate an instance of Author, as well as any nested objects for which you defined relationships in the model (and for which the JSON contains data). You then simply work with your model objects. Whenever you want to serialize an object or group of objects, simply do as follows:

// Encode the author
let authorDict = author.dictionaryRepresentation

// Serialize data
if let data = try? dict.serializeAsJson(pretty: true) {
    // Do something with the data...

Setting Related Objects Programmatically

Modelmatic provides methods to make it easier to programmatically set objects for properties that model to-one or to-many relationships. While it’s easy enough to remove objects (simply set to-one properties to nil, or use array methods to remove objects from arrays), setting or adding objects to these properties can be slightly more involved. That’s because Modelmatic automatically sets property values for any inverse relationships you define in your model, so that child objects will have references to their parents.

While inverse relationships aren’t required, they’re often convenient. Just be sure to use the weak lifetime qualifier for references to parent objects.

Even if you’re not currently using inverse relationships, it’s a good idea to use the convenience methods provided by ModelObject for modifying relationship values. That way, if you change your mind later, you won’t need to change your code to add support for setting parent references.

To-Many Relationships

ModelObject provides two methods for modifying to-many relationships, as shown in the following examples:

// Adding an object to a to-many relationship
let author = Author(dictionary: authorDict, entity: authorEntity)
let book = Book(dictionary: bookDict, entity: bookEntity)
do {
    // Adds a book to the author's 'books' array, and sets the book's 'author' property
    try author.add(modelObject: book, forKey: "books")
catch MappingError.unknownRelationship(let name) {
    print("Unknown relationship \(name)")

// Adding an array of objects to a to-many relationship
let books = [Book(dictionary: bookDict2, entity: bookEntity),
             Book(dictionary: bookDict3, entity: bookEntity)]
do {
    // Adds two books to the author's 'books' array, setting each book's 'author' property
    try author.add(modelObject: books, forKey: "books")
catch MappingError.unknownRelationship(let name) {
    print("Unknown relationship \(name)")

To-One Relationships

An additional method is provided for setting the value of a to-one relationship, as shown here:

// Set the value of a to-one relationship
let book = Book(dictionary: bookDict1, entity: bookEntity)
let pricing = Pricing(dictionary: ["retailPrice": expectedPrice], entity: pricingEntity)
do {
    // Sets the book's 'pricing' property, and sets the pricing's 'book' property
    try book.set(modelObject: pricing, forKey: "pricing")
catch MappingError.unknownRelationship(let name) {
    print("Unknown relationship \(name)")

Next Installment

In Modeling JSON Mappings — Part 2, we’ll take a look under the hood to see how the Modelmatic framework leverages the data model to automate encoding and decoding.

Newsletter Form
  • Where the future is going: sniffing out all of Apple’s clues
    Where the future is going: sniffing out all of Apple’s clues
    See more: Where the future is going: sniffing out all of Apple’s clues
    Small orange arrow symbol
  • Consider the Following – Part 2
    Consider the Following – Part 2
    See more: Consider the Following – Part 2
    Small orange arrow symbol
Abstract graffiti-style artwork with vivid colors and dynamic shapes.
Simple Form

Connect with our team.


An error has occured, please try again.

Try again