NS_OPTIONS for Swift (type-checked bitmask container). Basically an easier-to-implement RawOptionSet.

Overview

a replacement for NS_OPTIONS / RawOptionSet

Build Status CocoaPods CocoaPods CocoaPods GitHub tag

Bitmask<T> is a quicker way to get NS_OPTIONS-style functionality in a Swift environment. It's intended as a replacement for Swift's RawOptionSet, which is so long and arduous to implement that someone wrote a code generator for it.

It allows you to use the simple, familiar syntax of Swift's bitwise operators (|, &, ~, ^, etc.) with any custom struct, enum, or class type by wrapping that type in a Bitmask<T>.

install

Use CocoaPods.

In your Podfile:

pod 'SwiftBitmask'

And then from the command line:

$ pod install

Bitmask<T> with raw integer types

The Bitmask class takes a generic parameter indicating the type of integer you want to use to store the bitmask's raw value. Code:

let bitmask = Bitmask<UInt16>(1 << 2 | 1 << 5)
bitmask.bitmaskValue  // returns UInt16(1 << 2 | 1 << 5)
let bitmaskA = Bitmask<UInt16>(1 << 2)
let bitmaskB = Bitmask<UInt16>(1 << 5)
let allTogetherNow = bitmaskA | bitmaskB
bitmask.bitmaskValue  // also returns UInt16(1 << 2 | 1 << 5), just like above

Bitmask<T> with any type

Bitmasks can be converted back and forth between non-integer types as well. Any type implementing the IBitmaskRepresentable protocol gets this functionality for free.

All the protocol requires is that you implement var bitmaskValue, ensuring that its type conforms to BitwiseOperationsType and Equatable:

public protocol IBitmaskRepresentable
{
    typealias BitmaskRawType : BitwiseOperationsType, Equatable
    var bitmaskValue : BitmaskRawType { get }
}

Just use any built-in integer type and it'll work.

Here's a quick example of a type that implements IBitmaskRepresentable:

enum MonsterAttributes : UInt16, IBitmaskRepresentable
{
    case Big = 1
    case Ugly = 2
    case Scary = 4

    var bitmaskValue : BitmaskRawType { return self.rawValue }

    init(bitmaskValue: UInt16) {
        self.init(rawValue:bitmaskValue)
    }
}

Now you can create a Bitmask<MonsterAttributes> and use it the same way you would use a Bitmask with a raw integer underlying type:

let b = Bitmask<MonsterAttributes>(.Big, .Scary)

what does this get me?

concise syntax

You can write code that's almost as concise as the syntax you would use for simple, integral bitwise operations, but with the improved type safety and versatility of doing so with your own custom type. Note that you almost never have to write Bitmask<T>:

// prefix operator initialization
let bitmask = |MonsterAttributes.Scary         // == Bitmask(MonsterAttributes.Scary)

// implicit initialization via bitwise operators
let option : MonsterAttributes = .Ugly
let orWithVar = option | .Big                 // == Bitmask<UInt16> with a bitmaskValue of 1 | 2
let simpleOr  = MonsterAttributes.Big | .Ugly // == Bitmask<UInt16> with a bitmaskValue of 1 | 2

// the raw bitmask value can be obtained with from the bitmaskValue property
let simpleOrValue = simpleOr.bitmaskValue                        // == UInt16(1 | 2)
let orValue       = (MonsterAttributes.Big | .Ugly).bitmaskValue // == UInt16(1 | 2)

if statements

Bitmask also implements NilLiteralConvertible and BooleanType, which allow for concise conditionals:

// Bitmask<T> implements BooleanType
if simpleOr & MonsterAttributes.Scary {
    println("(boolean comparison) scary!")
}

// Bitmask<T> implements NilLiteralConvertible
if simpleOr & MonsterAttributes.Scary != nil {
    println("(nil literal comparison) scary!")
}

pattern matching + switch

Bitmask can tango with the pattern-matching operator (~=), which is basically equivalent to checking if bits are set using &:

// Bitmask<T> is compatible with ~=, the pattern-matching operator
if simpleOr ~= MonsterAttributes.Scary {
    println("(pattern matching operator) scary!")
}

if simpleOr ~= MonsterAttributes.Scary | .Big {
    println("(pattern matching operator) either big or scary!")
}

You can write switch statements with Bitmask too, although in my experience playing around with this code so far, it almost never seems to be the control structure that makes the most sense. If you insist upon trying it out, just be careful to add fallthroughs where appropriate and put your case statements in an order that makes sense for your application:

