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
        }

    // ...

    }

3 Answers 3

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" )
Sign up to request clarification or add additional context in comments.

2 Comments

If you have as! then the String can never be nil and ?? .DisplayAll is useless. Shouldn't it be as? instead?
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.
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")
    }

    // ...

}

1 Comment

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?
9

Update for Xcode 6.3, Swift 1.2:

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

note the as!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.