Serializing enums with associated values

Welcome to this new posts in my series about enums with associated values. In the previous posts we looked at the equatability of enums with associated values. Another problem arises when we want to store enums (maybe as part of the model) on a file or want to send it over a network, let’s say: want to serialize the enum. Although the primitive types as Int and String conform to the NSCoding protocol as well as collections of those types (e.g. Arrays and Dictionaries) enums and structs don’t. And since they are not classes we can also not implement the NSCoding protocol for them. For simple enums we can use its raw values for coding, but that’s not possible for enums with associated values.

Let’s look at the following example:

Again we have our well-known enum “Happiness” and an instance of it. With a primitive type or a collection of primitives we could use the NSKeyedArchiver to create a serialized Data object of the instance, but that doesn’t work for the enum. Funny enough we do not get a compiler error but it fails at runtime:

Error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).

There are a lot of proposals in the community on how to serialize structs (e.g. The Red Queen Coder) and this mechanism also works for enums. The idea is to transform the struct (or the enum in our case) to a dictionary of NSCoding compliant types. This dictionary can be given to an NSCoder (i.e. NSKeyedArchiver). It’s convenient to define a protocol for the necessary methods.

The first method transforms the enum (or struct) to a dictionary and the new init creates it from the supplied one. The implementation of this protocol is not too complicated:

Let’s look at the encoding part first. The method propertyListRepresentation() should transform the enum to a dictionary „PropertyList“ ([String : Any]). This dictionary has at least one entry for the main value. I call it here “happiness” which is an Int and maps to the case in the enum. The possibly present associated values in the enum form additional entries in the dictionary. This dictionary can be supplied to an NSCoder since it consists only of primitive types.

The other part is the decoding of an optionally supplied dictionary and creating the desired enum. Of course, this may fail either because there is no dictionary supplied or the dictionary has not the necessary entries. First we look for the main entry in the dictionary “happiness”. It maps to the desired case of the enum. Depending on the case we have to add the possibly present associated values. That’s it. Simple, isn’t it?

Now we can use these methods to supply the enum to an NSCoder:

Everything’s fine now. We do not get a compiler nor does it fail at runtime. The creation of a new enum from the archive gives the same enum as the originally supplied one.

OK, now we can archive and unarchive enums with associated values quite simply but our enums are still not NSCoding compliant since they are not classes. This is more a matter of taste but in this post from Ryuichi Saito there is a proposal to create a nested NSObject/NSCoding class within the struct (or enum in our case). This class takes care of the coding and decoding of the enum (or struct). Let’s take a look for this in our example:

Here we use our mechanism with the propertyListRepresantation since we do not want to do the job of coding and decoding twice. It’s convenient to put the coded and decoded vars in protocols and extensions:

Now we can test the coding and decoding using the NSCoding compliant part of the enum:

This works as well but its not really shorter than the original approach. For me it’s not worthwhile to make this effort and I’ll stick to the propertyListRepresentation.

Here is the playground of this exercise.


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.