In this tutorial I will move from the simplest way of saving data to a file to a sustainable solution using Apple’s Codable protocol. There will be no substantial handling of errors.

0) Setup:

Drag a text field (@IBOutlet weak var inputFld: NSTextField!), a label ( @IBOutlet weak var outputLabel: NSTextField!) and two buttons into the storyboard. Label the buttons ‘Save’ and ‘Load’, and create actions for them. I’ll give the full code for both of them, as it contains the necessary setup for the NSOpenPanel and NSSavePanel. (You can read about those classes in greater depth in this post, which also explains the origin of the .unicorn extension (abused here: if you want to use your own file types, please register your UTI with the system)).

Either turn Sandboxing off, or set Target: Capabilities: user selected file to read/write.

and add the following to the save and load functions:
//1) Saving a string to file
saveString(to: panelResult)

//1) Loading a string from file
loadString(from: panelResult)

NSCoding

NSCoding

2) So far, so good. We have fully functioning open and save panels, which allow users full control over where they want to save files; and we have a robust framework for loading and saving, even though all we do with errors is print them to console.

We now make a brief detour into the land of NSCoding.

Until Summer 2017, the NSCoding protocol was responsible for encoding and decoding your objects. And I’m meaning ‘objects’ in the narrowest possible sense: in order to conform to NSCoding, your class had to inherit from NSObject. This created a certain amount of friction: on the one hand, you are encouraged to use value types (structs) and enums in your code… and then you need to provide initialisers for these that are based on Foundation types, so you can save your size: .small property as “small”, pass that string to your initialiser, and create a .small value from it.

(NSCoding has a long list of ‘encodeX/decodeX’ methods, so there’s a little more to watch out for. For a good article on the topic, see http://nshipster.com/nscoding/ I’m borrowing a LOT of their syntax, because it’s so elegant. Making the required coder init a _convenience_ one solved a lot of awkwardness.)

2b) Comment out the string-saving/loading functions, and add the following two calls:

//2) NSCoding saveCodedUnicorn(to: panelResult)

// 2) NSCodingloadCodedUnicorn(from: panelResult)

and create a variable and the two functions:

var myUnicorn = UnicornObject(text: "This is an old-fashioned unicorn. It conforms to NSCoding.")

(it is far more common to have arrays or other collections as root objects, but I wanted the simplest possible case here.)

Codable

Now with Codable

3) Let’s play that again, with Codable:

3a)struct ModernUnicorn: Codable {

var text: String = "I am the model of a codable unicorn."

}

That looks great! I love it. So little boilerplate! So easy to remember!

… and then I fell into the rabbit hole of ‘ok, I’ve got codable items, but what do I actually DO with them?’

If the answer is ‘write to JSON’, you’re in luck. You can find lots of tutorials for JSON. What you cannot find – at least, what I could not find between June 2017 and February 2018 – is one single snippet of code by Apple that shows you how to write Codable to a file. (The Archives and Serializations Programming Guide, last updated 17/2/2012, does not mention Codable)

3b) We can try our luck with NSKeyedArchiver and NSKeyedUnarchiver. Spoiler: the archiveRootObject and unarchiveObject(withFile:) methods don’t work with Codable.

3c) This article and the gist linked here go down the route of NSKeyedArchiver using encodeEncodable(forKey:) respectively NSKeyedUnarchiver.decodeTopLevelDecodable(forKey:)

From their signatures (and the fact that they throw with Swift 3+ syntax), we can conclude that these are meant to be used with Codable, hence:

Yeah. That. This may be _safe_, but I’ll admit that on my own, I struggled to work out which methods I should be using, and I don’t think I would ever have found NSKeyedArchiveRootObjectKey on my own. To give its definition:

Global VariableNSKeyedArchiveRootObjectKey
Archives created using the class method archivedData(withRootObject:) use this key for the root object in the hierarchy of encoded objects. The NSKeyedUnarchiver class method unarchiveObject(with:) looks for this root key as well.

I admit to a certain amount of bafflement here: on the one hand, it _looks_ as if the usage above is the correct counterpart to archivedData(withRootObject); on the other hand, you can – at least, experimentally I could – use any String to archieve the same effect, and using a known global variable seems like a convenience. There’s nothing in the documentation to say that it will be treated differently, so I’m going purely on gut feeling.

I wanted to get this out of the way. The ability to create files using the new Codable protocol was one that I tried to achieve ever since I saw the WWDC video introducting it (around 23:30), but since I’m halfway down this rabbit hole and my current app is at a point where I’d like a solid saving solution, I want to look at serialization/deserialization a bit more. I’m lucky that I found an article that gave me most of the code I needed, and reading the documentation I am fairly confident that this is the right way to go about it. But for me, the question ‘how do I do x’ is only partly answered by ‘here’s some code’ – I’m a gestalt thinker, I want to know how I could have worked this out myself. And since I didn’t, I want to at least retread the steps, so that the next time I face a complex, badly-documented problem, I will be able to work things out better.

