Generating Random Numbers in Swift

Photo of Piotr Sochalewski

Piotr Sochalewski

Updated Dec 6, 2021 • 6 min read
numbers

Generating (pseudo)random numbers is pretty easy, but there are a few nuances that everyone should know. Generating cryptographically secure random numbers is a bit more difficult, but I'd like to present it too. Let's dive into this random blog post.

Manny happy returns, C

The most common functions that allow generating random numbers are arc4random() and arc4random_uniform(). Both come from C and are pretty simple in usage. Though arc4random() is not recommended, because it suffers from a condition called modulo bias (some numbers appear more often than others).

Int(arc4random_uniform(10)) // returns random Int between 0 and 9
Int(arc4random_uniform(10)) + 1 // returns random Int between 1 and 10

Unfortunately arc4random_uniform() returns UInt32, so you probably need to cast it to Int, which is easier to work with.

You can simply use this function to get i.e. random element of array as well.

extension Array {
var random: Element? {
guard !isEmpty else { return nil }
return self[Int(arc4random_uniform(UInt32(count)))]
}
}

If you wish to get random Double, then you have to use drand48(), which returns floating-point number between 0.0 and 1.0.

GameplayKit

Since iOS 9 there is a cool framework called GameplayKit that also allows generating random numbers. Although the name, it works well not only in games.

import GameplayKit

let randomSource = GKRandomSource.sharedRandom()
randomSource.nextInt() // returns random Int between Int32.min and Int32.max
randomSource.nextInt(upperBound: 10) // returns random Int between 0 and 9
randomSource.nextUniform() // returns random Float between 0.0 and 1.0
randomSource.nextBool() // returns random Bool

Looks much better than the old C function, especially when it comes to readability, but in fact it uses arc4random function family to return values.

Roll a dice

I guess it's an example that always appears, but it also looks nice in GameplayKit.

// This is accessible through GKRandomDistribution.d6() as well
let 🎲 = GKRandomDistribution(forDieWithSideCount: 6)
let roll = 🎲.nextInt()

It's important to use the same distribution object's instance to get next dice roll result. If you need to roll two or more dices, then you should have an instance of GKRandomDistribution for each of them.

Shuffled distribution

GKShuffledDistribution is an interesting class that inherits from GKRandomDistribution. It shapes the distribution of random numbers so that you are less likely to get repeats.

It's called fair distribution, because every number will appear an equal number of times. The shuffled distribution makes sure not to repeat any one value until it has used all of its possible values. In a six-sided dice example, if the dice rolls a 1, then another 1 won't be generated for at least five more rolls.

let 🎲 = GKShuffledDistribution.d6()
(1...100).forEach { _ in print(🎲.nextInt()) }

// could be: 1, 5, 2, 4, 6, 3, 1, 3, 2, 6, 4, 5, 4, 3, 6, 5, 1, 2, …

Gaussian distribution

A moment ago I said that you need three GKRandomDistribution instances when you're rolling three dices. And that's true. But you can also use the Gaussian distribution.

In the example you get a sum of the result of rolling three six-sided dice.

let random = GKRandomSource()
let 🎲 = GKGaussianDistribution(randomSource: random,
                                lowestValue: 3,
                                highestValue: 18)
let roll = 🎲.nextInt()

Pseudo-random, not random

You need to remember that all the examples return pseudo-random numbers. They're fine most of the time, but are not cryptographically robust.

All of them uses a source (also known as seed). Sometimes visible, sometimes not. You can find it easily in GKGaussianDistribution initialiser above, but it exists in all GKRandomDistributions.

What it means? All generated numbers are based on the seed. When you know a seed, then you know the whole sequence.

let seed: UInt64 = 666

let notSoRandomSource1 = GKMersenneTwisterRandomSource(seed: seed)
let 🎲1 = GKRandomDistribution(randomSource: notSoRandomSource1,
                               lowestValue: 1,
                               highestValue: 6)
let first = (1...100).map { _ in 🎲1.nextInt() }

let notSoRandomSource2 = GKMersenneTwisterRandomSource(seed: seed)
let 🎲2 = GKRandomDistribution(randomSource: notSoRandomSource2,
                               lowestValue: 1,
                               highestValue: 6)
let second = (1...100).map { _ in 🎲2.nextInt() }

let areTheSame = first == second // true

Does your sequence start with 6, 2, 5, 3, 2, 3, 6, 2, 2, 5 too? That's not a coincidence.

Cryptographically secure random numbers

As I said before this one is a bit more difficult, but you should definitely use it when security matters.

If you have ever used encrypted Realm database, then probably the code below is not something new for you.

var bytes = [UInt8](repeating: 0, count: 64)
SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
let key = Data(bytes: bytes)

It makes usage of Security framework and the default random number generator from the framework. These three lines, eventually give you 64 bytes data object with random numbers inside. It's close to what we need, but how to convert this to get a number?

let bytesCount = 4
var random: UInt32 = 0
var randomBytes = [UInt8](repeating: 0, count: bytesCount)

SecRandomCopyBytes(kSecRandomDefault, bytesCount, &randomBytes)

NSData(bytes: randomBytes, length: bytesCount)
  .getBytes(&random, length: bytesCount)

print(random) // prints UInt32 random number

The code above prints random UInt32 number. You can safely initialise Int with it if you want.

Post scriptum

Swift 4.2 is going to introduce a few really cool functions, which make access to old C-style API much easier. It also adds shuffling and randomising for collections. If you want to learn more about this, please take a look at SE-0202 proposal.

Still hungry for knowledge?

If yes, then I'm pretty sure this short blog post doesn't reply to all your questions. Feel free to ask me anything, but before let me recommend you two great sources of knowledge for randomising numbers.

Photo of Piotr Sochalewski

More posts by this author

Piotr Sochalewski

Piotr's programming journey started around 2003 with simple Delphi/Pascal apps. He has loved it...
Lost with AI?  Get the most important news weekly, straight to your inbox, curated by our CEO  Subscribe to AI'm Informed

Read more on our Blog

Check out the knowledge base collected and distilled by experienced professionals.

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business