Swift has the OptionSet type, which basically adds set operations to C-Style bit flags. Apple is using them pretty extensively in their frameworks. Examples include the options parameter in animate(withDuration:delay:options:animations:completion:)
.
On the plus side, it lets you use clean code like:
options: [.allowAnimatedContent, .curveEaseIn]
However, there is a downside as well.
If I want to display the specified values of an OptionSet
, there doesn't seem to be a clean way to do it:
let options: UIViewAnimationOptions = [.allowAnimatedContent, .curveEaseIn]
print("options = " + String(describing: options))
Displays the very unhelpful message:
options = UIViewAnimationOptions(rawValue: 65664)
The docs for some of these bit fields expresses the constant as a power-of-two value:
flag0 = Flags(rawValue: 1 << 0)
But the docs for my example OptionSet, UIViewAnimationOptions
, doesn't tell you anything about the numeric value of these flags and figuring out bits from decimal numbers is not straightforward.
Question:
Is there some clean way to map an OptionSet to the selected values?
My desired output would be something like:
options = UIViewAnimationOptions([.allowAnimatedContent, .curveEaseIn])
But I can't think of a way to do this without adding messy code that would require me to maintain a table of display names for each flag.
(I'm interested in doing this for both system frameworks and custom OptionSets I create in my own code.)
Enums let you have both a name and a raw value for the enum, but those don't support the set functions you get with OptionSets.
This article in NSHipster gives an alternative to OptionSet that offers all the features of an OptionSet, plus easy logging:
https://nshipster.com/optionset/
If you simply add a requirement that the Option type be CustomStringConvertible, you can log Sets of this type very cleanly. Below is the code from the NSHipster site - the only change being the addition of
CustomStringConvertible
conformance to theOption
classThen using it:
That outputs
Just like you want it to.
That works because a Set displays its members as a comma-delimited list inside square brackets, and uses the
description
property of each set member to display that member. Thedescription
property simply displays each item (the enum's name as aString
) with a.
prefixAnd since the rawValue of a
Set<Option>
is the same as an OptionSet with the same list of values, you can convert between them readily.I wish Swift would just make this a native language feature for
OptionSet
s.