Search code examples
swiftenumsswift4failable

In place of a failable initializer, can you instead have it return a default case?


Consider this enum...

enum ServiceValue : String, Decodable {
    case a
    case b
    case c
    case other
}

Given a string 'a' you can get an instance of the enum, like so:

// 'value' is an optional since the initializer is bailable
let value = ServiceValue(rawValue:'a')

What we're trying to do is instead of returning 'nil' for unknown values such as 'd' or 'somethingUnknown', we want to return ServiceValue.other. However you can't override the initializer since it's not in a base-class, but rather compiler-generated for this enumeration itself.

That said, we tried rolling our own, like so...

init(serviceValue:String){
    self.init(rawValue:serviceValue) ?? self.init(rawValue:"other")
}

...but that didn't work with the following errors:

A non-failable initializer cannot delegate to failable initializer 'init(rawValue:)' written with 'init?'

and

Initializer delegation ('self.init') cannot be nested in another expression

Of course I could simply write a static method initFrom, like so...

initFrom(serviceValue:String) -> ServiceValue {
    return ServiceValue(rawValue:serviceValue) ?? ServiceValue(rawValue:"other")
}

...but that's a factory method, not a true initializer, nor does it protect against someone still using init(rawValue: (although perhaps that last part is good as it would be changing expected behavior. In a perfect world, I'd just hide that initializer altogether, but again, you can't override initializers in an enum.)

So can this be achieved, or is the factory pattern the only way to go?


Solution

  • You can create your custom non fallible initializer with a default value as follow:

    init(string: String) { 
        self = ServiceValue(rawValue: string) ?? .other
    }