How to solve problems with bitwise operators in Swift
By: Natascha Fadeeva
Tags: Bitwise Operations, Swift, Swift Programming, Problem Solving, iOS App Development

How to solve problems with bitwise operators in Swift

Recently, I had to solve the following task at work.

The server sends an integer value between 0 and 127 that represents the state of a weekdays selection. When the server sends a 0 , no checkbox should be selected. For a 127 , all check boxes should be selected. The numbers in between represent all the other states. The task was to evaluate this value to be able to preselect the appropriate checkboxes.

The logic behind the integer value is simple when we imagine that each weekday is represented by its bit value:

mon tue wed thu fri sat sun

0000000 - equals 0 - no weekday is selected

0000011 - equals 3 - sat sun are selected

1010101 - equals 85 - mon wed fri sun are selected

1111100 - equals 124 - mon tue wed thu fri are selected

1111111 - equals 127 - all days are selected

With this pattern, we are able to represent every state of the seven check boxes with numbers ranging from 0 to 127.

So, how do we convert the number send by the server into the correct checkboxes state?

The trivial solution

The first approach that might come to our mind could look like this:

switch weekdaysSelectionValue {

case 0:

monCheckbox.isChecked = false

tueCheckbox.isChecked = false

wedCheckbox.isChecked = false

thuCheckbox.isChecked = false

friCheckbox.isChecked = false

satCheckbox.isChecked = false

sunCheckbox.isChecked = false

case 1:

monCheckbox.isChecked = false

tueCheckbox.isChecked = false

wedCheckbox.isChecked = false

thuCheckbox.isChecked = false

friCheckbox.isChecked = false

satCheckbox.isChecked = false

sunCheckbox.isChecked = true

.

.

.

case 127:

monCheckbox.isChecked = true

tueCheckbox.isChecked = true

wedCheckbox.isChecked = true

thuCheckbox.isChecked = true

friCheckbox.isChecked = true

satCheckbox.isChecked = true

sunCheckbox.isChecked = true

}

Having 127 switch cases is a lot of fun, but let’s look into a more generic approach to solve this with bitwise operators.

monCheckbox.isChecked = // get bit on position 6 & check if it's 1

tueCheckbox.isChecked = // get bit on position 5 & check if it's 1

wedCheckbox.isChecked = // get bit on position 4 & check if it's 1

thuCheckbox.isChecked = // get bit on position 3 & check if it's 1

friCheckbox.isChecked = // get bit on position 2 & check if it's 1

satCheckbox.isChecked = // get bit on position 1 & check if it's 1

sunCheckbox.isChecked = // get bit on position 0 & check if it's 1

To implement the solution above, let’s look into how to work with bits in Swift.

Basics about bits in Swift

A bit is the smallest piece of information that can be 1 or 0. Eight bits together are called a byte. The unsigned UInt8 type in Swift is represented by 8 bits, that means we can represent 2⁸ = 256 different numbers with UInt8 which is enough for us since we only need 127 numbers.

UInt8 0 = 00000000 = 0 * 2^0

1 = 00000001 = 1 * 2^0

2 = 00000010 = 0 * 2^0 + 1 * 2^1

3 = 00000011 = 1 * 2^0 + 1 * 2^1

...

6 = 00000110 = 1 * 2^0 + 1 * 2^1 + 1 * 2^2

...

255 = 11111111 = 1 * 2^0 + 1 * 2^1 + 1 * 2^2 + 1 * 2^3 + 1 * 2^4 + 1 * 2^5 + 1 * 2^6

A signed Int8 type also represents 256 different numbers, but ranging from -128 to 127, since the most left bit is used to signify whether the integer is positive or negative. So theoretically, we could also use an Int8 for our task.

In Swift, when defining a number, we can choose to write down it’s bit representation:

let value1: UInt8 = 3

let value2: UInt8 = 0b00000011

In the example above, value1 and value2 are the same values, they are just written down differently. We could use basic operators on them like we are used to:

