Argo 1.0 is out! We wanted to slim down Argo while maintaining usability and give it more power, so we sifted through every line and asked “Is this needed?”, “Is there a better way?”, and “Is this named properly?”. What we’re left with is an all-around better Argo that’s easier to use and debug.
So, what’s new?
Decoded
The biggest change to Argo 1.0 is the introduction of the Decoded<T>
type.
In previous versions of Argo, we’ve relied heavily on Swift’s Optionals to
represent the result of decoding the JSON into a model. .None
meant failure
and .Some(T)
meant success. This worked fine for most cases but broke down if
you wanted more context around why the decoding failed. Users were forced
to always use an Optional
result and could not take advantage of a more complex
custom type like Result
.
We’ve replaced Optional
with a Decoded<T>
type that represents three
possible outcomes of decoding: Success, Type Mismatch, or Missing Key. Success
is the same as .Some
was before, representing a successful decoding. The two
new error types allow us to retain a more detailed reason for failure.
.TypeMismatch
means that the type needed for the model doesn’t match the type
in the JSON. .MissingKey
means that the key requested from the JSON doesn’t
exist or is NULL.
We reworked the JSONDecodable
protocol to have the decode
function return
the new Decoded<T>
type, where T
is the DecodedType
typealias. Let’s take
a look at what decoding a User
used to look like and what it looks like with
Argo 1.0.
Given a User
object represented in JSON like this:
{
"id": 5,
"name": "Gob",
"email": "gob@bananastand.com"
}
Before Argo 1.0, we would implement the decode
function like so:
struct User: JSONDecodable {
static func create(id: Int)(name: String)(email: String) -> User {
return User(id: id, name: name, email: email)
}
static func decode(j: JSONValue) -> User? {
return User.create
<^> j <| "id"
<*> j <| "name"
<*> j <| "email"
}
}
Now with Argo 1.0, we slightly modify the decode
function definition but
everything else is the same:
struct User: Decodable {
static func create(id: Int)(name: String)(email: String) -> User {
return User(id: id, name: name, email: email)
}
static func decode(j: JSON) -> Decoded<User> {
return User.create
<^> j <| "id"
<*> j <| "name"
<*> j <| "email"
}
}
Global decode
To decode JSON into our User
model, we used to do this:
let json: AnyObject? = NSJSONSerialization...
let value: JSONValue? = json.map { JSONValue.parse($0) }
let user: User? = value.flatMap(User.decode)
To make this easier with the new Decoded<T>
type, Argo 1.0 introduces a
global decode
function that will call parse
and the model’s decode
function for you.
let json: AnyObject? = NSJSONSerialization...
let user: User? = json.flatMap(decode)
You can still choose to ignore the Decoded<T>
type and its error info by
typing the result of the expression as a User?
as we did above. If you would
like to keep the error context, then type the result as Decoded<User>
like
so:
let json: AnyObject? = NSJSONSerialization...
let user: Decoded<User> = json.flatMap(decode)
With a Decoded<User>
you can use a switch
statement to handle the different
success or error possibilities. You can also just print the value using
println
and see the error message in the console.
Some Renaming
We’ve also shortened a few names. JSONDecodable
is now just Decodable
and
JSONValue
is just JSON
.
What does it all mean?
The Decoded<T>
type allows you to retain detailed error information of
decoding failures. The global decode
function allows you to ignore the new
Decoded<T>
type if you choose or use it to handle decoding failures. More
interestingly, both of these additions pave the way for you to implement your
own return type from a decode. For instance, you can overload the global
decode
function to return a Result<User>
. This puts power in the user’s
hands to customize Argo to return values that stay consistent with the rest of
the code base.
Go Forth and Decode
Thanks to everyone who helped get Argo to 1.0 through issues and PRs. We
think Argo makes decoding objects from JSON easy and efficient and we’re glad
you think so too! Keep your contributions coming and let’s decode
for a
better tomorrow together!