1

To use NSCoding with Swift's Enum type I made an extension on NSCoder:

extension NSCoder {
    func encodeEnum<Enum: RawRepresentable where Enum.RawValue == String>(value: Enum, forKey key: String) {
        self.encodeObject(value.rawValue, forKey: key)
    }

    func decodeEnumForKey<Enum: RawRepresentable where Enum.RawValue == String>(key: String) -> Enum? {
        guard let returnValue = self.decodeObjectForKey(key) as? String else { return nil }
        return Enum(rawValue: returnValue)
    }
}

The encodeEnum method works fine for a String-backed Enum, but when I try to decode the prior encoded Enum like so:

enum MyEnum: String { case Something, Other }
class MyEnumClass: NSObject, NSCoding {
    let myEnum: MyEnum

    init(myEnum: MyEnum) {
        self.myEnum = myEnum
    }

    required convenience init?(coder aDecoder: NSCoder) {
        guard let tmp = aDecoder.decodeEnumForKey("myKey") as? MyEnum else { return nil }

        self.init(myEnum: tmp)
    }
}

I get an error on aDecoder.decodeEnumForKey("myKey"):

Value of type `NSCoder` has no member `RawValue`

I'm pretty sure it has something to do with the generic and the condition that Enum.RawValue == String. But I do not understand while it's not working, but works for encodeEnum().

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jens Kohl
  • 5,899
  • 11
  • 48
  • 77

1 Answers1

1

The problem is that in

guard let tmp = aDecoder.decodeEnumForKey("myKey") as? MyEnum else { return nil }

the compiler cannot infer the generic placeholder of

func decodeEnumForKey<Enum: ...>(key: String) -> Enum?

to be MyEnum, you have to cast the result to MyEnum? instead:

guard let tmp = aDecoder.decodeEnumForKey("myKey") as MyEnum? else { return nil }

so that the return type is inferred as MyEnum? from the calling context.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382