<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Automatic substitution / removal of default values is very useful when reading or writing a file, respectively, and should be supported by the &lt;Codable&gt; family of protocols and objects:<div class=""><br class=""></div><div class="">• When reading, swapping in a default value for missing or corrupted values makes it so hand-created or third-party-created files don’t have to write every single value to make a valid file, and allows slightly corrupted files to auto-repair (or get close, and let the user fix up any data that needs it after) rather than completely fail to load. (Repairing on read creates a virtuous cycle with user-created files, as the user will get _some_ feedback on her input even if she’s messed up, for example, the type of one of the properties.)</div><div class=""><br class=""></div><div class="">• When writing, providing a default value allows the container to skip keys that don’t contain useful information. This can dramatically reduce file sizes, but I think its other advantages are bigger wins: just like having less source code makes a program easier to debug, having less “data code” makes files easier to work with in every way — they’re easier to see differences in, easier to determine corruption in, easier to edit by hand, and easier to learn from.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">My first pass attempt at adding defaults to Codable looks like this:</div><div class=""><br class=""></div><div class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><span style="color: #ba2da2" class="">public</span> <span style="color: #ba2da2" class="">class</span> ReferencePieceFromModel :&nbsp;<span style="color: #703daa" class="">Codable</span> {</div><div class=""><br class=""></div><div style="margin: 0px; line-height: normal; color: rgb(0, 132, 0); background-color: rgb(255, 255, 255);" class=""><span style="color: #000000" class="">&nbsp; &nbsp;&nbsp;</span>// MARK: properties</div><div class=""><div class=""><span style="background-color: rgb(255, 255, 255); color: rgb(186, 45, 162);" class=""><span style="color: rgb(0, 0, 0);" class="">&nbsp; &nbsp;&nbsp;</span>public</span><span style="background-color: rgb(255, 255, 255);" class="">&nbsp;</span><font color="#ba2da2" style="background-color: rgb(255, 255, 255);" class="">let</font><span style="background-color: rgb(255, 255, 255);" class="">&nbsp;name:&nbsp;</span><span style="background-color: rgb(255, 255, 255); color: rgb(112, 61, 170);" class="">String</span><span style="background-color: rgb(255, 255, 255);" class="">&nbsp;=&nbsp;</span><span style="background-color: rgb(255, 255, 255); color: rgb(209, 47, 27);" class="">""</span></div></div><div class=""><div class=""><span style="background-color: rgb(255, 255, 255); color: rgb(186, 45, 162);" class=""><span style="color: rgb(0, 0, 0);" class="">&nbsp; &nbsp;&nbsp;</span>public</span><span style="background-color: rgb(255, 255, 255);" class="">&nbsp;</span><font color="#ba2da2" style="background-color: rgb(255, 255, 255);" class="">let</font><span style="background-color: rgb(255, 255, 255);" class="">&nbsp;styles:&nbsp;[</span><span style="background-color: rgb(255, 255, 255); color: rgb(112, 61, 170);" class="">String]</span><span style="background-color: rgb(255, 255, 255);" class="">&nbsp;=&nbsp;</span><span style="background-color: rgb(255, 255, 255);" class="">[]</span></div></div><div class=""><span style="background-color: rgb(255, 255, 255); color: rgb(209, 47, 27);" class=""><br class=""></span></div><div class=""><br class=""></div><div class=""><div style="margin: 0px; line-height: normal; color: rgb(0, 132, 0); background-color: rgb(255, 255, 255);" class=""><span style="color: #000000" class="">&nbsp; &nbsp;&nbsp;</span>// MARK: &lt;Codable&gt;</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">public</span> <span style="color: #ba2da2" class="">required</span> <span style="color: #ba2da2" class="">init</span>(from decoder: <span style="color: #703daa" class="">Decoder</span>) <span style="color: #ba2da2" class="">throws</span> {</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">let</span> container = <span style="color: #ba2da2" class="">try</span> decoder.<span style="color: #3e1e81" class="">container</span>(keyedBy: <span style="color: #4f8187" class="">CodingKeys</span>.<span style="color: #ba2da2" class="">self</span>)</div><div style="margin: 0px; line-height: normal; font-family: Helvetica; background-color: rgb(255, 255, 255); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="color: rgb(186, 45, 162);" class="">self</span>.<span style="color: rgb(79, 129, 135);" class="">name</span> = container.<span style="color: rgb(49, 89, 93);" class="">decode</span>(<span style="color: rgb(112, 61, 170);" class="">String</span>.<span style="color: rgb(186, 45, 162);" class="">self</span>, forKey: .<span style="color: rgb(49, 89, 93);" class="">name</span>, defaults: type(of: <span style="color: rgb(186, 45, 162);" class="">self</span>).<span style="color: rgb(79, 129, 135);" class="">defaultsByCodingKey</span>)</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">styles</span> = container.<span style="color: #31595d" class="">decode</span>([<span style="color: #703daa" class="">String</span>].<span style="color: #ba2da2" class="">self</span>, forKey: .<span style="color: #31595d" class="">styles</span>, defaults: type(of: <span style="color: #ba2da2" class="">self</span>).<span style="color: #4f8187" class="">defaultsByCodingKey</span>)</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">public</span> <span style="color: #ba2da2" class="">func</span> encode(to encoder: <span style="color: #703daa" class="">Encoder</span>) <span style="color: #ba2da2" class="">throws</span> {</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">var</span> container = encoder.<span style="color: #3e1e81" class="">container</span>(keyedBy: <span style="color: #4f8187" class="">CodingKeys</span>.<span style="color: #ba2da2" class="">self</span>)</div><div style="margin: 0px; line-height: normal; font-family: Helvetica; background-color: rgb(255, 255, 255); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">try</span> container.<span style="color: #31595d" class="">encode</span>(<span style="color: #4f8187" class="">name</span>, forKey: .<span style="color: #31595d" class="">name</span>, defaults: type(of: <span style="color: #ba2da2" class="">self</span>).<span style="color: #4f8187" class="">defaultsByCodingKey</span>)</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="color: #ba2da2" class="">try</span> container.<span style="color: #31595d" class="">encode</span>(<span style="color: #4f8187" class="">styles</span>, forKey: .<span style="color: #31595d" class="">styles</span>, defaults: type(of: <span style="color: #ba2da2" class="">self</span>).<span style="color: #4f8187" class="">defaultsByCodingKey</span>)</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">private</span> <span style="color: #ba2da2" class="">static</span> <span style="color: #ba2da2" class="">let</span> defaultsByCodingKey: [<span style="color: #4f8187" class="">CodingKeys</span> : <span style="color: #ba2da2" class="">Any</span>] = [</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #31595d" class="">name</span> : <span style="color: #d12f1b" class="">""</span>,</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;.<span style="color: #31595d" class="">styles</span> : [<span style="color: #703daa" class="">String</span>]()</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp; ]</div></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><div style="margin: 0px; line-height: normal; color: rgb(0, 132, 0);" class=""><span style="color: #000000" class="">&nbsp; &nbsp;&nbsp;</span>// MARK: private</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">private</span> <span style="color: #ba2da2" class="">enum</span> CodingKeys : <span style="color: #703daa" class="">String</span>, <span style="color: #703daa" class="">CodingKey</span> {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">case</span> name</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="color: #ba2da2" class="">case</span> styles</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; }</div><div class="">}</div><div class=""><br class=""></div><div class="">With just a couple additions to the Swift libraries:</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; line-height: normal; color: rgb(112, 61, 170);" class=""><span style="color: #ba2da2" class="">extension</span><span style="color: #000000" class=""> </span>KeyedDecodingContainer<span style="color: #000000" class=""> </span><span style="color: #ba2da2" class="">where</span><span style="color: #000000" class=""> Key : Hashable {</span></div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">func</span> decode&lt;T&gt;(<span style="color: #ba2da2" class="">_</span> type: <span style="color: #4f8187" class="">T</span>.Type, forKey key: <span style="color: #703daa" class="">Key</span>, defaults: [<span style="color: #703daa" class="">Key</span> : <span style="color: #ba2da2" class="">Any</span>]) -&gt; <span style="color: #4f8187" class="">T</span> <span style="color: #ba2da2" class="">where</span> <span style="color: #4f8187" class="">T</span> : <span style="color: #703daa" class="">Decodable</span> {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">if</span> <span style="color: #ba2da2" class="">let</span> typedValueOptional = <span style="color: #ba2da2" class="">try</span>? <span style="color: #3e1e81" class="">decodeIfPresent</span>(<span style="color: #4f8187" class="">T</span>.<span style="color: #ba2da2" class="">self</span>, forKey: key), <span style="color: #ba2da2" class="">let</span> typedValue = typedValueOptional {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">return</span> typedValue</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; } <span style="color: #ba2da2" class="">else</span> {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">return</span> defaults[key] <span style="color: #ba2da2" class="">as</span>! <span style="color: #4f8187" class="">T</span></div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal;" class="">}</div><div style="margin: 0px; line-height: normal; font-family: Helvetica; min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; color: rgb(112, 61, 170);" class=""><span style="color: #ba2da2" class="">extension</span><span style="color: #000000" class=""> </span>KeyedEncodingContainer<span style="color: #000000" class=""> </span><span style="color: #ba2da2" class="">where</span><span style="color: #000000" class=""> Key : Hashable {</span></div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">mutating</span> <span style="color: #ba2da2" class="">func</span> encode&lt;T&gt;(<span style="color: #ba2da2" class="">_</span> value: <span style="color: #4f8187" class="">T</span>, forKey key: <span style="color: #703daa" class="">Key</span>, defaults: [<span style="color: #703daa" class="">Key</span> : <span style="color: #ba2da2" class="">Any</span>]) <span style="color: #ba2da2" class="">throws</span> <span style="color: #ba2da2" class="">where</span> <span style="color: #4f8187" class="">T</span> : <span style="color: #703daa" class="">Encodable</span> &amp; <span style="color: #703daa" class="">Equatable</span> {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">if</span> value != (defaults[key] <span style="color: #ba2da2" class="">as</span>! <span style="color: #4f8187" class="">T</span>) {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">try</span> <span style="color: #3e1e81" class="">encode</span>(value, forKey: key)</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal; font-family: Helvetica; min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; <span style="color: #ba2da2" class="">mutating</span> <span style="color: #ba2da2" class="">func</span> encode&lt;T&gt;(<span style="color: #ba2da2" class="">_</span> value: [<span style="color: #4f8187" class="">T</span>], forKey key: <span style="color: #703daa" class="">Key</span>, defaults: [<span style="color: #703daa" class="">Key</span> : <span style="color: #ba2da2" class="">Any</span>]) <span style="color: #ba2da2" class="">throws</span> <span style="color: #ba2da2" class="">where</span> <span style="color: #4f8187" class="">T</span> : <span style="color: #703daa" class="">Encodable</span> &amp; <span style="color: #703daa" class="">Equatable</span> {&nbsp;<span style="color: rgb(0, 132, 0);" class="">// I AM SO SORRY THIS IS ALL I COULD FIGURE OUT TO MAKE [String] WORK!</span></div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">if</span> value != (defaults[key] <span style="color: #ba2da2" class="">as</span>! [<span style="color: #4f8187" class="">T</span>]) {</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ba2da2" class="">try</span> <span style="color: #3e1e81" class="">encode</span>(value, forKey: key)</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; }</div><div style="margin: 0px; line-height: normal;" class="">}</div></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">(Note the horrible hack on&nbsp;KeyedEncodingContainer where I had to special-case arrays of &lt;Equatable&gt;s, I guess because the compiler doesn’t know an array of &lt;Equatable&gt;s is Equatable itself?)</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Problems with this technique I’ve identified are:</div><div class=""><br class=""></div><div class="">⑴ It doesn’t allow one to add defaults without manually writing the init(from:) and encode(to:), ugh.</div><div class="">⑵ The programmer has to add 'type(of:&nbsp;<span style="color: rgb(186, 45, 162);" class="">self</span>).<font color="#4f8187" class="">defaultsByCodingKey</font>’ to every call, ugh.</div><div class=""><br class=""></div></div><div class="">Both of these could possibly be worked around if we could add an optional method to the &lt;Codable&gt; protocol, that would look something like:&nbsp;</div><div class=""><span style="color: rgb(112, 61, 170); background-color: rgb(255, 255, 255);" class=""><br class=""></span></div><div class=""><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">&nbsp; &nbsp;&nbsp;<span style="color: #ba2da2" class="">public&nbsp;</span><span style="color: rgb(186, 45, 162);" class="">static</span>&nbsp;<span style="color: #ba2da2" class="">func</span>&nbsp;default&lt;Key&gt;(keyedBy type: Key.Type, key: Key)&nbsp;-&gt; Any? <span style="color: #ba2da2" class="">where</span> <span style="color: #703daa" class="">Key</span> : <span style="color: #703daa" class="">CodingKey</span></div></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><span style="color: #703daa" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">(the above line isn’t tested and doubtlessly won’t work as typed and has tons of think-os.)</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">This would get called by KeyedEncodingContainers and KeyedDecodingContainers only for keys that are Hashable (which I think is all keys, but you can stick un-keyed sub-things in Keyed containers and obviously those can’t have defaults just for them) and the container would be asked to do the comparison itself, with ‘==‘.&nbsp;</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">Something I haven’t tried to address here is what to do if values are NOT &lt;Equatable&gt; — then of course ‘==‘ won’t work. One approach to this would be to provide a way for the static func above to return ‘Hey, I don’t have anything meaningful for you for this particular property, because it’s not Equatable.’ This could be as simple as returning ‘nil’, which would also be a decent way to say, “This property has no meaningful default” which is also needed.</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">Alternatively, one could imagine adding TWO callbacks in the &lt;Codable&gt; for this kind of case, which are essentially *WAVES HANDS*:</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp;<font color="#ba2da2" class="">public static</font>&nbsp;<span style="color: rgb(186, 45, 162);" class="">func</span>&nbsp;isThisValueTheDefault(<span style="color: rgb(186, 45, 162);" class="">_</span>&nbsp;value: Any, forKey key:&nbsp;<span style="color: rgb(186, 45, 162);" class="">Self</span>.Key)&nbsp;<span style="color: rgb(186, 45, 162);" class="">throws -&gt;&nbsp;</span>Any?</div><div class=""><div style="margin: 0px; line-height: normal;" class=""><div class=""><div style="margin: 0px; line-height: normal;" class="">&nbsp; &nbsp; &nbsp;<span style="color: rgb(186, 45, 162);" class="">public&nbsp;</span><span style="color: rgb(186, 45, 162);" class="">static</span>&nbsp;<span style="color: rgb(186, 45, 162);" class="">func</span>&nbsp;defaultValue&lt;Key&gt;(keyedBy type: Key.Type, key: Key)&nbsp;-&gt;&nbsp;Any?&nbsp;<span style="color: rgb(186, 45, 162);" class="">where</span>&nbsp;<span style="color: rgb(112, 61, 170);" class="">Key</span>&nbsp;:&nbsp;<span style="color: rgb(112, 61, 170);" class="">CodingKey</span></div></div><div class=""><span style="color: rgb(112, 61, 170);" class=""><br class=""></span></div></div></div><div class="">These might also need a 'keyedBy type: Key.Type’ parameter — to be honest I haven’t messed with different key spaces so I’m not sure how they work. Also I’m not the best at generics yet. (At this point I’m not even sure if protocols can contain ‘class’ functions, so maybe none of this would work.)</div></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">Another advantage to the two-method approach (besides not requiring the values to be &lt;&nbsp;Equatable&nbsp;&gt;) is that it allows one to provide defaults for floating values, which can often be changed just by floating-point error by like 0.00000000001 and then end up registering false changes. In the isValueDefault(…) the programmer could implement a comparison with a ‘slop’ so if the encoder were about to write 0.000000000001 and the default were 0 nothing would be written.</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class="">-Wil</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><br class=""></div></body></html>