I'm trying to encode an object and i have some troubles. It work's fine with strings, booleans and else, but i don't know how to use it for enum. I need to encode this:
enum Creature: Equatable {
enum UnicornColor {
case yellow, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
I'm using this code for encode:
func saveFavCreature(creature: Dream.Creature) {
let filename = NSHomeDirectory().appending("/Documents/favCreature.bin")
NSKeyedArchiver.archiveRootObject(creature, toFile: filename)
}
func loadFavCreature() -> Dream.Creature {
let filename = NSHomeDirectory().appending("/Documents/favCreature.bin")
let unarchived = NSKeyedUnarchiver.unarchiveObject(withFile: filename)
return unarchived! as! Dream.Creature
}
Here is required functions (model.favoriteCreature == Dream.Creature)
override func encode(with aCoder: NSCoder) {
aCoder.encode(model.favoriteCreature, forKey: "FavoriteCreature")
aCoder.encode(String(), forKey: "CreatureName")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let favoriteCreature = aDecoder.decodeObject(forKey: "FavoriteCreature")
let name = aDecoder.decodeObject(forKey: "CreatureName")
}
It works fine with "name", i think the problem is in aCoder.encode() coz i don't know what type to write there. I get next error when run: -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
I read some advices in comments and can assume that i have no rawValues in enum Creature, i made raw type "String" in that enum:
enum Creature: String, Equatable {
enum UnicornColor {
case yellow, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
Now i have this error: Enum with raw type cannot have cases with arguments. Also i read that associated values and raw values can't coexist. Maybe there is some other way to archive enum without raw values?
Hope someone can help me, thank's
The main problem for your issue is that you cannot pass Swift enums to
encode(_:forKey:)
.This article shown by Paulw11 will help you solve this part. If the enum can easily have
rawValue
, it's not too difficult.But, as you see, Enum with raw type cannot have cases with arguments.
Simple enums can easily have
rawValue
like this:But enums with associate values, cannot have
rawValue
in this way. You may need to manage by yourself.For example, with having inner enum's
rawValue
asInt
:You can write an extension for
Dream.Creature
as:(In fact, it is not an actual
rawValue
and you'd better rename it for a more appropriate name.)With a definition like shown above, you can utilize the code shown in the link above.
You are dealing with a problem that arises because Swift native features don't always play well with Objective-C.
NSCoding
has its roots in the Objective-C world, and Objective-C doesn't know anything about Swift Enums, so you can't simply archive an Enum.Normally, you could just encode/decode the enumeration using raw values, but as you found, you can't combine associated types and raw values in a Swift enumeration.
Unfortunately this means that you will need to build your own 'raw' value methods and handle the cases explicitly in the
Creature
enumeration:You can then encode/decode like this:
Testing gives:
Personally, I would make
Creature
a class and use inheritance to deal with the variation between unicorns and other typesTo simplify the coding/decoding you could provide an initializer for Creature requiring a
Data
and a computed property nameddata
. As Creature changes or as new associated values are added, the interface toNSCoding
does not change.A serialization of Creature into and out of
Data
could be accomplished like this.