Lesson 3 - RollingDie in Swift - Constructors and Random numbers
In the previous lesson, First object-oriented app in Swift, we programmed our first object-oriented application in Swift.
We are already able to create new classes with fields and parameterized methods with return values. Today, we're going to start working on an arena in which two warriors will battle against each other. The battle will be turn-based and one warrior will decrease the health points of other one based on his attack and on the other warrior's defense. Essentially, we'll be simulating a board game, so we'll add a rolling "die" to our game so as to add a bit of randomness. We're also going to learn to declare custom constructors.
We'll start by creating a new Command Line Tool application and naming it
ArenaFight
. We'll add a new class
to our project and
name it RollingDie
. Let's think about the fields we'll have to add
to our die in order for it to function according to our current needs. We should
be able to choose the number of sides. Typically 6 or 10 sides as is standard in
this type of game. Our die will also need a random number generator. We are
provided with this generator by Swift which provides a special function for
these purposes. So our class will contain only the sidesCount
field
for now which we'll set to 6
by default. That way we won't need to
deal with an Optional
or write a constructor.
Last time, for simplicity's sake, we set all the fields of our class as
publicly accessible; however, in most cases, we don't want our fields to be
modified externally. In this case, we will use the private
modifier. A field is from then on only accessible from the inside of the class
and from the outside Swift treats it like it doesn't exist. When we design a
class we usually set everything to private
and then we make
internal
(just remove private
to make it visible for
the whole module) only those class members we really need to expose. Our class
should now look something like this:
class RollingDie { private var sidesCount = 6 }
Сonstructors
We would now only be able to set the value of the public
fields
from outside of the class since the private
fields are not visible
from the outside. We've already discussed constructors a bit. A constructor is a
method that is called while an object instance is being
created. We use it to set the internal state of the object and perform
any necessary initializations. We'd create a die in the main.swift
now like this:
let rollingDie = RollingDie()
The RollingDie()
method is the constructor. Since our class
doesn't have a constructor, Swift automatically generated an empty method.
However, we'll now add a constructor to the RollingDie
class. We
declare it as a method, but it doesn't have a return type and
it must be named init()
. In the constructor, we'll
set the number of sides to a fixed value. This way, we don't have to specify it
directly in the declaration, but we need to assign a data type. The
RollingDie
class will look like this:
private var sidesCount : Int init() { sidesCount = 6 }
If we create the die now, it'll have the sidesCount
field set to
6
. We'll print the number of sides to the console, so as to
visually confirm that the value is there. It's not a good idea to set the field
as public
/internal
since we don't want somebody to be
able to change the number of sides once the die is created.
We'll add a new getSidesCount()
method to the class which
returns the value of the sidesCount
field. That way we basically
made the field ready-only (the field isn't visible and the only way to ready it
is through this method; it can't be changed from outside the class). Swift
offers some more constructs for these purposes, but let's not bother with them
now. The new method will look like this:
func getSidesCount() -> Int {
return sidesCount
}
Let's move on to main.swift
so we could create our first die and
print the number of its sides to the console:
let rollingDie = RollingDie() // at this moment the constructor is called
print(rollingDie.getSidesCount())
}
The output:
6
Now we have visual confirmation that the constructor had been called. However, we'd like to be able to specify how many sides our die will have. Let's add a parameter to our constructor.
init(aSidesCount: Int) { sidesCount = aSidesCount }
Notice that we prefixed the parameter name with a
otherwise it
would have the same name as the field and cause an error. Let's go back to
main.swift
and pass a value for this parameter in the
constructor:
let rollingDie = RollingDie(aSidesCount: 10)
print(rollingDie.getSidesCount())
The output:
10
Everything works as expected. Swift will not generate an empty, aka parameterless, constructor now, so we can't create a die without passing the parameter it needs. We can make it possible by adding another constructor, the parameterless one this time. We'll set the number of sides to 6 in it since the user probably expects this value as default:
init() {
sidesCount = 6
}
Let's create 2 dice instances now, one with a parameter and one without (in
main.swift
):
let sixSided = RollingDie()
let tenSided = RollingDie(aSidesCount: 10)
print(sixSided.getSidesCount())
print(tenSided.getSidesCount())
The output:
6 10
Swift doesn't mind us having two methods of the same name as long as their
parameters are different. We say that the init()
method (the
constructor) is overloaded. This works for all methods with
same names and different parameters, not just constructors. Xcode offers
overloaded versions of methods when we type a left bracket. We can scroll
through the method variants with the arrow keys. We can see our two constructor
overloads on the list:

