Search code examples
iosswiftenumsinitialization

Overriding Enum init?(rawValue: String) to not be optional


I want to have init from rawValue for my enum in Swift to return default value if rawValue init will return nil. Right now I have something like this:

public init(fromRawValue: String){
        self = Language(rawValue: fromRawValue) ?? .English
}

I don't like this because it is completely new initializer. I tried to make something like this:

public init(rawValue: String){
        self = Language(rawValue: fromRawValue) ?? .English
}

But I have got runtime exception with bad access. Can I somehow make it work or I just have to use this new and I cannot override original one to not be optional?

I would like to know if it possible to override original init from rawValue not workaround with completely new one that is using failable one.


Solution

  • The default initializer is failable. It means that if the received parameter does not match a valid enum case it does return nil.

    Now you want to do 2 incompatibles things:

    1. You want to redefine the default initializer making it not failable. In fact you want a default enum value created when the received param is not valid.
    2. Inside your redefined initializer you want to call a failable initializer (which no longer exists) using the same name of the new one.

    This is not possible, I the 3 possible solutions as follows:

    1. Creating a different init

    You define a new not failable initializer with a default value, a different parameter name and inside it you call the default failable initializer.

    enum Language: String {
        
        case english = "English", italian = "Italian", french = "French"
        
        init(fromRawValue: String) {
            self = Language(rawValue: fromRawValue) ?? .english
        }
    }
    
    1. Redefining the default init

    You redefine the default initializer, you make it not failable and you write the full logic inside it.

    enum Language: String {
        
        case english = "English", italian = "Italian", french = "French"
        
        init(rawValue: String) {
            switch rawValue {
            case "Italian": self = .italian
            case "French": self = .french
            default: self = .english
            }
        }
    }
    
    1. Creating a static func
    enum Language: String {
    
        case english = "English", italian = "Italian", french = "French"
    
        static func build(rawValue: String) -> Language {
            return Language(rawValue: rawValue) ?? .english
        }
    }
    

    Now you can build a Language value writing:

    let italian = Language.build(rawValue: "Italian") // Italian
    let defaultValue = Language.build(rawValue: "Wrong input") // English