21

Let's say I have an existing code as follows:

enum SomeEnumCases {
  case existing
  case alreadyExisting
}

func doSomething(withEnums enumCase: SomeEnumCases) {
  switch enumCase {
  case .existing:
    print("This case was already existing")
  case .alreadyExisting:
    print("This case was already existing too...")
  }
}

Now, if I were to add a new case the the enum, the function above would show a compile error saying the switch case must be exhaustive, and I would be FORCED to handle the new missing case. I would add a third case in the switch statement, or add a default statement.

Now, I order to handle such unforeseen enum cases, I would like to add an @unknown default case to the existing function above. The only problem is, now it would give me a warning saying Default will never be executed.

So the question is, how do I future-proof my enum such that I can:

  1. Exhaustively handle all current enum cases, AND
  2. Have a default handling mechanism for the future unknown case, AND
  3. See a warning only when newer cases are added and these cases must be handled by the default case.

That means, the following code SHOULD NOT give warnings:

enum SomeEnumCases {
  case existing
  case alreadyExisting
}

func doSomething(withEnums enumCase: SomeEnumCases) {
  switch enumCase {
  case .existing:
    print("This case was already existing")
  case .alreadyExisting:
    print("This case was already existing too...")
  @unknown default: // <-- warning: Default will never be executed: should be suppressed
    print("Alright, this is something new and exciting !!")
  }
}

but the following code SHOULD give a warning:

enum SomeEnumCases {
  case existing
  case alreadyExisting
  case new
}

func doSomething(withEnums enumCase: SomeEnumCases) {
  switch enumCase { // <-- warning: Switch must be exhaustive: This should stay.
  case .existing:
    print("This case was already existing")
  case .alreadyExisting:
    print("This case was already existing too...")
  @unknown default:
    print("Alright, this is something new and exciting !!")
  }
}

Is that possible through @unknown or otherwise ?

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Mehul Parmar
  • 3,599
  • 3
  • 26
  • 42
  • Like the regular default, @unknown default matches any value; it is a "catch-all" case. However, the compiler will produce a warning if all known elements of the enum have not already been matched. For better understanding follow this link: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md – sinner Mar 30 '19 at 17:01

1 Answers1

20

The warning is probably somewhat misleading as the spec says (emphasis added):

A nonfrozen enumeration is a special kind of enumeration that may gain new enumeration cases in the future—even after you compile and ship an app. Switching over a nonfrozen enumeration requires extra consideration. When a library’s authors mark an enumeration as nonfrozen, they reserve the right to add new enumeration cases, and any code that interacts with that enumeration must be able to handle those future cases without being recompiled. Only the standard library, Swift overlays for Apple frameworks, and C and Objective-C code can declare nonfrozen enumerations. Enumerations you declare in Swift can’t be nonfrozen.

So it's not so much that the branch will never be executed but that the feature is completely unsupported for your SomeEnumCases user-defined Swift enum.

There seems to be no supported way of doing what you wish in Swift 5, and some indications that adding cases is seen as a breaking change as it could/would break binary compatibility, but Swift is an ever moving target...

CRD
  • 52,522
  • 5
  • 70
  • 86
  • That's a shame as it will put most developers off using this new case. If they do want to use it, they will have to deal with having permanent warnings in their project saying "Default will never be executed". If you have many enums in your project and want to future proof your code, this becomes untenable as you will end up with numerous warnings which no developer wants to have in their project – AdamM Apr 18 '19 at 08:16
  • 2
    @AdamM – If used with the right kind of "enum" there will be no warnings, it might have been better if the Swift compiler issued an error when it is used with the wrong kind to avoid the confusion. A traditional enum is a type defined by a set of literal values and in the C-family this type is weak and can be expanded by adding more literals. The type of enum Swift uses is essentially a "tagged disjoint union", they exist in a number of languages, and can be constructed in the C-family using a combination of `enum`, `union` and `struct`. [cont...] – CRD Apr 19 '19 at 08:51
  • 2
    [...cont] Given the (current) way Swift compiles its enums adding a new case could break binary compatibility between existing compiled code and the *existing* cases, so recompilation of code is required when any new cases are added. That's not to say Swift couldn't support non frozen Swift-style enums, but they (presumably) weighed the tradeoffs and choose not to (but Swift is an ever moving target...) – CRD Apr 19 '19 at 08:55