How do I encode enum using NSCoder in swift?
Asked Answered
U

3

67

Background

I am trying to encode a String-style enum using the NSCoding protocol, but I am running into errors converting to and back from String.

I get the following errors while decoding and encoding:

String is not convertible to Stage

Extra argument ForKey: in call

Code

    enum Stage : String
    {
        case DisplayAll    = "Display All"
        case HideQuarter   = "Hide Quarter"
        case HideHalf      = "Hide Half"
        case HideTwoThirds = "Hide Two Thirds"
        case HideAll       = "Hide All"
    }

    class AppState : NSCoding, NSObject
    {
        var idx   = 0
        var stage = Stage.DisplayAll

        override init() {}

        required init(coder aDecoder: NSCoder) {
            self.idx   = aDecoder.decodeIntegerForKey( "idx"   )
            self.stage = aDecoder.decodeObjectForKey(  "stage" ) as String    // ERROR
        }

        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeInteger( self.idx,             forKey:"idx"   )
            aCoder.encodeObject(  self.stage as String, forKey:"stage" )  // ERROR
        }

    // ...

    }
Upu answered 12/10, 2014 at 15:16 Comment(0)
P
70

You need to convert the enum to and from the raw value. In Swift 1.2 (Xcode 6.3), this would look like this:

class AppState : NSObject, NSCoding
{
    var idx   = 0
    var stage = Stage.DisplayAll

    override init() {}

    required init(coder aDecoder: NSCoder) {
        self.idx   = aDecoder.decodeIntegerForKey( "idx" )
        self.stage = Stage(rawValue: (aDecoder.decodeObjectForKey( "stage" ) as! String)) ?? .DisplayAll
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeInteger( self.idx, forKey:"idx" )
        aCoder.encodeObject(  self.stage.rawValue, forKey:"stage" )
    }

    // ...

}

Swift 1.1 (Xcode 6.1), uses as instead of as!:

    self.stage = Stage(rawValue: (aDecoder.decodeObjectForKey( "stage" ) as String)) ?? .DisplayAll

Swift 1.0 (Xcode 6.0) uses toRaw() and fromRaw() like this:

    self.stage = Stage.fromRaw(aDecoder.decodeObjectForKey( "stage" ) as String) ?? .DisplayAll

    aCoder.encodeObject( self.stage.toRaw(), forKey:"stage" )
Phionna answered 12/10, 2014 at 15:26 Comment(2)
If you have as! then the String can never be nil and ?? .DisplayAll is useless. Shouldn't it be as? instead?Penance
The as! is casting the String which does exist so the cast will succeed. Stage(rawValue: "someString") returns an optional because the string may not define a valid enum value. You have to unwrap that optional. The nil coalescing operator replaces that optional with an unwrapped version if it exists or .DisplayAll if it doesn't.Phionna
L
10

Here is a solution for Swift 4.2. As stated in the other answers, the problem is that you try to directly assign the stage variable with a decoded string, and you try to cast the stage variable to a string in the encodeWithCoder method. You need to use raw values instead.

enum Stage: String {
    case DisplayAll = "Display All"
    case HideQuarter = "Hide Quarter"
    case HideHalf = "Hide Half"
    case HideTwoThirds = "Hide Two Thirds"
    case HideAll = "Hide All"
}

class AppState: NSCoding, NSObject {
    var idx = 0
    var stage = Stage.DisplayAll

    override init() {}

    required init(coder aDecoder: NSCoder) {
        self.idx = aDecoder.decodeInteger(forKey: "idx")
        self.stage = Stage(rawValue: aDecoder.decodeObject(forKey: "stage") as String)
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encode(self.idx, forKey:"idx")
        aCoder.encode(self.stage.rawValue, forKey:"stage")
    }

    // ...

}
Lutist answered 13/7, 2018 at 17:33 Comment(1)
What if the enum don't have the row values? In my app, it is just an enum: enum SomeType { case1, case2, case3 } like this? How to handle for that case?Bronnie
R
9

Update for Xcode 6.3, Swift 1.2:

self.stage = Stage(rawValue: aDecoder.decodeObjectForKey("stage") as! String) ?? .DisplayAll

note the as!

Refraction answered 10/3, 2015 at 13:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.