Active Filters: JSON

Topics

#86: Decodable 🔩

Topics

We continue our look at frameworks that map JSON into model types today with Decodable by Johannes Lund. Decodable is another fantastic solution for this task. It takes advantage of Swift 2's new error handling functionality, and unlike ObjectMapper (covered in Bite #84), the properties on your models don't need to be optionals. Let's take a closer look:

To get things wired up we just need to implement the Decodable protocol on our model types, like so:

struct Spaceship {
  var captain: User
  var topSpeed: Double
}

extension Spaceship: Decodable {
  static func decode(j: AnyObject) throws -> Spaceship {
    return try Spaceship(
      captain: j => "captain",
      topSpeed: j => "topSpeed"
    )
  }
}

Then we can convert from JSON to one of these structs like this:

do {
  let ship = try Spaceship.decode(json)
} catch let error { print(error) }

We can decode JSON arrays like this:

do {
  let ships = try [Spaceship].decode(array)
} catch let error { print(error) }

Decodable also handles nested types and is quite flexible. For example, we aren't even forced to use Decodable on every type. Here we bypass Decodable a bit and instantiate a value for our rank property manually:

extension User: Decodable {
  static func decode(j: AnyObject) throws -> User {
    return try User(name: j => "name", rank: Rank(name: j => "rank"))
  }
}

Some other noteworth Decodable features are its wonderful printable errors, and how easy it is to add custom decode functions for things like parsing custom date formats, etc.

More info about Decodable can be found at git.io/decodable

Topics

#84: ObjectMapper 📲

Topics

There are plenty (no really, plenty) of options when it comes to parsing JSON into model objects on iOS. We'll be taking a look at some of them from time to time over the coming weeks. First up is ObjectMapper by Hearst. Let's take a look how it works with a fictional set of types:

With ObjectMapper, we implement the Mappable protocol on our types to support converting to and from JSON:

struct Spaceship: Mappable {
  var captain: User?
  var topSpeed: Double?

  init?(_ map: Map) { }

  mutating func mapping(map: Map) {
    captain  <- map["captain"]
    topSpeed <- map["topSpeed"]
  }
}

struct User: Mappable {
  var name: String?
  var rank: Int?

  init?(_ map: Map) { }

  mutating func mapping(map: Map) {
    name <- map["name"]
    rank  <- map["rank"]
  }
}

Then we can convert from JSON to one of these structs like this:

let ship = Mapper<Spaceship>().map(JSONString)

And go the other way (from a struct back to JSON) like this:

let JSONString = Mapper().toJSONString(ship, prettyPrint: true)

ObjectMapper can easily handle nested objects, here on our Spaceship model, we've got an optional User property for the captain of the ship.

It also supports subclasses and custom transforms when serializing/deserializing properties. One of the best things about ObjectMapper are the extensions available for other great iOS libraries like Alamofire and Realm (covered in Bite #49). Here's AlamofireObjectMapper in action:

Alamofire.request(.GET, "https://api.jeditemple.com/ships/31", parameters: nil)
  .responseObject { (response: Spaceship?, error: ErrorType?) in
    print(response?.topSpeed)
  }

ObjectMapper stands out in the simplicity of it's API and the ease at which we can jump in and start using it.

More info about ObjectMapper can be found at git.io/objectmapper