From this angle the implementation was relatively trivial. I’ve taken the implementation of NSCoding, providing information a coder needs to know to encode/decode our objects and combine it with setting up NSKeyedArchiver (an NSCoder subclass) as the starting point; and looked for the Codable equivalent, staying as close to the original as possible. Using codable means that you do not need to write the encoding and init(coder:) instructions, which left finding out how NSKeyedArchiver handles Codable objects as opposed to NSCoding-compliant ones. Then it’s a matter of syntax, and do-try-catch blocks, and you have the solution above. Plus NSKeyedArchiveRootObjectKey, which is mentioned in the NSKeyedArchiver documentation, but – see quoted definition above – has no obvious link to Codable and the Codable-related methods encodeEncodable(forKey:) and decodeTopLevelDecodable(forKey:).

At this point, however, I ran into several questions, which in turn led to the realisation that I could not, as things stand, actually save my app, because I rely heavily on protocols, and you cannot encode an array of protocol-compliant objects. If all the compiler knows about objects – needs to know about objects – is that they have certain properties/methods, which class do you tell to instantiate an object with the current chunk of data? And thus I halfheartedly poked at the question, found it a little too intense, and decided to delve deeper into the Codable protocol and its uses in order to avoid having to wrap my head around two complex things at once.

[/expand]

4) Verbose Codable

Codable with boilerplate written out

The amount of boilerplate necessary to write to use Codable instead of NSCoding in out trivial example is about equal; but as long as you don’t need to do any customisation, objects with more properties will win out under Codable. But it’s useful to look at what’s under the hood to allow more customisation.

Let’s create a VerboseUnicorn struct, the corresponding var verboseUnicorn = VerboseUnicorn(), and change all references to ModernUnicorns to VerboseUnicorn.

struct VerboseUnicorn: Codable {
var text: String = "Too many words."

init(){
}
}

4a) Coding Keys

Even with NSCoding, it’s good practice to use an enum to provide coding keys instead of having to type strings: if I type ‘text’ in my encoding method, and ‘unicornText’ in init(coder:), I create a problem. A CodingKeys enum would give us code completion; problem solved.

add

enum CodingKeys: String, CodingKey {
case text = "unicornText"
}

to your VerboseUnicorn class. CodingKeys can be either String or Int; for most cases – particularly if you choose an output format like JSON that you can examine manually to see whether your save has been successful – Strings render the output more readable.

The most common use for Coding keys was JSON encoding: JSON frequently uses snake_case for property names (lowercase words separated by underscores , while Swift prefers camelCase (names closed up with subsequent nouns starting with capitals). This is, in fact, so common that in Swift 4.1 JSONEncoder now has a .convertToSnakeCase decoding strategy, with a corresponding .convertFromSnakeCase for JSONDecoder.

(I had to type both of these out using the signatures in the Encodable/Decodable protocol documentation.)

Build and run. The code from Section 3 – now amended for Verbose Unicorns – should work exactly the same.
You need to include all of the keys you want to encode in the CodingKeys enum; but if you have properties you do not wish to save – for instance, because you will be updating them with a network call – you can simply leave them out of the CodingKeys enum, even if you rely on the compiler’s implementation of Codable/Decodable..

4c) The first curious thing about the code above is the mentioning of Encoder/Decoder protocols. (For the sake of brevity, whereever the text below applies to both Encoders and Decoders, I shall simply go with Encoders only.)

There’s a hole the in the documentation.
Apple’s article on encoding and decoding custom types uses a ‘Landmark’ struct as an example, and says Adopting Codable on your own types enables you to serialize them to and from any of the built-in data formats, and any formats provided by custom encoders and decoders. For example, the Landmark structure can be encoded using both the PropertyListEncoder and JSONEncoder classes, even though Landmark itself contains no code to specifically handle property lists or JSON.

I have looked long and hard in the documentation, and I have not been able to find any evidence that NSKeyedArchiver (or its superclass, NSCoder) conforms to the Encoder protocol; yet the code given in this section undoubtedly works, and personally I am convinced that the presence of encodeEncodable means it SHOULD be used as I’m using it here.

The Swift Evolution Proposal SE-0167 confirms that suspicion, but that’s a late update to this post; it’s not a location where I would have looked for information.

Setting a breakpoint reveals something even more intriguing:

_PlistEncoder sounds like a private class, and that’s as far as I can go with this particular puzzle. Unsurprisingly, the Decoder object passed to our init(from decoder:) method is a _PlistDecoder.

