o be defined, conforming to <code>Decodable</code> protocol. Starting with the base class and then the subclass.</p>
<figure id="9d61">
<div>
<div>
<iframe class="gist-iframe" src="/gist/Stoff81/d0633ad7fb6aa2c4b1c8f21b1382fcbc.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="a17c">This is the base class and defines all the attributes associated with it. There is nothing complicated about this, so no custom code needs to be written, it just uses the standard API.</p>
<figure id="d678">
<div>
<div>
<iframe class="gist-iframe" src="/gist/Stoff81/059f5c5b323a7663c3335b7fd2884042.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="a6c1"><code>Beer</code> has the additional attribute of <code>alcohol_content</code> so we need to add that. This class needs to be initialised before the base class. We need to write our own custom init method. You can see on line 11, we pass the same decoder object to the super class so that it can decode the base classes attributes.</p><h2 id="2a26">Serialisation</h2>
<figure id="daf8">
<div>
<div>
<iframe class="gist-iframe" src="/gist/Stoff81/dc9581ee1523698133dc420611c6b216.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="9ac4">This is how we convert the json into memory. Nothing complicated here, but we now need to define the <code>Drinks</code> object. This object contains a heterogenous array so we will need to implement the <code>Decodable</code> protocol ourselves.</p><h2 id="f1ba">Heterogenous Array</h2>
<figure id="aea6">
<div>
<div>
<iframe class="gist-iframe" src="/gist/Stoff81/1a89f149f19cbe564901ca7944bac63a.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="6f28">Line 2 shows that we add an array of <code>Drink</code> objects as a property to this class.</
Options
p><p id="7d03">Next we create an enum <code>DrinksKey</code> which is used to decode the dictionary containing the array of drinks.</p><p id="9079">We also define an enum <code>DrinkTypeKey</code> which is used to serialise the type field for each object in the array.</p><p id="29f2">And a final enum <code>DrinkTypes</code> with specifies all the possible drink types.</p><p id="6448">With all the enums specified we can get to the serialisation.</p><p id="5bac">Line 19: Get the drinks dictionary — <code>container</code></p><p id="ccbd">Line 20: Get the array of drinks — <code>drinkArrayForType</code></p><p id="c547">Line 23: Make a copy of the drinks array — <code>drinksArray</code> This is needed as we be iterating the <code>drinkArrayForType</code> In doing so we will have no way to get the decoder object for each element in the array. <i>See summary on this below.</i></p><p id="082d">Line 24: Iterate the <code>drinkArrayForType</code> container</p><p id="14ff">Line 25: Get the current drink container</p><p id="828b">Line 26: Decode the type attribute</p><p id="3f43">We can now use this decoded attribute to identify which class to decode. We have to use the copied <code>drinksArray</code> object for decoding however, as the iterator of <code>drinksArrayForType</code> has moved onto the next object. Viola!</p><h1 id="297d">Summary</h1><p id="6c5b">This last step was the only way I could get the <code>Decodable</code> API to do my bidding. As far as I can see there is no way to get a decoder from an <code>UnkeyedDecodingContainer</code> or <code>KeyedDecodingContainer</code> This seems like a limitation of the API, but Im sure there are good reasons for it not to be possible. It does seem a little strange to expose that dependency just for this use case of decoding.</p><p id="7d68">If you think otherwise, let me know, it may be worth thinking about it a little further and adding a suggestion for improvement to the Swift API itself. After all this is only the first iteration of the this API, its possible developers may have missed this use case.</p><h1 id="bf19">Full Playground</h1>
<figure id="4fbb">
<div>
<div>
Swift 4.0 Codable — Decoding subclasses, inherited classes, heterogeneous arrays
Background
Swift 4.0 has introduced a really helpful API. Codable If you have been following the Swift project and the new features in this version on the language you have probably already heard about it. I ran into some issues when working with some of our REST APIs so wanted to document them here in case other people run into the same situations.
Getting Started
If you are familar with Codable you can skip this sections, if not this is how Apple summarise is on there docs page:
Make your data types encodable and decodable for compatibility with external representations such as JSON.
There is already quite a lot of good articles out there on how to use this new API. These are the best ones, I would suggest reading them as an entry point:
A heterogeneous array is when you have a piece of JSON which contains multiple different objects. In my case, these objects are subclasses of a base class Drink Each subclass has different attributes which need to be serialised.
Models
Firstly, the models need to be defined, conforming to Decodable protocol. Starting with the base class and then the subclass.
This is the base class and defines all the attributes associated with it. There is nothing complicated about this, so no custom code needs to be written, it just uses the standard API.
Beer has the additional attribute of alcohol_content so we need to add that. This class needs to be initialised before the base class. We need to write our own custom init method. You can see on line 11, we pass the same decoder object to the super class so that it can decode the base classes attributes.
Serialisation
This is how we convert the json into memory. Nothing complicated here, but we now need to define the Drinks object. This object contains a heterogenous array so we will need to implement the Decodable protocol ourselves.
Heterogenous Array
Line 2 shows that we add an array of Drink objects as a property to this class.
Next we create an enum DrinksKey which is used to decode the dictionary containing the array of drinks.
We also define an enum DrinkTypeKey which is used to serialise the type field for each object in the array.
And a final enum DrinkTypes with specifies all the possible drink types.
With all the enums specified we can get to the serialisation.
Line 19: Get the drinks dictionary — container
Line 20: Get the array of drinks — drinkArrayForType
Line 23: Make a copy of the drinks array — drinksArray This is needed as we be iterating the drinkArrayForType In doing so we will have no way to get the decoder object for each element in the array. See summary on this below.
Line 24: Iterate the drinkArrayForType container
Line 25: Get the current drink container
Line 26: Decode the type attribute
We can now use this decoded attribute to identify which class to decode. We have to use the copied drinksArray object for decoding however, as the iterator of drinksArrayForType has moved onto the next object. Viola!
Summary
This last step was the only way I could get the Decodable API to do my bidding. As far as I can see there is no way to get a decoder from an UnkeyedDecodingContainer or KeyedDecodingContainer This seems like a limitation of the API, but Im sure there are good reasons for it not to be possible. It does seem a little strange to expose that dependency just for this use case of decoding.
If you think otherwise, let me know, it may be worth thinking about it a little further and adding a suggestion for improvement to the Swift API itself. After all this is only the first iteration of the this API, its possible developers may have missed this use case.