# Swift Bitwise and Shift Operators: &, |, ^, ~, <<, >>

Source: https://devdocs10.com/articles/swift/swift-bitwise-shift  
By Murat Bo · Published 2026-05-01 · DevDocs10 (https://devdocs10.com)

Swift bitwise and shift operators let you work directly with the individual bits inside integer values. This is useful for flags, masks, binary protocols, permissions, graphics, and performance-sensitive code. In this tutorial, you will learn what the operators `&`, `|`, `^`, `~`, `<<`, and `>>` do in Swift 5+, how signed and unsigned integers affect results, how to avoid common mistakes, and how to apply these operators in realistic code.

## 1. What Is Swift Bitwise and Shift Operations?

Bitwise operators compare or change numbers at the bit level. Instead of treating an integer like a single decimal value such as `42`, they work with its binary form such as `00101010`. Shift operators move bits left or right.

These operators are available for Swift integer types such as `Int`, `UInt8`, `Int16`, and similar fixed-width integers.

- `&` performs a bitwise AND and keeps only bits that are set in both values.
- `|` performs a bitwise OR and sets bits that appear in either value.
- `^` performs a bitwise XOR and sets bits that differ.
- `~` flips every bit in a value.
- `<<` shifts bits left.
- `>>` shifts bits right.

> Bitwise code is usually easiest to understand when you think in binary, even if your program later displays decimal numbers.

## 2. Why Bitwise and Shift Operators Matter

Many beginners write code for normal arithmetic for years without using bitwise operators. That is fine, because not every project needs them. But when you do need them, they are often the clearest and most efficient tool.

You should reach for bitwise and shift operators when you need compact storage, low-level data handling, flag combinations, protocol parsing, or direct control over bits. For example, a permissions system can store multiple true or false settings inside one integer.

You should not use bitwise code when a regular Boolean property, enum, array, or dictionary is clearer and the compact binary representation adds no value. Bitwise code can become hard to read if used only to look clever.

- Use bitwise operators for flags, masks, packed values, and binary data.
- Use shifts when multiplying or dividing by powers of two in binary-oriented logic.
- Avoid bitwise code when it makes business logic harder to understand.

## 3. Basic Syntax

Before writing more advanced examples, it helps to see the minimal syntax. The examples below use fixed-width integer types so the bit patterns are easier to reason about.

### Bitwise AND, OR, and XOR

In this example, two `UInt8` values are combined in three different ways. Each operator compares matching bit positions.

```swift
let a: UInt8 = 0b10101100  // 172
let b: UInt8 = 0b00111100  // 60

let andResult = a & b
let orResult = a | b
let xorResult = a ^ b

print(andResult) // 44  -> 00101100
print(orResult)  // 188 -> 10111100
print(xorResult) // 144 -> 10010000
```

Output (tested with Swift 6.4):

```text
44
188
144
```

Here, `&` keeps only shared 1 bits, `|` combines all 1 bits, and `^` keeps only the positions where the bits differ.

### Bitwise NOT

The `~` operator inverts every bit. Because fixed-width integers have a limited number of bits, the result depends on the type width.

```swift
let value: UInt8 = 0b00001111
let inverted = ~value

print(inverted) // 240 -> 11110000
```

Output (tested with Swift 6.4):

```text
240
```

This flips 0 bits to 1 and 1 bits to 0. Since `UInt8` is 8 bits wide, all 8 positions are inverted.

### Left Shift and Right Shift

Shift operators move all bits left or right by a certain number of positions.

```swift
let number: UInt8 = 0b00001111

let leftShifted = number << 2
let rightShifted = number >> 2

print(leftShifted)  // 60 -> 00111100
print(rightShifted) // 3  -> 00000011
```

Output (tested with Swift 6.4):

```text
60
3
```

A left shift moves bits toward more significant positions. A right shift moves them toward less significant positions. For unsigned integers, shifted-in bits become 0.

**Warning:** Shifts are not just visual movement. They change the numeric value, and the result depends on the integer type and whether it is signed or unsigned.

## 4. Step-by-Step Examples

### Example 1: Use AND to check whether a flag is enabled

A common bitwise task is checking whether a specific flag exists inside a combined set of flags. Each flag is usually a power of two so it uses exactly one bit.

```swift
let canRead: UInt8 = 0b00000001
let canWrite: UInt8 = 0b00000010
let canDelete: UInt8 = 0b00000100

let userPermissions = canRead | canDelete

let hasWritePermission = (userPermissions & canWrite) != 0
let hasDeletePermission = (userPermissions & canDelete) != 0

print(hasWritePermission)  // false
print(hasDeletePermission) // true
```

Output (tested with Swift 6.4):

```text
false
true
```

The AND operation isolates one bit. If the result is not zero, that flag was present in the original value.

### Example 2: Use OR to combine multiple options

Bitwise OR is often used to turn bits on. This is how you build a value that contains many independent settings.

```swift
let bold: UInt8 = 0b00000001
let italic: UInt8 = 0b00000010
let underline: UInt8 = 0b00000100

let style = bold | underline

print(style) // 5 -> 00000101
```

Output (tested with Swift 6.4):

```text
5
```

The result contains both the `bold` bit and the `underline` bit. OR is ideal when you need to merge multiple one-bit options into one value.

### Example 3: Use XOR to toggle bits

XOR is useful for toggling a bit. If a bit is 1, XOR with 1 turns it to 0. If a bit is 0, XOR with 1 turns it to 1.

```swift
let darkModeFlag: UInt8 = 0b00000001
var settings: UInt8 = 0b00000000

settings = settings ^ darkModeFlag
print(settings) // 1

settings = settings ^ darkModeFlag
print(settings) // 0
```

Output (tested with Swift 6.4):

```text
1
0
```

This pattern is helpful when you want a compact on/off toggle inside a larger integer.

### Example 4: Use shifts to extract packed values

Suppose one byte stores red, green, and blue information in different bit positions. A shift followed by a mask can extract each piece.

```swift
let packed: UInt8 = 0b10110110

// Top 3 bits
let red = (packed >> 5) & 0b00000111

// Middle 3 bits
let green = (packed >> 2) & 0b00000111

// Bottom 2 bits
let blue = packed & 0b00000011

print(red)   // 5
print(green) // 5
print(blue)  // 2
```

Output (tested with Swift 6.4):

```text
5
5
2
```

This is a very common bitwise pattern: shift the target bits into position, then apply a mask with AND.

### Example 5: Signed right shift behaves differently

Right shifts on signed integers preserve the sign in Swift. This is called an arithmetic shift. It can surprise developers who expect zeros to be inserted on the left.

```swift
let positive: Int8 = 4
let negative: Int8 = -4

print(positive >> 1) // 2
print(negative >> 1) // -2
```

Output (tested with Swift 6.4):

```text
2
-2
```

The negative value remains negative after shifting because the sign bit is preserved. This matters when you process signed data at the bit level.

## 5. Practical Use Cases

- Represent user permissions such as read, write, and delete in one integer field.
- Parse binary file formats or network packet headers where each bit or group of bits has a defined meaning.
- Pack multiple small values into one integer to save memory or match an external protocol.
- Manipulate color channels, pixel formats, or low-level graphics data.
- Toggle feature flags efficiently when many on or off options need compact storage.
- Build masks that extract or clear selected bits in embedded-style or systems-oriented Swift code.

## 6. Common Mistakes

### Mistake 1: Using decimal-looking values instead of bit masks

Beginners sometimes choose random numbers for flags instead of powers of two. That causes flags to overlap.

**Warning:** Bad code: overlapping flags cannot be checked reliably.

```swift
// Bad: these values overlap in binary
let read: UInt8 = 1
let write: UInt8 = 2
let delete: UInt8 = 3
```

Here, `3` is `0b00000011`, which overlaps with the first two flags.

```swift
// Good: each flag uses a unique bit
let read: UInt8 = 0b00000001
let write: UInt8 = 0b00000010
let delete: UInt8 = 0b00000100
```

Each corrected flag is a power of two, so each one occupies exactly one bit.

### Mistake 2: Checking flags with OR instead of AND

To test whether a flag is present, some developers use OR. That does not isolate the existing bit.

**Warning:** Bad code: OR adds bits and does not answer whether a flag was already set.

```swift
let canEdit: UInt8 = 0b00000010
let permissions: UInt8 = 0b00000001

// Bad
let hasEdit = (permissions | canEdit) != 0
```

This returns true even when the original bit was not present, because OR creates a value with the bit turned on.

```swift
// Good
let hasEdit = (permissions & canEdit) != 0
```

AND is correct because it preserves only the bits shared by both values.

### Mistake 3: Ignoring signed versus unsigned shifts

Signed right shifts can surprise you because the sign bit is preserved.

**Warning:** Bad code: assuming signed right shift always inserts zero bits.

```swift
// Bad assumption
let value: Int8 = -8
let shifted = value >> 1
print(shifted) // -4, not a zero-filled positive result
```

Output (tested with Swift 6.4):

```text
-4
```

If you need zero-fill behavior, use an unsigned integer type when appropriate.

```swift
// Good
let value: UInt8 = 0b11111000
let shifted = value >> 1
print(shifted) // 124 -> 01111100
```

Output (tested with Swift 6.4):

```text
124
```

Choosing the correct integer type is part of writing correct bitwise code.

### Mistake 4: Forgetting type width when using NOT

The result of `~` depends on the width of the integer type. Beginners often expect a simple mathematical negative.

**Warning:** Bad code: assuming bitwise inversion ignores integer size.

```swift
// Bad expectation
let small: UInt8 = 15
let inverted = ~small
print(inverted) // 240, because UInt8 has 8 bits
```

Output (tested with Swift 6.4):

```text
240
```

The result is not based on unlimited binary digits. It is based on the fixed width of `UInt8`.

```swift
// Good: choose the type intentionally
let small: UInt8 = 15
let mask: UInt8 = 0b11111111
let inverted = small ^ mask
print(inverted) // 240
```

Output (tested with Swift 6.4):

```text
240
```

This makes the fixed-width inversion idea more explicit and easier to reason about.

## 7. Best Practices

### Use named masks instead of raw numbers

Bitwise code becomes hard to maintain when numbers like `4` or `32` appear without context. Named constants make your intent clear.

```swift
let isAdmin: UInt8 = 0b00000001
let isModerator: UInt8 = 0b00000010

let roles: UInt8 = isAdmin | isModerator
let canAccessAdminPanel = (roles & isAdmin) != 0
```

This is easier to understand than checking for unexplained numeric values.

### Choose fixed-width integer types for binary work

When your logic depends on exact bit size, prefer types like `UInt8`, `UInt16`, or `UInt32`. The default `Int` depends on platform word size.

```swift
// Better for protocol fields or packed bytes
let header: UInt16 = 0b1011000011110000
let version = (header >> 12) & 0b1111
```

This avoids ambiguity and gives you stable behavior across environments.

### Separate masking and shifting into readable steps

One long bitwise expression may be correct but hard to review. Breaking it into steps helps other developers and reduces bugs.

```swift
let packet: UInt16 = 0b1010101111001101

let statusMask: UInt16 = 0b0000000011110000
let maskedStatus = packet & statusMask
let status = maskedStatus >> 4

print(status)
```

Output (tested with Swift 6.4):

```text
12
```

This makes debugging much easier than squeezing everything into a single line.

### Document the bit layout near the code

Bitwise operations are much safer when you write down what each bit means. A short comment can prevent major confusion later.

```swift
// Byte layout: 7 6 5 | 4 3 2 | 1 0
//              red   green   blue
let pixel: UInt8 = 0b10110110
let red = (pixel >> 5) & 0b111
```

The code is the same, but the meaning is much clearer. In bitwise programming, comments often carry essential context.

## 8. Limitations and Edge Cases

- Bitwise and shift operators work with integer types, not floating-point types like `Double` or `Float`.
- The result of `~` depends on the width and signedness of the integer type being used.
- Right shift on signed integers preserves the sign bit, which can produce negative results that surprise beginners.
- Using `Int` can make bit-width assumptions less explicit because its size follows the platform word size.
- Shift counts should be chosen carefully. In practice, your code should only shift by values that make sense for the target type width.
- Bitwise code is compact but often less readable than higher-level logic, so it can increase maintenance cost if overused.

## 9. Practical Mini Project

Let us build a small permission manager using bitwise operators. The project will define several permissions, combine them into one value, and provide functions to add, remove, toggle, and check permissions. This is a realistic use of `&`, `|`, `^`, and `~`.

```swift
struct Permissions {
    static let read: UInt8 = 0b00000001
    static let write: UInt8 = 0b00000010
    static let delete: UInt8 = 0b00000100
    static let publish: UInt8 = 0b00001000
}

struct UserAccount {
    var permissionBits: UInt8 = 0
    
    mutating func grant(_ permission: UInt8) {
        permissionBits = permissionBits | permission
    }
    
    mutating func revoke(_ permission: UInt8) {
        permissionBits = permissionBits & ~permission
    }
    
    mutating func toggle(_ permission: UInt8) {
        permissionBits = permissionBits ^ permission
    }
    
    func has(_ permission: UInt8) -> Bool {
        return (permissionBits & permission) != 0
    }
}

var account = UserAccount()

account.grant(Permissions.read)
account.grant(Permissions.write)
print(account.has(Permissions.read))    // true
print(account.has(Permissions.delete))  // false

account.toggle(Permissions.delete)
print(account.has(Permissions.delete))  // true

account.revoke(Permissions.write)
print(account.has(Permissions.write))   // false

print(account.permissionBits) // final packed permission value
```

Output (tested with Swift 6.4):

```text
true
false
true
false
5
```

This mini project demonstrates several key bitwise patterns. OR grants a permission by setting a bit, AND with NOT removes a permission by clearing a bit, XOR toggles a permission, and AND checks whether a specific permission exists. These are the same operations you will see in many production systems that store flags compactly.

## 10. Key Points

- `&` keeps only the bits that are 1 in both operands.
- `|` combines bits and is commonly used to set flags.
- `^` keeps differing bits and is useful for toggling flags.
- `~` inverts every bit in the chosen integer type.
- `<<` and `>>` move bits left and right.
- Fixed-width integer types like `UInt8` make bitwise logic easier to reason about.
- Signed right shifts preserve the sign bit, so negative values need extra care.
- Bit masks should use powers of two so each flag occupies exactly one bit.

## 11. Practice Exercise

Try building a tiny settings system with bitwise flags.

- Create three settings: `sound`, `music`, and `notifications`.
- Store them inside one `UInt8` value.
- Turn on `sound` and `notifications`.
- Check whether `music` is enabled.
- Then toggle `music` on.
- Expected output: first print `false`, then print `true`, then print the final packed numeric value.
- Hint: use `|` to set flags, `&` to test them, and `^` to toggle one.

```swift
let sound: UInt8 = 0b00000001
let music: UInt8 = 0b00000010
let notifications: UInt8 = 0b00000100

var settings: UInt8 = 0

// Turn on sound and notifications
settings = settings | sound
settings = settings | notifications

// Check music before toggling
let hasMusicBefore = (settings & music) != 0
print(hasMusicBefore) // false

// Toggle music on
settings = settings ^ music

// Check music after toggling
let hasMusicAfter = (settings & music) != 0
print(hasMusicAfter) // true

print(settings) // 7
```

Output (tested with Swift 6.4):

```text
false
true
7
```

## 12. Final Summary

Swift bitwise and shift operators give you direct control over the binary representation of integer values. In this tutorial, you learned what `&`, `|`, `^`, `~`, `<<`, and `>>` do, how to use them with masks and flags, how shifts help with packed data, and why signed versus unsigned integers matter.

You also saw how these operators appear in real programming tasks such as permission systems and binary parsing. Although bitwise code is more specialized than everyday Swift syntax, it becomes much easier once you think in terms of individual bits and use clear masks, comments, and fixed-width types.

If you want to keep improving, practice by packing and unpacking small values, building your own flag types, and reading binary examples carefully. Once these patterns feel natural, many low-level Swift tasks will become much more approachable.
