Skip to content

Custom Types

Brian Mullen edited this page Dec 4, 2016 · 3 revisions

There are 3 main ways (Value, Deserializable and IndexDeserializable) to create a custom type to be used in data extraction.

All the examples will use the following struct:

struct User {
     var id: Int
     var name: String
     var email: String
}

Value protocol

This is the main protocol to be used for allowing your custom type to be extracted.

extension User: Value {
    static func value(from object: Any) throws -> User {
        let id: Int
        let name: String
        let email: String
        
        // values are in a Dictionary
        if let data = object as? Extractable {
            id = try data.value(for: "id")
            name = try data.value(for: "name")
            email = try data.value(for: "email")
        }
        // values are in an Array
        else if let data = object as? IndexExtractable {
            id = try data.value(for: 0)
            name = try data.value(for: 1)
            email = try data.value(for: 2)
        }
        else {
            let expectedType = "Extractable or IndexExtractable"
            throw OutlawError.typeMismatch(expected: expectedType, actual: type(of: object))
        }
        
        return User(id: id, name: name, email: email)
    }
}

Deserializable protocol

This protocol was designed to make life easier by supplying an init(object:) initializer instead of creating a static value(from:) method.

extension User: Deserializable {
    init(object data: Extractable) throws {
        id = try data.value(for: "id")
        name = try data.value(for: "name")
        email = try data.value(for: "email")
    }
}

Besides using an initializer, you will notice a few more reductions in the amount of code you have to write. First, you don't need to make sure the data coming into your initializer is a type that you can extract data from. Second, the properties are already typed and so you don't need to specify what the types are of each property.

IndexDeserializable protocol

This protocol is identical to Deserializable except that your data comes from an IndexExtractable object (i.e. Array) instead of an Extractable object (i.e. Dictionary).

extension User: IndexDeserializable {
    init(object data: IndexExtractable) throws {
        id = try data.value(for: 0)
        name = try data.value(for: 1)
        email = try data.value(for: 2)
    }
}

Which one should you use?

That answer depends on your preference and the structure of your data. Which ever one you decide to pick, you can now extract your custom data type.

If you choose to use Value or Deserializable, you can extract your data like so:

let users: [User] = try json.value(for: "users")

If you choose to use Value or IndexDeserializable, you can extract your data like so:

let users: [User] = try json.value(for: 0)

Clone this wiki locally