Decodable
Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by Argo, but without a bizillion functional operators.
How does it work?
A protocol
A parse-function
And shameless operator-overloading
The too-many generated overloads, all calling the parse
-function, can be found in Overloads.swift. Return types include T?
, [T?]
, [T?]?
, Any
and [String: T]?
. When conditional protocol conformance is supported in Swift this won't be necessary, and automagic decoding of infinitly nested generic types (like [[[[[[[[[A???]]: B]]]?]]?]]
) would work.
An overload may look like this:
KeyPaths
Keypaths can be created from string and array literals as well as with explicit initializers. They can also be joined using the operators =>
and =>?
. =>?
is another operator that indicates that nil
should be returned if the key to the right is missing.
- When composing
=>
and=>?
operators in the same keypath, the strictness of=>
is still honoured. - Optional key paths (
=>?
) require an optional return type
Errors
Errors will be caught and rethrown in the decoding process to backpropagate metadata, like the JSON object that failed decoding, the key path to it, and the root JSON object.
From DecodingError.swift:
Handling Errors
Expressions like j => "key"
will throw directly, and catch
-statements can be used to create the most complex error handling behaviours. This also means that try?
can be used to return nil if anything goes wrong instead of throwing.
For convenience there is an operator, =>?
, that only returns nil on missing keys, for APIs that indicate null
in that manner, and to aid working with different response formats.
Overload | Null Behaviour | Missing Key Behavior | Type Mismatch Behaviour | Errors in subobjects |
---|---|---|---|---|
=> -> T |
throws | throws | throws | uncaught (throws) |
=> -> T? |
nil | throws | throws | uncaught (throws) |
=>? -> T? |
nil | nil | throws | uncaught (throws) |
try? => -> T |
nil | nil | nil | caught (nil) |
Customization
Int
, Double
,String
, Bool
, Date
(ISO8601), NSArray
, and NSDictionary
types that conform to DynamicDecodable
with the following declaration:
This allows Decodable to implement default decoding closures while allowing you to override them as needed.
Note that when extending new types to conform to Decodable
there is really no point in conforming to DynamicDecodable
since you already control the implementation. Also note that the decoder
properties are intended as "set once". If you need different behaviour on different occations, please create custom decode functions.
The default Date.decoder
uses a ISO8601 date formatter. If you don't want to create your own decode closure there's a helper:
When Decodable
isn't enough
Don't be afraid of not conforming to Decodable
.
Tips
- You can use
Decodable
with classes. Just make sure to either call arequired
initializer on self (e.gself.init
) and returnSelf
, or make your classfinal
. ( This might be a problem though) - The
Decodable
-protocol and the=>
-operator should in no way make you committed to use them everywhere.
Compatibility
Swift version | Compatible tag or branch |
---|---|
Swift 4.0 | 0.6.0 |
Swift 3.0 | v0.5 |
Swift 2.3 | v0.4.4 |
Swift 2.2 | v0.4.3 |
Note on Swift 4.0 usage
Due to collisions with the standard library you will have to import ambiguous symbols specifically, in addition to Decodable
as a whole.
This means you likely want the following
and you can import other symbols, e.g KeyPath
, DecodingError
, in a simlilar fashion (using import struct
and import enum
)