Riccardo Perego
Riccardo Perego

Reputation: 213

Swift remove .rawValue from enums

I have this enum:

enum DMED: String {
    case money = "DMMoney"
    case netWorth = "DMNetWorth"
    case businessNum = "DMBusinessNum"
    case generalEPM = "DMGeneralEPM"
    case generalThreat = "DMGeneralThreat"
}

And in a class I have, I have this decoder function:

required init?(coder aDecoder: NSCoder) {
    self.money = (aDecoder.decodeDouble(forKey: DMED.money.rawValue))
    self.netWorth = (aDecoder.decodeDouble(forKey: DMED.netWorth.rawValue))
    self.businessNum = (aDecoder.decodeInteger(forKey: DMED.businessNum.rawValue))
    self.generalEPM = (aDecoder.decodeInteger(forKey: DMED.generalEPM.rawValue))
    self.generalThreat = (aDecoder.decodeInteger(forKey: DMED.generalThreat.rawValue))
}

I would love to know if it would be possible to remove the .rawValue from all of the enum calls. Thanks for your help in advanced.

Upvotes: 0

Views: 3565

Answers (3)

Hamish
Hamish

Reputation: 80781

One option would be to write a wrapper that allows for typed-key coding:

struct KeyedNSCoder<Key : CodingKey> {

  let coder: NSCoder

  init(_ coder: NSCoder, keyedBy _: Key.Type) {
    self.coder = coder
  }

  func decodeDouble(forKey key: Key) -> Double {
    return coder.decodeDouble(forKey: key.stringValue)
  }

  func decodeInteger(forKey key: Key) -> Int {
    return coder.decodeInteger(forKey: key.stringValue)
  }

  // repeat for other coding methods...
}

Then you can simply say:

enum DMED : String, CodingKey {
  case money = "DMMoney"
  case netWorth = "DMNetWorth"
  case businessNum = "DMBusinessNum"
  case generalEPM = "DMGeneralEPM"
  case generalThreat = "DMGeneralThreat"
}

class C : NSCoding {

  var money: Double // note: do not represent monetary values with floating-point numbers
  var netWorth: Double
  var businessNum: Int
  var generalEPM: Int
  var generalThreat: Int

  required init?(coder aDecoder: NSCoder) {
    let decoder = KeyedNSCoder(aDecoder, keyedBy: DMED.self)
    self.money = decoder.decodeDouble(forKey: .money)
    self.netWorth = decoder.decodeDouble(forKey: .netWorth)
    self.businessNum = decoder.decodeInteger(forKey: .businessNum)
    self.generalEPM = decoder.decodeInteger(forKey: .generalEPM)
    self.generalThreat = decoder.decodeInteger(forKey: .generalThreat)
  }

  func encode(with coder: NSCoder) {
    // ...
  }
}

Upvotes: 0

Cristik
Cristik

Reputation: 32775

You could add an extension over NSCoder to handle the DMED keys:

extension NSCoder {

    func decodeDouble(forKey key: DMED) -> Double  {
        return decodeDouble(forKey: key.rawValue)
    }

    func decodeInteger(forKey key: DMED) -> Int {
        return decodeInteger(forKey: key.rawValue)
    }
}

self.money = aDecoder.decodeDouble(forKey: .money)

Or, if you want to make the methods more general, and work with any String enums, you could make the method overrides generic:

extension NSCoder {

    func decodeDouble<R: RawRepresentable>(forKey key: R) -> Double where R.RawValue == String {
        return decodeDouble(forKey: key.rawValue)
    }

    func decodeInteger<R: RawRepresentable>(forKey key: R) -> Int where R.RawValue == String {
        return decodeInteger(forKey: key.rawValue)
    }
}

self.money = aDecoder.decodeDouble(forKey: DMED.money)

Or, making use of the type inference support, you can declare an even more generic method:

extension NSCoder {

    func decode<R: RawRepresentable>(forKey key: R) -> Double where R.RawValue == String {
        return decodeDouble(forKey: key.rawValue)
    }

    func decode<R: RawRepresentable>(forKey key: R) -> Int where R.RawValue == String {
        return decodeInteger(forKey: key.rawValue)
    }

    func decode<R: RawRepresentable>(forKey key: R) -> Any? where R.RawValue == String {
        return decodeObject(forKey: key.rawValue)
    }
}

required init?(coder aDecoder: NSCoder) {
    self.money = aDecoder.decode(forKey: DMED.money)
    self.netWorth = aDecoder.decode(forKey: DMED.netWorth)
    self.businessNum = aDecoder.decode(forKey: DMED.businessNum)
    self.generalEPM = aDecoder.decode(forKey: DMED.generalEPM)
    self.generalThreat = aDecoder.decode(forKey: DMED.generalThreat)
}

Upvotes: 1

rmaddy
rmaddy

Reputation: 318774

If your only use of the enum values is so they can be used as String constants, you should change your code to use a struct with static values.

struct DMED {
    static let money = "DMMoney"
    static let netWorth = "DMNetWorth"
    static let businessNum = "DMBusinessNum"
    static let generalEPM = "DMGeneralEPM"
    static let generalThreat = "DMGeneralThreat"
}

required init?(coder aDecoder: NSCoder) {
    self.money = (aDecoder.decodeDouble(forKey: DMED.money))
    self.netWorth = (aDecoder.decodeDouble(forKey: DMED.netWorth))
    self.businessNum = (aDecoder.decodeInteger(forKey: DMED.businessNum))
    self.generalEPM = (aDecoder.decodeInteger(forKey: DMED.generalEPM))
    self.generalThreat = (aDecoder.decodeInteger(forKey: DMED.generalThreat))
}

Upvotes: 1

Related Questions