switch simpleOr
{
    case |MonsterAttributes.Big:
        println("big!")
        fallthrough

    case MonsterAttributes.Big | .Scary:
        println("either big or scary!")
        fallthrough

    case |MonsterAttributes.Ugly:
        println("ugly!")
        fallthrough

    case |MonsterAttributes.Scary:
        println("scary!")
        fallthrough

    default:
        // ...
}

automatic bitmask raw values for the lazy and overworked

You can also have your Bitmask object auto-generate the bitmask values for your IBitmaskRepresentable type. This is useful if, say, your type is an enum with a non-integer raw type.

For example, let's say you're reading options out of a JSON config file and they're represented as an array of strings, and you want your enum's rawValues to represent what's in the file. By implementing IAutoBitmaskable, you can use a couple of convenience functions in your type's implementation of IBitmaskRepresentable and escape having to write a big switch statement that doubles the length of your type's code.

Just conform to IAutoBitmaskable and add class/static var autoBitmaskValues : [Self], which should return all of the values of your enum. You can then implement var bitmaskValue and init(bitmaskValue:T) with the auto-bitmasking functions like so:

enum MonsterAttributes : String, IBitmaskRepresentable, IAutoBitmaskable
{
    case Big = "big"
    case Ugly = "ugly"
    case Scary = "scary"

    static var autoBitmaskValues : [MonsterAttributes] = [.Big, .Ugly, .Scary,]

    var  bitmaskValue: UInt16  { return AutoBitmask.autoBitmaskValueFor(self) }
    init(bitmaskValue: UInt16) { self = AutoBitmask.autoValueFromBitmask(bitmaskValue) }
}

If you're curious, the values in the autoBitmaskValues array are assigned integer bitmask values (i.e., 1 << 0, 1 << 1, ... 1 << n) from left to right.

a few implementation details

It's worth noting that because Bitmask stores its raw value as a basic BitwiseOperationsType (in other words, an integer of some kind), its memory footprint is incredibly small, especially compared with an implementation that basically functions as a wrapper over a Set (or some other collection type). This is particularly nice when you're using bitmasks to initialize or configure a huge number of objects at the same time.

The trade-off, of course, is that any time you convert values back and forth between your IBitmaskRepresentable type and Bitmask, it incurs some processing overhead in accessing your object's bitmaskValue property (and, if you implemented IAutoBitmaskable, there's an array search operation involved as well — take a look at AutoBitmask.swift if you're concerned about that).

I chose this implementation because I'm using Bitmask to initialize values during a load phase of a game I'm working on. Since the actual Bitmask objects are out of the picture as soon as loading is complete, there's very little back-and-forth between my IBitmaskRepresentable types and Bitmask, meaning there's not much processing overhead for me. I just hold onto the integer values, which is what the game engine wants anywway.

However, if you have a use case that would benefit from implementing Bitmask as a collection wrapper, I'd be interested in hearing about it in the issue queue!

license

ISC

authors / contributors

bryn bellomy < [email protected] >

You might also like...
Pilgrim - Dependency injection for Swift (iOS, OSX, Linux). Strongly typed, pure Swift successor to Typhoon.

