I'm trying to make my class conform to NSCoding
, but running into problems because one of its properties is an enum, like this:
enum Direction {
case north
case south
}
If enums were codable I could do it like this:
class MyClass: NSObject, NSCoding {
var direction: Direction!
required init(coder aDecoder: NSCoder) {
direction = aDecoder.decodeObject(forKey: "direction") as! Direction
}
func encode(with aCoder: NSCoder) {
aCoder.encode(direction, forKey: "direction")
}
}
but enums aren't codable so encode()
throws an error.
The answers to "How do I encode enum using NSCoder in swift?" suggest encoding the enum's rawValue
and then initializing it from rawValue
on the other end. But in this case, Direction
doesn't have a rawValue
!
It does have a hashValue
, which seems promising. I can encode its hashValue
without a problem, and decode back to an Int
on the other end. But there doesn't seem to be a way to initialize an enum from its hashValue
, so I can't turn it back into a Direction
.
How can I encode and decode a valueless enum?
New in Swift 4, an enum is encodable. However, it must have a raw value. You can easily do with no additional code, however:
To make your Codable enum work with an NSCoding class MyClass, implement your
init(coder:)
andencode(with:)
like this:You can define some keys and store them instead.
It's a bit tricky way. You should always look after the library with
Direction
is part of. And add keys for new directions.One option is to make the enum conform to
Codable
. This could be through whichever mechanism you want (e.g. use a raw value such as Int or String, or implement the methods).So lets assume we have the following:
In the required methods for
NSCoding
, you can then do the following:So, this works, because
NSKeyedArchiver
andNSKeyedUnarchiver
are aware ofCodable
, butNSCoder
is not. However, given thatNSArchiver
andNSUnarchiver
are now deprecated, and essentially, non-keyed archiving is strongly discouraged, so for your project, it is safe to usefatalError()
in this manner - assuming that you test your code.I think adding a raw value to the enum here is the solution with the least code and is the most maintainable. So if you can modify the enum, add a raw value.
Now let's assume you can't modify the enum. You still can do this in a few ways.
The first one, which I think is quite ugly, is to add an
extension
of the enum and add a static method like this:To convert
Direction
to a codeable value, useString(describing:)
to convert the enum to a string. To convert a string back to an enum, just use the method above.The second one, slightly better, but still not as good as just adding a raw value.
You use a dictionary:
To convert
Direction
to a codeable value, useString(describing:)
to convert the enum to a string. To convert a string back to an enum, just access the dictionary.With Swift 5, if your enum
Direction
can have raw values, you can useCodable
(Decodable
andEncodable
protocols) as an alternative toNSCoding
:Usage: