I think ultimately it comes down to what kind of experience you want clients to have when they encounter an unknown case. This was something lots of people (including myself) worried about when the proposal was first raised: how is it even possible to handle an unknown case?
The best answer(s) IMO were:
- Inspect the value, with whatever API you know exists
- Read the library documentation very carefully
- Show an error
The first case is ideal - you can maybe even "properly" handle the value, depending on what information you need and which properties the enum exposes. My concern was: if I can pretty-much handle the case just by inspecting computed properties, why bother trying to exhaustively switch the value at all? All the benefits of enums fall away and you're left with an awkward struct. If you used a real struct instead in that case, you could get all kinds of benefits (like less boilerplate... basically everywhere).
The other cases are just ¯\_(ツ)_/¯
. Even developers with great documentation don't always keep every little bit of it current and accurate (even Apple doesn't). And it's not always possible to abort up to a sensible, stable point of recovery whenever a new enum value is detected.
The discussion about attributes and naming is interesting, but we also need to consider the guidelines we're setting about how to design a resilient API in Swift, so clients can still do useful things if values evolve.
I think that if you're trying to do a 1:1 mapping from C->Swift, you're better-off with a struct. Otherwise you get an experience which is worse than using the library's native C interface directly.
If you have a non-1:1 mapping, you might lose information going from C->Swift anyway. So you should use a failable initialiser and collapse all unknown values to nil
. That might be best for ICU - I'm not an expert, but I assume we're only going to be exposing a small subset of its functionality?