Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I encode enum using NSCoder in swift?

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         }      // ...      } 
like image 719
kfmfe04 Avatar asked Oct 12 '14 15:10

kfmfe04


People also ask

What is an enum in Swift?

In Swift, an enum (short for enumeration) is a user-defined data type that has a fixed set of related values. We use the enum keyword to create an enum. For example, Note: Enum values are also called enum cases. And, we use the case keyword to declare values inside the enum. Since enum is a data-type, we can create variables of enum type.

How do you create an enumeration case in Swift?

You use the case keyword to introduce new enumeration cases. Swift enumeration cases don’t have an integer value set by default, unlike languages like C and Objective-C. In the CompassPoint example above, north, south, east and west don’t implicitly equal 0, 1, 2 and 3.

What are associated values in Swift enumerations?

This additional information is called an associated value, and it varies each time you use that case as a value in your code. You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed.

How does Apple’s barcode enum work?

Apple’s Barcode implementation is simple: This enum however contains more information than can be represented by a simple raw value type like a String or an Int and so it cannot conform to Codable without additional instructions as to how each case is to be coded.


2 Answers

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" ) 
like image 193
vacawama Avatar answered Oct 08 '22 09:10

vacawama


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")     }      // ...  } 
like image 34
Jared Cleghorn Avatar answered Oct 08 '22 09:10

Jared Cleghorn