pilgrim.ph Pilgrim is a dependency injection library for Swift with the following features: Minimal runtime-only library that works with pure Swift (s

Injector - A Swift package for simple dependency injection that also supports Swift UI previews

A Swift package for simple dependency injection that also supports Swift UI prev

Cleanse is a dependency injection framework for Swift.
Cleanse is a dependency injection framework for Swift.

Cleanse - Swift Dependency Injection Cleanse is a dependency injection framework for Swift. It is designed from the ground-up with developer experienc

DIKit Dependency Injection Framework for Swift, inspired by KOIN.

DIKit Dependency Injection Framework for Swift, inspired by KOIN. Basically an implementation of service-locator pattern, living within the applicatio

Tranquillity is a lightweight but powerful dependency injection library for swift.
Tranquillity is a lightweight but powerful dependency injection library for swift.

DITranquillity Tranquillity is a lightweight but powerful dependency injection library for swift. The name "Tranquillity" laid the foundation in the b

Pure makes Pure DI easy in Swift.

Pure Pure makes Pure DI easy in Swift. This repository also introduces a way to do Pure DI in a Swift application. Table of Contents Background Pure D

Swinject is a lightweight dependency injection framework for Swift.
Swinject is a lightweight dependency injection framework for Swift.

Swinject Swinject is a lightweight dependency injection framework for Swift. Dependency injection (DI) is a software design pattern that implements In

Dependency Injection framework for Swift (iOS/macOS/Linux)
Dependency Injection framework for Swift (iOS/macOS/Linux)

Declarative, easy-to-use and safe Dependency Injection framework for Swift (iOS/macOS/Linux) Features Dependency declaration via property wrappers or

Swift Ultralight Dependency Injection / Service Locator framework
Swift Ultralight Dependency Injection / Service Locator framework

Swift Ultralight Dependency Injection / Service Locator framework

Comments
  • Issues with Swift 1.2

    Issues with Swift 1.2

    @brynbellomy Hi there, love the idea of the library, but I'm having trouble getting it to compile with Swift 1.2. There seems to be issues in SwiftBitmask, Funky and Llama. I thought I had most of them fixed, but my compiler is now seg faulting and I have no idea what's going on.

    opened by bennichols 9
  • Updated for Swift 3

    Updated for Swift 3

    Updated SwiftBitmask for Swift 3.  A bunch of syntax has changed but Swift 3 hasn't added anything that encompasses the functionality of SwiftBitmask— so it remains a much cleaner and better alternative to the Swift-default .rawValue crappery.


    Eliminated usage of the Funky library, since it (still) hasn't been updated for Swift 3.

    It's worth noting that the Swift 3 syntax for the things that Funky was doing here is surprisingly similar to Funky's syntax, and most cases more concise. It's plausible that the authors of Funky have abandoned work on their lib because Swift 3 added enough new functional-programming stuff to satisfy these types of workflows.

    As a side-effect of removing Funky, SwiftBitmask is no longer dependent upon any CocoaPods, so removed all the CocoaPods stuff.

    Unfortunately, leaving a Podfile without any required pods isn't an option— pod update warns that “[!] The Podfile does not contain any dependencies.” and the build fails with “…/SwiftBitmask/Pods/Target Support Files/Pods-SwiftBitmask/Pods-SwiftBitmask-resources.sh: No such file or directory”.

    Auto-updated to Swift 3 syntax using Xcode 8.2.1, and made additional by-hand Swift 3 updates.

    Performed Xcode's recommended setting updates.

    opened by capnslipp 2
  • Manual Installation

    Manual Installation

    CocoaPods and Carthage are awesome tools and make our life really easier, but there are some devs who still don't know how to use them.

    It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

    opened by lfarah 0
Owner
Bryn Bellomy
Distributed systems, P2P
Bryn Bellomy
ViperServices - Simple dependency injection container for services written for iOS in swift supporting boot order

ViperServices Introduction ViperServices is dependency injection container for iOS applications written in Swift. It is more lightweight and simple in

Siarhei Ladzeika 5 Dec 8, 2022
A new approach to Container-Based Dependency Injection for Swift and SwiftUI.

A new approach to Container-Based Dependency Injection for Swift and SwiftUI. Why do something new? Resolver was my first Dependency Injection system.

Michael Long 573 Jan 2, 2023
Container is a lightweight dependency injection framework for Swift.

Container Container is a lightweight dependency injection framework for Swift. Installation is available in the Swift Package Manager. Swift Package M

Aleksei Artemev 17 Oct 13, 2022
Deli is an easy-to-use Dependency Injection Container that creates DI containers

Deli is an easy-to-use Dependency Injection Container that creates DI containers with all required registrations and corresponding factories.

Jungwon An 134 Aug 10, 2022
Dip is a simple Dependency Injection Container.

Dip is a simple Dependency Injection Container. It's aimed to be as simple as possible yet p

Olivier Halligon 949 Jan 3, 2023
A `CodingKey` for any encoding or decoding container.

AnyCodingKey A CodingKey for any encoding or decoding container. Overview AnyCodingKey provides a wrapper for a String (and optionally an Int), and co

null 2 Sep 10, 2022
A micro-framework that leverages Swift Property Wrappers to implement the Service Locator pattern

Locatable Context Locatable is a Swift micro framework that leverages Property Wrappers to implement the Service Locator pattern, through a custom att

Vincent Pradeilles 116 Jan 9, 2022
Guise - An elegant, flexible, type-safe dependency resolution framework for Swift

Guise is an elegant, flexible, type-safe dependency resolution framework for Swift. Flexible dependency resolution, with optional caching Elegant, str

null 52 Oct 3, 2022
An elegant, flexible, type-safe dependency resolution framework for Swift

Guise is an elegant, flexible, type-safe dependency resolution framework for Swift. Flexible dependency resolution, with optional caching Elegant, str

null 52 Oct 3, 2022
compiler-driven, structured, type-safe source generation. never use gyb again!

factory 2022-09-10-a factory is a structured, type-safe source generation tool. It is intended to be a replacement for (and improvement over) the gyb

taylorswift 14 Dec 8, 2022