Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Store Enum with associated values in NSUserDefaults (Swift)

I wish to pass enums with associated values to a Today Extension, Since I am using NSUserDefaults for that it seems that there is no straight forward way to do that as Enum does not conform to AnyObject.

enum DeepLinkAction {
  case StartPractice(lessonName:String)
  case StartTest
  case Letters(didComplete:Bool, iconURL:String)
}

I tried using NSKeyedArchiver but is requires conforming to AnyObject.

Is there a way to do that or I should just remove the associated values ?

Thanks

like image 215
shannoga Avatar asked Dec 30 '25 21:12

shannoga


1 Answers

As brilliantly described here, in Swift4 you can now use Codable protocol to store enum with associated values in NSUserDefaults. You can use something like the following

enum Example {
  case caseOne(a: DownloadState, b: Route)
  case caseTwo(a: String, b: Int, c: Data?)
}

extension Example: Codable {
  private enum CodingKeys: String, CodingKey {
    case base, caseOneParams, caseTwoParams
  }

  private enum Base: String, Codable {
    case caseOne, caseTwo
  }

  private struct CaseOneParams: Codable {
    let a: DownloadState
    let b: Route
  }

  private struct CaseTwoParams: Codable {
    let a: String
    let b: Int
    let c: Data?
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)

    switch self {
    case .caseOne(let a, let b):
      try container.encode(Base.caseOne, forKey: .base)
      try container.encode(CaseOneParams(a: a, b: b), forKey: .caseOneParams)
    case .caseTwo(let a, let b, let c):
      try container.encode(Base.caseTwo, forKey: .base)
      try container.encode(CaseTwoParams(a: a, b: b, c: c), forKey: .caseTwoParams)
    }
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let base = try container.decode(Base.self, forKey: .base)

    switch base {
    case .caseOne:
      let caseOneParams = try container.decode(CaseOneParams.self, forKey: .caseOneParams)
      self = .caseOne(a: caseOneParams.a, b: caseOneParams.b)
    case .caseTwo:
      let caseTwoParams = try container.decode(CaseTwoParams.self, forKey: .caseTwoParams)
      self = .caseTwo(a: caseTwoParams.a, b: caseTwoParams.b, c: caseTwoParams.c)
    }
  }
}

DownloadState and Route in the example code above have to be Codable types.

And then to write in NSUserDefaults

let example: Example = ... 
UserDefaults.standard.set(try? PropertyListEncoder().encode(example), forKey: "yourExampleKey")

to read

if let data = UserDefaults.standard.value(forKey:"yourExampleKey") as? Data {
    let example = try? PropertyListDecoder().decode(Example.self, from: data)
}
like image 77
Giorgio Avatar answered Jan 02 '26 11:01

Giorgio



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!