Switch statement for imported NS_OPTIONS (RawOptionSetType) in Swift?
I was frustrated enough about this problem that I wrote a Bitmask<T>
class that can handle these use cases. The code is up on Github: brynbellomy/SwiftBitmask
It allows you to do stuff like this with any kind of object as your underlying type (here I'm using an enum
):
enum MonsterAttributes : IBitmaskRepresentable, IAutoBitmaskable {
case Big, Ugly, Scary
static var autoBitmaskValues : [MonsterAttributes] = [.Big, .Ugly, .Scary,]
var bitmaskValue: UInt16 { return AutoBitmask..autoBitmaskValueFor(self) }
init(bitmaskValue: UInt16) { self = AutoBitmask.autoValueFromBitmask(bitmaskValue) }
}
// various ways to initialize
let option : MonsterAttributes = .Ugly
let bitmaskOfOption = Bitmask(option)
let anotherBitmaskOfOption = |MonsterAttributes.Ugly // same as bitmaskOfOption
let orWithVar = option | .Big // == Bitmask<MonsterAttributes> with a bitmaskValue of 1 | 2
let simpleOr = MonsterAttributes.Big | .Ugly // == Bitmask<MonsterAttributes> with a bitmaskValue of 1 | 2
// getting the raw integral bitmask value
let simpleOrValue = simpleOr.bitmaskValue // == UInt16(1 | 2)
let orValue = (MonsterAttributes.Big | .Ugly).bitmaskValue // == UInt16(1 | 2)
// implements BooleanType
if simpleOr & .Ugly { /* this code will execute */ }
// supports pattern matching operator
if simpleOr ~= .Ugly { /* this code will execute */ }
if simpleOr ~= (.Ugly | .Scary) { /* this code will execute */ }
... and all you have to do is implement a one-property protocol.
I'm really curious if anyone has any feedback on or ideas for the code, so please leave an issue in the queue if you think of anything!
How to create NS_OPTIONS-style bitmask enumerations in Swift?
Swift 3.0
Almost identical to Swift 2.0. OptionSetType was renamed to OptionSet and enums are written lower case by convention.
struct MyOptions : OptionSet {
let rawValue: Int
static let firstOption = MyOptions(rawValue: 1 << 0)
static let secondOption = MyOptions(rawValue: 1 << 1)
static let thirdOption = MyOptions(rawValue: 1 << 2)
}
Instead of providing a none
option, the Swift 3 recommendation is to simply use an empty array literal:
let noOptions: MyOptions = []
Other usage:
let singleOption = MyOptions.firstOption
let multipleOptions: MyOptions = [.firstOption, .secondOption]
if multipleOptions.contains(.secondOption) {
print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.thirdOption) {
print("allOptions has ThirdOption")
}
Swift 2.0
In Swift 2.0, protocol extensions take care of most of the boilerplate for these, which are now imported as a struct that conforms to OptionSetType
. (RawOptionSetType
has disappeared as of Swift 2 beta 2.) The declaration is far simpler:
struct MyOptions : OptionSetType {
let rawValue: Int
static let None = MyOptions(rawValue: 0)
static let FirstOption = MyOptions(rawValue: 1 << 0)
static let SecondOption = MyOptions(rawValue: 1 << 1)
static let ThirdOption = MyOptions(rawValue: 1 << 2)
}
Now we can use set-based semantics with MyOptions
:
let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = [.FirstOption, .SecondOption]
if multipleOptions.contains(.SecondOption) {
print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.ThirdOption) {
print("allOptions has ThirdOption")
}
Swift 1.2
Looking at the Objective-C options that were imported by Swift (UIViewAutoresizing
, for example), we can see that options are declared as a struct
that conforms to protocol RawOptionSetType
, which in turn conforms to _RawOptionSetType
, Equatable
, RawRepresentable
, BitwiseOperationsType
, and NilLiteralConvertible
. We can create our own like this:
struct MyOptions : RawOptionSetType {
typealias RawValue = UInt
private var value: UInt = 0
init(_ value: UInt) { self.value = value }
init(rawValue value: UInt) { self.value = value }
init(nilLiteral: ()) { self.value = 0 }
static var allZeros: MyOptions { return self(0) }
static func fromMask(raw: UInt) -> MyOptions { return self(raw) }
var rawValue: UInt { return self.value }
static var None: MyOptions { return self(0) }
static var FirstOption: MyOptions { return self(1 << 0) }
static var SecondOption: MyOptions { return self(1 << 1) }
static var ThirdOption: MyOptions { return self(1 << 2) }
}
Now we can treat this new option set, MyOptions
, just like described in Apple's documentation: you can use enum
-like syntax:
let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)
And it also behaves like we'd expect options to behave:
let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption != nil { // see note
println("multipleOptions has SecondOption")
}
let allOptions = MyOptions.fromMask(7) // aka .fromMask(0b111)
if allOptions & .ThirdOption != nil {
println("allOptions has ThirdOption")
}
I've built a generator to create a Swift option set without all the find/replacing.
Latest: Modifications for Swift 1.1 beta 3.
Is there a better way to check active notification settings?
Looks like the only thing you can do to improve the code is make it more concise.
func printUserNotificationSettings() {
println("Notification Settings:")
let notificationSettingsTypes = UIApplication.sharedApplication().currentUserNotificationSettings().types
let badgeOn = (notificationSettingsTypes & .Badge) != nil
let soundOn = (notificationSettingsTypes & .Sound) != nil
let alertOn = (notificationSettingsTypes & .Alert) != nil
println("\tBadge? \(badgeOn)")
println("\tSound? \(soundOn)")
println("\tAlert? \(alertOn)")
}
UIUserNotificationType
implements RawOptionSetType
which is the swift mapping from NS_OPTIONS
in Objective C code. In earlier betas of Xcode, these objects also implemented BooleanType
, which would have allowed you to write this code a little more concisely, but that seems to have been removed prior to release.
Also, searching around, the most common way to do the check is != nil
so I have included that modification as well, it seems to improve the readability a bit.
Here is a pretty robust StackOverflow post on the topic: Switch statement for imported NS_OPTIONS (RawOptionSetType) in Swift?
And another great article on the background of RawOptionSetType: http://nshipster.com/rawoptionsettype/
MCOMessageFlag in Swift
As Dinh Viêt Hoà suggested in a comment, the fetchOperation request headers needed to include the flags.
let requestKind = MCOIMAPMessagesRequestKind.Headers | MCOIMAPMessagesRequestKind.Flags
let uids = MCOIndexSet(range: MCORangeMake(1, UINT64_MAX))
let folder = "INBOX"
let fetchOperation = self.imapSession.fetchMessagesOperationWithFolder(folder, requestKind: requestKind, uids: uids)
Declaring and using a bit field enum in Swift
Updated for Swift 2/3
Since swift 2, a new solution has been added as "raw option set" (see: Documentation), which is essentially the same as my original response, but using structs that allow arbitrary values.
This is the original question rewritten as an OptionSet
:
struct MyOptions: OptionSet
{
let rawValue: UInt8
static let One = MyOptions(rawValue: 0x01)
static let Two = MyOptions(rawValue: 0x02)
static let Four = MyOptions(rawValue: 0x04)
static let Eight = MyOptions(rawValue: 0x08)
}
let m1 : MyOptions = .One
let combined : MyOptions = [MyOptions.One, MyOptions.Four]
Combining with new values can be done exactly as Set
operations (thus the OptionSet part), .union
, likewise:
m1.union(.Four).rawValue // Produces 5
Same as doing One | Four
in its C-equivalent. As for One & Mask != 0
, can be specified as a non-empty intersection
// Equivalent of A & B != 0
if !m1.intersection(combined).isEmpty
{
// m1 belongs is in combined
}
Weirdly enough, most of the C-style bitwise enums have been converted to their OptionSet
equivalent on Swift 3, but Calendar.Compontents
does away with a Set<Enum>
:
let compontentKeys : Set<Calendar.Component> = [.day, .month, .year]
Whereas the original NSCalendarUnit
was a bitwise enum. So both approaches are usable (thus the original response remains valid)
Original Response
I think the best thing to do, is to simply avoid the bitmask syntax until the Swift devs figure out a better way.
Most of the times, the problem can be solved using an enum
and and a Set
enum Options
{
case A, B, C, D
}
var options = Set<Options>(arrayLiteral: .A, .D)
An and check (options & .A
) could be defined as:
options.contains(.A)
Or for multiple "flags" could be:
options.isSupersetOf(Set<Options>(arrayLiteral: .A, .D))
Adding new flags (options |= .C
):
options.insert(.C)
This also allows for using all the new stuff with enums: custom types, pattern matching with switch case, etc.
Of course, it doesn't have the efficiency of bitwise operations, nor it would be compatible with low level things (like sending bluetooth commands), but it's useful for UI elements that the overhead of the UI outweighs the cost of the Set operations.
Related Topics
How to Filter Nsarray in Swift
Swift 3:Delegate Within Tapgesturerecognizer in Generics Doesn't Get Called
How to Unpack Multiple Levels of Nested JSON in Firebase Database
When Two Optionals Are Assigned to an If Let Statement, Which One Gets Unwrapped? Swift Language
Nsmanagedobject Subclasses Duplicate Declaration
Swift Uikit Dynamics Add Collision Boundary. After Rotation Does Not Work Correctly
How to Simultaneously Satisfy Constraints Error Whats Causing This
Resizing Large Resolution Images Producing 1000X1000 Pixels Size When Size Is Set to 500X500 Pixels
Getting Data Out of Completionhandler in Swift in Nsurlconnection
Swift Alamofire Return Value Is Empty
Stopping Timer at Defined Amount of Time in Swift
Swift: Nil Error When Using Self.Moc.Save() to Save in Core Data
App Window on Top of All Windows Including Others App Windows
Contextual Member Has No Associated Value in Swift 3
How to Store the Progress of Progressview into Arraylist as One Element