Default or Base Case in Enums

Hi

Swift Enums have raw values and hence are easy to initialise, irrespective of the type of raw value. Recently, Swift has added “allCases” property to the enums conforming to CaseIterable protocol.
I suggest to add a protocol, which gives default or base case to all enums. So that when initialising using the raw value, it returns that case instead of returning nil. This results in a non-failable initializer which can save a lot of boiler plate code. We can just check .baseCase and do the needful.

Regards
Imthath
Mac Developer

Why have a fail-able initializer in the first place if you're going to hide the result with some default value? Failing when you throw out an inappropriate value is a good thing. If you really want a default value, why not add a default initializer yourself?

The option of having a default initializer sounds like it should be able to be applied to all types, not just enums. Why not propose a protocol for that instead? (RangeReplaceableCollection would be amended to refine this new protocol.)

The purpose of the fail-able initializer was to avoid the switch statements. So yes, I can add a default initializer to fix that issue. But they I have to add the default initializer in all the enums I need.

Consider the following example. I’m developing a client app for a service, which is improving everyday. The API response for a field is a list of strings, like shifts in a production company (day shift, noon shift, night shift). Now for better usage within my app, while parsing the field, I define an enum with the three cases. I use switch statement and parse network response to enum. If the response is anything other than the expected three strings, in case that the company has added a new shift (say evening shift), I can use a base case in the enum.

To remove the switch case, I decide to use raw values for enums and use the fail-able initializer. But I don’t actually want it to return optional, so I write my own initializer and inside it, call the fail-able initializer and if it fails, I set the enum to base case.

So far so good, now consider I want to do the same for 20 fields. I have to write default initializers for 20 enums. Isn’t that too much of boiler plate code?

You've got other patterns that'll work for this.

let value = Foo(rawValue: string) ?? .defaultValue

If you want you can wrap that bit up in an initializer too:

extension MyEnum {
  init(rawValueOrDefault: String) {
    self = MyEnum(rawValue: string) ?? .defaultValue
  }
}
3 Likes

Thanks Jordan. I do want to take up your second option and write initializers for all my enums.

But I’m telling there can be even a better way of doing this. Just like Swift 4.2 introduced String and Data initializers which are not optional. An enum initializer which returns default case in case of no match is also possible.

My general concern around this is that this “default behavior” would tend to be somewhat of a code smell, except if you were trying to patch over error handing in some form of conversion, eg networking code with a server that you cannot adjust. In this case, the patch makes sense to be localized and documented at the point of use.

If your code is fragile enough that you’re running a risk of conversion from a raw value failing generally, what is going on? Generally, this type of failure is a sign of a problem that needs a more appropriate patch than just a cover like this.

Ultimately I think the fact these cases are “failable” is by design to make devs consider this at the site or use. This seems like a convenience patch for a problem, which doesn’t seem like a good idea for inclusion in the language.

Thanks for your concern Rod. The problem I’m facing is that I’m writing a Mac App for a API service, which is evolving way too often and I want my app to run irrespective of the changes they make to the network response until I come up with the new update.

I have found a suitable fix, by extending Optional to safely unwrap with a default type.

I don't see a compelling reason to add this feature.
Nil-coalescing should work just fine in all cases this is needed,
and much clearer in intend when the default value is (and isn't) desired.
It's unlike Dictionary subscription that has default value to reduce the boilerplate on get-mutate-set pattern.

It IS like Dictionary’s default value, that’s why I brought it up for comparison.

Dictionary’s default value has only 1 advantage over nil-coalescing(as far as I can tell)—to allow for the resulting value to mutate in-place.
Enum’s default value has no such use case since anything that needs mutation will need to be stored in a variable anyway and so it is perfectly fine to use nil-coalescing here.
Especially since IMO, Enum(rawValue: value) ?? .defaultValue isn’t even bloated in the first place.

1 Like

I think you meant to write It's not unlike..., and I didn't pick up on that.

1 Like