As I said above, even though the step of replacing NSCoding with Codable and choosing the appropriate methods on NSKeyedArchiver is a logical step, looking at the documentation only and trying to figure out Codable from that meant that I looked at the two available Encoders and boggled that Apple would create a serialisation solution that did not contain an obvious path for saving to file. (I continue to be boggled by the fact that this is NOT DOCUMENTED and that there is no working code example for it. I mean… No.

throw(Error.OutOfWords)

The existence of a JSONEncoder class as well as a PropertyListEncoder makes more sense when we go back to NSCoding. There is a NSPropertyListSerialization class, which converts data to and from property lists, as well as a NSJSONSerialization class.

Until I looked at NSCoding again for this article, I had not heard of either, but now the parallels are even clearer: it appears that the PropertyListEncoder and JSONEncoder replace these two classes specifically, while NSKeyedArchiver continues to do its thing as before. (Would it have been better to provide a FileEncoder? In my opinion, yes. In the absence of one, mention NSKeyedArchiver, dammit.)

The _PlistEncoder made me worry a little about the integrity of files created with NSKeyedArchiver, though: I will adress this (and other) questions later in this post.

4d) So let’s leave NSKeyedArchiver for a bit, and look at the Encoders/Decoders that have been created specifically to work with the Codable protocol.

Instead of a JSONEncoder, you can use a PropertyListEncoder (without the prettyPrinted text). This one turns out to be a PropertyListEncoder, as opposed to the _PlistEncoder used by NSKeyedArchiver. Once you change its extension from .unicorn to .plist in the Finder, it becomes a perfectly well-behaved PropertyList.

Containers

Containers: more finely grained than NSCoding

4e) There are ‘containers’ in the code above. Also, while in NSCoding we need to specify what we decode via a method on NSCoder (decodeObject as? String; decodeBool etc), here the ‘container’ is taking over that responsibility. (There are separate methods for decode and decodeIfPresent, which is a nice way of handling optionals. At least I think so – this article by Ben Scheirman disagrees and suggests encoding all values. [It also has a number of useful JSON examples explaining why you would want to use certain features of Codable and Encoders: if the JSON data structure looks like _this_, you transform it into Swift objects like _that_.])

Containers come in three flavours: keyed, unkeyed, and singleValue; they also can be nested. (If you have a type nested within another, you use nested containers).

A keyed container encodes a dictionary – or a Swift object, which one can conceptualize as a dictionary (where the property names are the keys and the property values the values.) An unkeyed container contains an ordered sequence – in other words, an array of objects, while a single value is, well, just that. So unless I am misunderstanding containers, the compiler automatically nests containers as needed – but if you want to make adjustments, you need to provide the containers yourself.

In other words, in order to do custom encoding you create a nested container of the appropriate type and ask it to encode your objects.

4f) Rather than duplicating all of the work Ben Scheirman has done, I simply point you to that article again: He also resolves the question of how to encode classes which inherit from others (there is a container.superEncoder involved).

This is somewhat surprising. If you turn the ModernUnicorn into a class, and create a PostmodernUnicorn class inheriting from Modern Unicorn, you would expect

to just work: after all, ModernUnicorn conforms to Codable and can easily be encoded.

PostmodernUnicorn, on the other hand, fails with Thread 1: EXC_BAD_ACCESS (code=1, address=0x10) which would have sent me into a complete and utter rabbit hole of despair had I encountered this in the process of serialising an app: which bit of the code did I get wrong? (Answer: none, this is the process that works perfectly fine with ModernUnicorn) If I hadn’t encountered the warning first, it would not have occurred to me that Codable does not support inheritance, because inheritance was the foundation of Objective-C programming, and still plays a big role in Swift. [*]

[*] Objective-C had one [well, functionally had one] root base class: NSObject. In Swift, you can have as many as you like. In Objective-C, to model subtle differences between objects, you’d create a class and subclass it; in Swift, you can use protocol composition. Both have extensions/categories; Swift uses them a lot more. Swift has composition baked in conceptually. There’s still an awful lot of inheritance going on.

Enums

The case of enums

5) Let’s talk about enums for a moment. This is a *wonderful* moment, since previously you could not encode enums. It turns out that enums are trivial to encode if they are of type String or Int (instead of you encoding a string for an enum value and constructing an enum value from a string, the compiler now does this for you).

If your enum contains an associated value, it’s not quite as easy, but the principle is straightforward enough:

You will need the verbosity of VerboseUnicorn (point 3 above): a CodingKeys enum, and both encode(encoder:) and init(decoder:); and I’ve added an error enum as well. Be aware that you cannot simply copy and paste this code: both encoder and decoder have too many methods, and you need to pick the right one (String.Type, Encodable.protocol) etc. (In Xcode 9.2, the compiler complained otherwise about ambiguity, so it’s safer to type your method.)

You basically tell the encoder what to encode for each case of the CodingKeys, and vice versa.

6) Open questions

Open questions

