@unknown default usage with enums in Swift

@unknown default has been introduced in Swift 5 with SE-0192. It’s a new addition to the way we can work with Swift enums and helps us to prepare for future changes. After updating your project to Swift 5 you might end up with a new warning in Xcode 10.2:

Switch covers known cases, but ‘UNAuthorizationStatus’ may have additional unknown values, possibly added in future versions
A new warning tells you to implement the @unknown default case
A new warning tells you to implement the @unknown default case

Basically, Swift warns you to be prepared for future changes.

Why do we need @unknown default to prepare for the future?

It’s quite an interesting question! The reason is Apple’s established process for evolving APIs. Whenever Apple adds a new enum case, it’s a source-breaking change. Say, for example, Apple introduces a new UNAuthorizationStatus case in the future, your project will not compile if you would've handled each case individually.

Frozen vs non-frozen enums

The change comes with so-called frozen and non-frozen enums. It basically means that frozen enums will never get any new cases. When dealing with non-frozen enums the @unknown keyword ensures that clients handle any future cases. For now, it looks like Apple made a decision based on expectations and made some enums frozen and some not. You can find the full list here.

Do I ever need the @unknown case with my own enums?

Quoting from the Swift Evolution proposal:

A key note: in this version of the proposal, nothing changes for user-defined Swift enums. This only affects C enums and enums in the standard library and overlays today. (This refers to libraries that Apple could hypothetically ship with its OSs, as it does with Foundation.framework and the Objective-C runtime.) The features described here may be used by third-party libraries in the future.

In other words, unless you defined your enums in C, you’ll not create warnings on implementation level when dealing with your own defined enums.

How to use @unknown default

In the following example, we’re switching between user notification authorization statuses. This would’ve worked fine in Swift 4.2, but now triggers a warning.

switch userNotificationsAuthorizationStatus {
case .notDetermined:
requestPermission()
case .authorized, .denied, .provisional:
// No need to request permission.
print("Didn't request permission for User Notifications")
}

Therefore, we can use a combination of the fallthrough keyword together with the new @unknown keyword.

switch userNotificationsAuthorizationStatus {
case .notDetermined:
requestPermission()
case .authorized, .denied, .provisional:
fallthrough
@unknown default:
// No need to request permission.
print("Didn't request permission for User Notifications")
}

The difference between default and @unknown default

@unknown default basically works the same as the regular default and therefore, matches any value. The main difference is that the compiler will produce a warning if all known elements of the enum have not yet been matched. New enum cases remain source-compatible as a result of throwing a warning instead of an error.

Conclusion

The main point to take here; update your code to Swift 5 and check your warnings whenever a new iOS version comes out. It might be that there’s a new enum case for you to take care of!


Originally published at SwiftLee.

More posts and updates: @twannl