Using Swift’s built-in randomization APIs
Discover page available: The Standard LibrarySwift offers quite a few different APIs that enable us to add various randomness features to our code. For example, let’s say that we wanted to generate a random number between 0 and 99 — that can be done simply by calling the static random()
method on different numeric types, such as Int
and Double
:
let randomInt = Int.random(in: 0..<100)
let randomDouble = Double.random(in: 0..<100)
Worth noting is that when generating random Double
or Float
values (or any other non-integer type), fractional values are also included. So while the above randomInt
has 100 potential values, randomDouble
can contain any valid Double
from 0.0
to 99.99...
, giving us an amount of potential values that’s several orders of magnitude larger.
Swift’s various collections also ship with built-in randomization APIs as well. For example, here’s how we could extract a random element, or completely shuffle a collection:
// Drawing a random card from a deck
let deckOfCards: [Card] = makeDeck()
let card = deckOfCards.randomElement()
// Shuffling a list of players to randomize who gets to go first
var players: [Player] = loadPlayers()
players.shuffle()
Note that randomElement
returns an optional, since we might be calling it on an empty collection.
One thing to keep in mind when adding randomization to a piece of code is how doing so might impact our ability to test that code. When writing unit tests, we’d ideally like to control the entire environment that our code is executed in, as to not cause flakiness (when tests start to sporadically fail) — and randomness is kind of the exact opposite of that sort of control.
To work around that problem, we could use function-based dependency injection to inject the randomization functions that we’re using into the types that we call them from. That way we’ll later be able to override those functions with predictable stubs within our tests.
For example, here we’re using that form of dependency injection to inject a Randomizer
function into an AudioPlayer
:
class AudioPlayer {
typealias Randomizer = (Range<Int>) -> Int
private let playlist: Playlist
private let randomizer: Randomizer
init(playlist: Playlist,
randomizer: @escaping Randomizer = Int.random) {
self.playlist = playlist
self.randomizer = randomizer
}
func playRandomSong() {
let index = randomizer(0..<playlist.songs.count)
playSong(atIndex: index)
}
...
}