let value3 = 3 + 0b00000011 // 6

let value4 = 3 - 0b00000011 // 0

Bitwise shifting and bitwise operators in Swift

Additionally to basic operators, Swift offers advanced operators for bit manipulations.

0b00000011 | 0b00000101 // Bitwise OR operator produces 0b00000111

0b00000011 & 0b00000101 // Bitwise AND operator produces 0b00000001

0b00000010 << 1 // Bitwise LEFT SHIFT operator produces 0b0000100

0b00000010 >> 1 // Bitwise RIGHT SHIFT operator produces 0b0000001

the OR operator | produces a 0 only if both bits are a 0 . Otherwise it produces a 1 .

operator produces a only if both bits are a . Otherwise it produces a . the AND operator & produces a 1 only if both bits are a 1 . Otherwise it produces a 0 .

operator produces a only if both bits are a . Otherwise it produces a . the LEFT SHIFT operator << shifts every bit to the left by a certain number of places. Any bits that are moved beyond the bounds are discarded.

operator shifts every bit to the left by a certain number of places. Any bits that are moved beyond the bounds are discarded. the RIGHT SHIFT operator >> does the same as the LEFT SHIFT operator in the other direction.

We can solve our task with the operators above. If you are interested in more of those operators, you can check out the official documentation.

The solution with bitwise operators

With the help of those operators, we now can do the following.

monCheckbox.isChecked = ((serverValue & 0b01000000) >> 6) == 1

tueCheckbox.isChecked = ((serverValue & 0b00100000) >> 5) == 1

wedCheckbox.isChecked = ((serverValue & 0b00010000) >> 4) == 1

thuCheckbox.isChecked = ((serverValue & 0b00001000) >> 3) == 1

friCheckbox.isChecked = ((serverValue & 0b00000100) >> 2) == 1

satCheckbox.isChecked = ((serverValue & 0b00000010) >> 1) == 1

sunCheckbox.isChecked = ((serverValue & 0b00000001) >> 0) == 1

What exactly is happening here? Let’s pick one value to illustrate this.

1. Imagine, we get a 85 from the server. That is a 0b01010101 as bit representation.

2. Now, we want to know if the Friday’s checkbox is checked. For that, we nullify all bits instead of the one on position 2 by using the AND operator.

0b01010101 & 0b00000100 // produces 0b000000100

3. Next, we want to know if the value on the second position is a 1 or a 0 . For that, we shift 2 positions to the left, so the value is now at position 0 .

0b000000100 >> 2 // produces 0b000000001

4. Finally, we can check if our produced value is a 0 or a 1 with the basic == operator.

0b000000001 == 1 // is true

Actually, we can even solve this without shifting. Instead of shifting to the right and checking if the value is 1 , we can check if our value is not 0 :

friCheckbox.isChecked = serverValue & 0b00000100 != 0

The solution with OptionSet

Now that we understand how to solve the task with bitwise operators, there is even a simpler solution. The Swift standard library offers the OptionSet that represents bit set types, where every bit represents a member of the set.

struct WeekdayOptions: OptionSet {

let rawValue: Int static let sun = WeekdayOptions(rawValue: 1 << 0)

static let sat = WeekdayOptions(rawValue: 1 << 1)

static let fri = WeekdayOptions(rawValue: 1 << 2)

static let thu = WeekdayOptions(rawValue: 1 << 3)

static let wed = WeekdayOptions(rawValue: 1 << 4)

static let tue = WeekdayOptions(rawValue: 1 << 5)

static let mon = WeekdayOptions(rawValue: 1 << 6)

}

After defining our weekday options as an OptionSet , we get all the logic behind it for free. To check if Monday is selected, we now can simply use the OptionSet 's contains method.

let weekdayOptions = WeekdayOptions(rawValue: serverValue)

monCheckbox.isSelected = weekdayOptions.contains(.mon)

Even though an OptionSet makes it easy, knowing the bit operators and how it works behind the scenes makes it's usage more clear.