6a) The first question that came to my mind was that of conditional objects and object graph handling. NSKeyArchiver/NSKeyUnarchiver’s concept of RootObjects deals with this; but the encodeEncodable function makes no mentioning of this, so the question is whether it retains that behaviour. As for conditional objects (to avoid retain cycles) – they’re a big question mark right now. As far as I can make out, Codable encoding means that there is no provision at all for encoding ‘this is a reference to an object we’ve already seen’ – I tried to encode the same unicorn 250 times, and ended up with 249 unicorns too many.

This is not satisfying, and I have no solution for this at all, only the workaround of leaning in much harder into Swift value types and dependency injection: if your object only exists at an entry point, and you propagate it after loading, passing it on when needed and then disregarding it, you can still encode your model using Codable. Most of this – using structs and enums and an architecture that simply does not keep shared objects around for longer than necessary – is good practice, but with the current limitation on Codable, it becomes much more important.

There is one conditional method, not on NSKeyedArchiver, but on Container:container.encodeConditional(object: Encodable, forKey:CodingKeys)

so you can at least conditionally encode any object properties, but ‘array with multiple references to the same object’ does not appear to be conditionally encodable.

I still think that ‘good coding practice, passing objects from a single entry point, creating temporary references that are discarded’ (you create a new temporary DetailViewController with the data you want to manipulate, you change your data, get it back to your DataManager, and the next time you need a DetailViewController you create a new instance) is good practice; it feels very comfortable using this pattern in Swift.

6b) What happens to the NSSecureCoding protocol in our new shiny Codable world? Are the concerns raised in the NSSecureCoding documentation still valid? Looking at the full signature of@nonobjc func decodeTopLevelDecodable(_ type: T.Type, forKey key: String) throws -> T? where T : Decodable

I would guess not, because rather than casting the result to a class (or protocol), MyClass.Self is integrated into the decoding function, just as it is with NSSecureCoding.
But that’s a guess, and don’t take my gut feeling as gospel; the real answer is that I do not know. For now – since all I want to do is some quick saving-and-loading of data while I build my app, which eventually probably will be using CoreData anyway – I would be comfortable using Codable in the manner described here.

6c) I would be comfortable using Codable … but can I actually do this? My App heavily relies on protocols rather than inheritance, which means that my root object will be [MyProtocol] with several classes conforming to MyProtocol being present.

So let’s create a Protocol – HasStringColour, a Kitten class, and make both Kittens and Unicorns conform to HasStringColour.
(right now, we’re playing with a String because Strings are easy to encode. Making enums conform to Codable is a little more hassle, so I skip that step here.)

(I’m adding the text property to make my life easier; otherwise I’d have to check whether the object I handle is of one of the classes I’m interested in, or I’d need a separate ‘hasText’ protocol. So we’re fudging things a little here. This is only a proof of concept anyway.)

Thread 1: Fatal error: Array does not conform to Encodable because HasStringColour does not conform to Encodable.

This should not be a great suprise. Encoding an array containing a protocol type is not a trivial problem – somewhere, somehow, we need to encode what *kind* of object each object is, so the decoder knows which initialiser to call with that particular chunk of data. You need, in other words, to save the metadata along with each object. In the case of uniform collections, you do that in the type information of your top-level object: if you encode an array of ModernUnicorns, can decode them as [ModernUnicorn].self, and all is well. The whole point of using protocol collections is that the compiler has no idea whether the object is a ModernUnicorn, a Kitten, or something else: it only knows that it has a text and a stringColour property.

This Stackoverflow question deals with a similar problem (only for JSON encoding); it involves a wrapper around the protocol types which carries the relevant metadata.

It’s a working solution, although I found it just a little too complex to wrap my head around it easily. We do, however, have a Swift native type that is made for branching solutions: enums. If you want to have an array that contains either unicorns or kittens, I would opt for an array of [RainbowCreator] – you can create a case for every class that conforms to your protocol, and then you will need to handle all of them in switch self. (See VerboseUnicorn for the code.)

Another solution (which I shall not write out here) would be to create a new collection type that stores unicorns and kittens in separate arrays (and encodes both of these) but exposes a [HasStringColour] array. Such a ProtocolCollection would need the appropriate accessors, and might be useful if you want to display your objects in TableViews or CollectionViews sorted by section.

And more…

Usage Example

Saving data. Particularly if you have a modern Cocoa app with structs and enums.

Alternatives

You can stick with tried-and-trusted NSCoding; depending on the complexity of your model, and considering that there is no easy way to transform one into the other, adopting Codable might not be an easy task. Or you can go with CoreData, or look for a third party solution (React, Server-side Swift). I have no idea how well those technologies would answer the questions asked above.

Extensions

One of the next posts on my list is the creation of filewrappers with Codable since filewrappers offer many additional features.