TANASCHITA.COM
Articles about Swift and iOS Development by Natascha Fadeeva

How to solve problems with bitwise operators in Swift

Learn to use the power of bitwise operations and option sets in iOS.

Image of the author.
Published 14. December 2020 - 6 min read
A dog made of ones and zeros
Solving problems with bits

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.

Visualized check boxes
Check boxes for weekday selection

The logic behind the integer value is simple when we imagine that each weekday is represented by it's 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.

Checkboxes selection for value 85 (1010101)

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 the bit on the 6th position & check if it is 0 or 1
tueCheckbox.isChecked = // get the bit on the 5th position & check if it is 0 or 1
wedCheckbox.isChecked = // get the bit on the 4th position & check if it is 0 or 1
thuCheckbox.isChecked = // get the bit on the 3th position & check if it is 0 or 1
friCheckbox.isChecked = // get the bit on the 2th position & check if it is 0 or 1
satCheckbox.isChecked = // get the bit on the 1th position & check if it is 0 or 1
sunCheckbox.isChecked = // get the bit on the 0th position & check if it is 0 or 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 // The bitwise OR operator produces 0b00000111 
0b00000011 & 0b00000101 // The bitwise AND operator produces 0b00000001
0b00000010 << 1 // The bitwise LEFT SHIFT operator << produces 0b0000100
0b00000010 >> 1 // The bitwise RIGHT SHIFT operator >> produces 0b0000001
  • the OR operator | produces a 0 only if both bits are a 0. Otherwise it produces a 1.
  • the AND operator & produces a 1 only if both bits are a 1. Otherwise it produces a 0.
  • 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.
  • 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 simply 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.

Of course, an OptionSet offers a lot more useful methods. You can check them out in the official documentation.

Written by

Image of the author.
Natascha Fadeeva
Author and creator of this site

Contact