Extract rawValue from associated enum

I understand why I cannot extract the raw value from an enum that uses associated values, even for one of the statements, but why can I not extract the raw value from the enum that is one of the associated values themselves?

enum FoesLevel {
    
    case pipsqueak (annimaux: PipsqueakFoes)
    case average (annimaux: AverageFoes)
    case tough (annimaux: ToughFoes)
}


enum PipsqueakFoes: String {
    
    case spider = "Spider"
    case beetle = "Beetle"
}

    
enum AverageFoes: String {
    
    case mouse = "Mouse"
    case squirrel = "Squirrel"
}


enum ToughFoes: String {
    
    case snake = "Snake"
    case gnome = "Gnome"
}

let test1 = FoesLevel.pipsqueak(annimaux: .spider).self
//print(test1.rawValue)

The FoesLevel enum does not have a rawValue property. You could define one yourself, or your could define a property with a more descriptive name. For example:

extension FoesLevel {
  var name: String {
    switch self {
    case .pipsqueak(let x): return x.rawValue
    case .average(let x): return x.rawValue
    case .tough(let x): return x.rawValue
    }
  }
}

Alternatively you can use the Mirror API to retrieve the label of the FoesLevel enumeration.

extension FoesLevel: RawRepresentable {

    init?(rawValue: String) {
        return nil
    }
    
    var rawValue: String {
        guard let label = Mirror(reflecting: self).children.first?.label else {
            return .init(describing: self)
        }
        return label
    }
    
}

let test1 = FoesLevel.pipsqueak(annimaux: .spider)
print(test1.rawValue) // pipsqueak

« annimaux » ?

Jèrriais ? En français il s’écrit avec seulement un « n » : « animaux ».

(And if it is actually supposed to be English like the surrounding code, then it would be “animals”.)

Just returning back to this now I found a way around this, Tiigi's solution is closer to what I was looking for as it returns an existing value, rather than having to put them back in again, which wouldn't make sense for what I wanted to achieve.

However, I don't believe that I was clear enough in the outset as from the example I gave I actually wanted to return the "spider" rawValue, not the enum's name. I did look into RawRepresentable, but I didn't understand it and looking at the code I am not able to understand what is going on to modify it.

Well now I am not clear about what you want. Did you try the code I wrote?

I wanted to extract the rawValue "Spider":

e.g.

let test1 = FoesLevel.pipsqueak(annimaux: .spider).self
print(test1.rawValue) //Spider

I repeat my question: did you try the code I wrote?

I chose to spell the property “name”, so that would be “print(test1.name)”. If you really want it to be spelled “rawValue”, that is a trivial change.

Also, why on earth do you have “.self” at the end of the line declaring test1? That is entirely redundant.

If I understand you correctly, I think this is what you're looking for:

let test1 = FoesLevel.pipsqueak(annimaux: .spider).self

if case let FoesLevel.pipsqueak(annimaux) = test1 {
    print(annimaux.rawValue) // "Spider"
}
// or
switch test1 {
case .pipsqueak(let annimaux):
    print(annimaux.rawValue) // "Spider"
default:
    break
}

You have to access the associated value of your FoesLevel case in order to get it's rawValue

As others have mentioned, you could also add RawRepresentable to FoesLevel:

enum FoesLevel: RawRepresentable {

    case pipsqueak (annimaux: PipsqueakFoes)
    case average (annimaux: AverageFoes)
    case tough (annimaux: ToughFoes)

    var rawValue: String {
        switch self {
        case .pipsqueak(let annimaux): return annimaux.rawValue
        case .average(let annimaux): return annimaux.rawValue
        case .tough(let annimaux): return annimaux.rawValue
        }
    }

    init?(rawValue: String) {
        if let pipsqueak = PipsqueakFoes(rawValue: rawValue) {
            self = .pipsqueak(annimaux: pipsqueak)
        }
        else if let average = AverageFoes(rawValue: rawValue) {
            self = .average(annimaux: average)
        }
        else if let tough = ToughFoes(rawValue: rawValue) {
            self = .tough(annimaux: tough)
        }
        else {
            return nil
        }
        
    }
}

print(FoesLevel.pipsqueak(annimaux: .spider).rawValue) // Spider

However personally I would recommend reversing the ownership here and have your Foes have a FoesLevel, rather than the FoesLevel containing the Foes

Thanks for the help GetSwifty, apologies for the late response. I did see the logic in what you proposed by reversing ownership, which actually fit better with what I needed, I think. However, I needed to further conform to Hashable and would like you to inspect my changes, let me know if they're ok please:

enum FoesLevel: RawRepresentable, Hashable {
    
    case mouse (category: PipsqueakFoes)
    case cat (category: AverageFoes)
    case lion (category: ToughFoes)
    
    
    var hashValue2 : String {
        return self.toString()
    }
    
    func toString() -> String {
        switch self {
        case .mouse : return "Mouse"
        case .cat : return "Cat"
        case .lion : return "Lion"
        }
    }

    
    var rawValue: String {
        switch self {
        case .mouse(let category): return category.rawValue
        case .cat(let category): return category.rawValue
        case .lion(let category): return category.rawValue
        }
    }
    
    init?(rawValue: String) {
        if let mouse = PipsqueakFoes(rawValue: rawValue) {
            self = .mouse(category: mouse)
        }
        if let cat = PipsqueakFoes(rawValue: rawValue) {
            self = .cat(category: cat)
        }
        if let lion = AverageFoes(rawValue: rawValue) {
            self = .lion(category: lion)
        }
        else {
            return nil
        }
    }
}

func == (lhs: FoesLevel, rhs: FoesLevel) -> Bool {
    return lhs.toString() == rhs.toString()
}

For some reason Xcode warned me about a duplicate declaration of "hashValue" ("hashValue2"), but I couldn't find it anywhere in my code, is this normal?

Thanks again!

Since your rawValue is a String, and String already conforms to Hashable, hashValue is synthesized for you when you declare that FoesLevel conforms to Hashable. You don't need to define it yourself.

I was hoping for the same, but when using the enum with a dictionary Swift kicked up a fuss. I am on v4x, but regardless I take it that I did it correctly?

I don't know for sure if that functionality was there in 4.x, but given the duplicate definition error you received, I assume that it was.

As for Swift having "kicked up a fuss", can you be more specific? What did you try to do and what was the actual error that you received?

Note, also, that your last bit of code above has some issues. Your init? method has a couple of type mismatch errors, and because you left out a few elses, even if the type errors are fixed, it will return nil for inputs of "Mouse" and "Cat" where it shouldn't. The init should probably look something like:

        if let mouse = PipsqueakFoes(rawValue: rawValue) {
            self = .mouse(category: mouse)
        } else if let cat = AverageFoes(rawValue: rawValue) {
            self = .cat(category: cat)
        } else if let lion = ToughFoes(rawValue: rawValue) {
            self = .lion(category: lion)
        } else {
            return nil
        }
    }

Beyond that, the init is fallible, so it returns a FoesLevel? rather than a FoesLevel. If you are trying to use it with a dictionary of type [FoesLevel: SomeOtherType] rather than [FoesLevel?: SomeOtherType] you will get errors if you try to use an optional as the key.