Many methods in Swift libraries have several overloads. Take a look at the
append()
method on a String
. It's good to look through
method overloads, so you don't end up programming something what has already
been done for you.
Now, let's go over how to name constructor parameters better than
aSidesCount
in our case. If we named them the same as the fields
they initialize, we would cause an error:
init(sidesCount: Int) sidesCount = sidesCount }
Swift can't tell the two apart. In this case, the parameter would be assigned
to the parameter again. Xcode will even alert us of this fact. We can refer to
the current instance inside a class because it's stored in the self
variable. We would use self
e.g. if our die had a
giveToPlayer(player: Player)
method, calling
player.pickUpDie(self)
inside. We'd essentially be passing itself,
the concrete die instance with which we're working, to the other player by means
of the self
keyword. We won't deal with any of the "player" stuff
for now, but we'll set things up to reference the current instance while setting
the field:
init(sidesCount: Int) { self.sidesCount = sidesCount }
Using self
, we specified that the left variable,
sidesCount
, belongs to the instance. The right one is treated as a
parameter by Swift. Now we have two constructors that allow us to create
different dice. Let's continue.
Random numbers
Next, we'll declare a roll()
method in the
RollingDie
class, which will return a random number from
1
to the number of sides. The method will be internal
,
meaning that it can be called from outside the class, and will have no
parameters. The return value will be of the Int
type. We can obtain
a random number using not exactly a pretty function,
arc4random_uniform()
, which takes the exclusive upper bound of the
generated number. That means it'll generate a random number from 0 to the
specified value - 1. Since the data type of the parameter is
UInt32
, we have to convert it. We'll also add one to the result to
get the 1-6 interval, because the lower bound is zero and we can't roll a zero
on a die
The roll()
method will look like this:
func roll() -> Int { return Int(arc4random_uniform(UInt32(sidesCount))) + 1 }
Printing information about the RollingDie class instance
Our RollingDie
class is almost ready, let's add one more field
before we wrap up this class. Swift uses the description
field to
return a textual representation of the instance. It's handy in
all cases where we need to print the instance or work with it as with text. Even
numerical data types have this field and Swift goes for this field when we use
the variable in an interpolated String
or in the
print()
method. When creating a class, we should consider whether
description
will come in handy. We should never make our own
method, such as print()
, when there is already a way to handle
string conversions in Swift. It makes no sense to use it in our die, but when
implementing warriors it could return their names. Still, for education's sake,
we'll add the description
field to our RollingDie
class anyway. It'll say that it's a die and contain how many sides it has.
First, let's try to print a RollingDie instance directly to the console:
let sixSided = RollingDie()
let tenSided = RollingDie(sidesCount: 10)
print(sixSided)
Only the path to our class is printed to the console, which is
ArenaFight.RollingDie
. Now let's have a look at how to declare the
description
field. The code is quite complex for us now so don't
think about it much, everything will be explained in the following lessons
class RollingDie : CustomStringConvertible { var description: String { return "A rolling die with \(sidesCount) sides" } // ... the rest of the RollingDie class } let sixSided = RollingDie() let tenSided = RollingDie(sidesCount: 10) print(sixSided)
We implemented description
as a special read-only property. In
the class definition, we can see : CustomStringConvertible
. That's
a protocol specifying what the class can do. We'll explain protocols in one of
the following lessons.
Let's print out the die instance to the console again.
The output:
A rolling die with 6 sides
Let's test our rolling dice now. We'll roll them in loops and see if they work as expected:
let sixSided = RollingDie() let tenSided = RollingDie(sidesCount: 10) // rolls the 6-sided die print(sixSided) for _ in 1...10 { print(sixSided.roll(), terminator: " ") } // rolls the 10-sided die print("\n\n \(tenSided)") for _ in 1...10 { print(tenSided.roll(), terminator: " ") }
The output should look something like this:
A rolling die with 6 sides 3 6 6 1 6 3 6 2 6 3 A rolling die with 10 sides 5 9 9 2 10 4 9 3 10 5
We've created a nice, customizable class that represents a rolling die. It'll come in handy when we get to the arena part of the application, but you can use it whenever you'd like. Hopefully, you now understand how OOP allows you to reuse components.
In the next lesson, Reference and value data types in Swift, we'll talk about the differences between
reference data types (objects) and value data types (e.g. Int
).