sundell.co | blog

Hi, I'm John! I'm a lead mobile developer at Spotify and an indie game developer.

This is my personal blog that's all about the Swift programming language.

Unbox 1.1

2015-11-19

Today I’m excited to release the first significant update to Unbox - version 1.1, which introduces two major new features.

UnboxableWithContext

Sometimes you need additional information - or a context - than what’s contained in the JSON dictionary that is being unboxed. So since its early days, Unbox has had support for passing an optional context object into the unboxing process - for this exact purpose.

However, since this object was always optional (and of type AnyObject), you could never really rely on it - and since one of the core design philosophies of Unbox is that you never should have to manually exit with return nil or throw out of an initializer - I wanted an API that let you establish up-front that a type requires a certain context to be unboxed.

So in Unbox 1.1; I’m inroducing a new protocol called UnboxableWithContext, which lets you specify a strongly typed context type that needs to be there in order to perform any unboxing.

This is super useful when you have depencenies between models - and don’t want to duplicate information in your JSON.

UnboxableEnum

In order to make enums first class citizens in the unboxing process, you can now make your enum types conform to UnboxableEnum. You only have to implement a unboxFallbackValue that gets used as a dummy value in case any unboxing failed.

Once you’ve done so, you can now simply use the normal unboxer.unbox(key) API to unbox your enum.

OK, let’s take these new features for a spin!

In the following example we’ll unbox a Person model, that we’ll then use as the owner of a House and the driver of a Car (without duplicating the Person data in the JSON for the House or Car). We’ll also define a Color enum that we’ll enable for direct unboxing.

/// Person is a regular Unboxable model
struct Person: Unboxable {
    let name: String
    let age: Int
    
    init(unboxer: Unboxer) {
        self.name = unboxer.unbox("name")
        self.age = unboxer.unbox("age")
    }
}

/// By making Color conform to UnboxableEnum, it can now be unboxed directly
enum Color: Int, UnboxableEnum {
    case Red
    case Blue
    
    static func unboxFallbackValue() -> Color {
        return .Red
    }
}

/// House takes a person as its context, and unboxes its color directly
struct House: UnboxableWithContext {
    let color: Color
    let owner: Person
    
    init(unboxer: Unboxer, context: Person) {
        self.color = unboxer.unbox("color")
        self.owner = context
    }
}

/// Car takes a person as its context, and unboxes its color directly
struct Car: UnboxableWithContext {
    let color: Color
    let driver: Person
    
    init(unboxer: Unboxer, context: Person) {
        self.color = unboxer.unbox("color")
        self.driver = context
    }
}

We can now unbox a Person model from the following JSON:

{
    "name": "John",
    "age": 28
}

And then a Car or House model from this super small JSON:

{
    "color": 1
}

Finally, let’s initialize our instances:

let person: Person = try UnboxOrThrow(driverJSON)
let car: Car = try UnboxOrThrow(carJSON, context: person)
let house: House = try UnboxOrThrow(houseJSON, context: person)

Hope these new features makes your JSON decoding code even simpler and more predictable!

Unbox on GitHub