Week 2 - Classes and objects

Goals

Preparation

In this lab you'll be implementing a Fraction class, adding and modifying code as you progress through. Whenever you're instructed to make changes, the entire file will be shown with the part to remain unchanged faded and the new/modified code highlighted. This makes the lab script a touch longer, but it should also make it fairly clear where the new code fits within the existing program.

Fractions

In this lab you will implement a class that represents a number as a fraction, with methods implementing fraction arithmetic.

A fraction is defined by two integer values - the numerator and denominator. These two values constitute the internal state stored by the member variables of the Fraction class. The class will also provide methods for adding, subtracting, multiplying and dividing of Fraction objects.

Defining a new class

Create a new Command-Line Tool project in Xcode, call it "prog2.1". In Xcode's project navigator find the prog2.1 directory with the main.swift file inside. Next, you'll need to create a new file, where the Fraction class will be defined and implemented. In order fro the new file to appear under prog2.1 directory (alongside with main.swift) make sure to select prog2.1 folder under prog2.1 target in the project navigator. Next, from the main menu select File -> New -> File.... For the file template select OS X -> Source -> Swift File. Name the file Fraction.swift and save in the prog2.1 directory of prog2.1 project (where you should also see main.swift). The new file should appear alongside main.swift in Xcode's project navigator window.

Let's take this one step at a time. Click on the new created file and inside define a new class as written below:

001:
002:
003:
004:
005:
006:
007:
008:
009:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
}

That's all you need to define a new class. From now on, you can declare variables to be of Fraction type. For instance, you can write the following program in main.swift, which declares a variable u as Fraction.

01:
02:
03:
04:
import Foundation

// Create a new Fraction object
var u: Fraction

Aside from declaring, there's nothing you can do with this variable at this point.

Member variables

In Swift, member variables are referred to as stored properties - they store the internal state of the class. Inside the Fraction class there will be two integers corresponding to the numerator and denominator value of the fraction. Here's how to define them:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
}

Initialisation

At this point Xcode will refuse to compile, complaining that Class 'Fraction' has no initializers. Initialisers are special methods that create an object instance of given class. Swift compiler requires an initialiser in order to facilitate creation of the corresponding object.

Default initialiser

Initialisers are special methods that create object instance of a given class. Default initialiser is one that takes no arguments, creating an object instance with its internal state set to default values. Let's make those defaults num=0 and den=1. That state corresponds to a fraction representation of a number zero.

Question: Why it does not make sense to set denominator to 0 as well?

Here's how to implement our default initialiser:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // INITIALISERS
    
    /**
        Default initialiser
    
        Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
}

Note that, in Swift, initialisers are always defined with the init keyword, and they are NOT preceded by the func keyword. The self keyword is a reference to the instance of the class that's just being created (it's analogous to the this keyword in Java). It's not necessary to use self to refer to object's internal variables unless the internal variable is shadowed (i.e., a variable with the function's scope has the same name). However, to make it clear that we're referring to the internal variable, the provided code will use the self reference even if it's not necessary.

At this point the Swift compiler should happily allow us to compile and run the following program in main.swift:

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
import Foundation

// Define a new Fraction object
var u: Fraction

// Create and initialise a new object instance
u = Fraction.init()

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

The line "var u: Fraction" can be read as "Define u as a variable that is a reference to a Fraction type object". It does not create (allocate memory for) an object, just lets the compiler know that u will from now on refer to a Fraction object. The line "u = Fraction.init()" can be read as "Use Fraction's initialiser init() to create a new Fraction object, and store its reference in u". You can think of initialisers as static methods that return objects of the class they initialise. Technically, the signature of the initialiser method is func init() -> Fraction, though it doesn't get written that way. In Swift, aside from their arguments, the initialiser signatures are implicit - that's why you don't precede their definition with the func keyword and don't need to specify the return type.

The last line of the program displays the values of object's numerator and denominator. Run the program. The output should be as the one shown below. To encourage you to run your program first and try to make sense of its output before having a look at the answer, the output boxes are initially hidden - click on the down-arrow button to see its contents.

u=0/1

Individual values of Fraction object's stored properties can be changed like so:

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction.init()

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

// Change the values of object's internal variables
u.num = 2 
u.den = 3 

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

When run, the above program should output the following:

u=0/1
u=2/3

We can also create another instance of a Fraction object, initialised with different internal values:

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction.init()

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

// Change the values of object's internal variables
u.num = 2 
u.den = 3 

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction()
x.num = -5 
x.den = 1 

print("x=\(x.num)/\(x.den)")

Did you notice, that the new object was defined and instantiated on the same line? Also, note that the init keyword can be omitted when invoking an initialiser. In Swift, a class name specifies a type; a class name followed by function brackets corresponds to a call to its initialiser. And so, in this case, "var x: Fraction = Fraction()" is the same as writing "var x: Fraction = Fraction.init()".

The output of the above program should be:

u=0/1
u=2/3
x=-5/1

Designated initialisers

Currently, the fraction always gets set to 0/1 when the object gets created. To get a different numerator and denominator, we had to set them individually after initialisation. It's more efficient to provide another initialiser, where the programmer can specify the numerator and denominator values to be set when the object is created. Here's how to write an alternate initialiser:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // INITIALISERS
    
    /**
        Default initialiser
    
        Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
        Designated initialiser
     
        Numerator and denominator values are passed in the arguments of the initialiser.
     
        - parameter num: Fraction's numerator
        - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
}

In Swift you can define multiple initialisers as long as their signatures differ in the arguments. The initialiser we just added takes two arguments: the numerator and denominator value. The assert function checks the denominator value. In this case, if denominator is zero, the den != 0 condition will evaluate to false, the assertion will trigger, and the program will stop (for more info on assertions take a look at Assertions). Denominator value of 0 in Fraction class is deemed bad, because division by zero evaluates to infinity (in some sense).

Note that in this initialiser the names of the arguments are the same as the names of the stored properties num and den. The arguments are shadowing the internal variables. Hence, inside this initialiser, a reference to num is understood by the compiler as a reference to the argument, and a reference to self.num as a reference to the internal variable. The same goes for den and self.den.

The new initialiser allows us to simplify main.swift a bit:

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5,den: 1)

print("x=\(x.num)/\(x.den)")

Note that calls to initialisers must include the names of the arguments. This helps the compiler to figure out which initialiser is being referred to (remember, there can be many initialisers). The output of this program should remain unchanged.

Convenience initialisers

Designated initialiser is an initialiser that initialises all the internal variables in the class. A convenience initialiser does not set these variables directly, but needs to invoke a designated initialiser. For instance, the call Fraction(num: -5,den: 1) correspond to setting a fraction to integer value of -5. If we find that setting a fraction to an integer value occurs often enough, it might become tedious to use the initialiser where denominator is always set to 1. Hence, it might makes sense to provide an initialiser like this:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // INITIALISERS
    
    /**
        Default initialiser
    
        Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
        Designated initialiser
     
        Numerator and denominator values are passed in the arguments of the initialiser.
     
        - parameter num: Fraction's numerator
        - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
        Convenience initialiser
    
        Numerator is set to some value, denominator
        is set to 1.
    
        - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
}

The convenience keyword lets the compiler know that the initialiser will not set the stored properties directly, but through a call to another (designated) initialiser. The initialiser we just implemented takes only one argument, the numerator value, and sets the denominator always to 1 via a call to the designated initialiser.

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

// Show the state of the object's internal variables
print("u=\(u.num)/\(u.den)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.num)/\(x.den)")

Exercise:

Rewrite init() to invoke init(num: Int, den: Int) with appropriate default values. If you forget to mark the rewritten initialiser as convenience, Xcode will remind you.

The main advantage of using single designated initialiser is that you centralise your initialisation code, and get all validity checks (such as the denominator assertion in this exercise) done in once place. This makes the code a bit safer - if you create a bad convenience initialiser, that is, one that allows for illegal internal state, the designated initialiser will catch the error at runtime.

Computed properties

The num and den variables in our Fraction class are referred to as stored properties...because they store the state of the object. Swift also allows for computed properties - these are special properties that externally look like internal variables, but in fact are a setter and a getter function. For more information on computed properties take a look at Computed properties. In this exercise, we will create two read-only computed properties. Read-only computed property only implements a getter function. One property will compute the decimal value of the fraction, the other a string representation of the object:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
        Converts fraction to a Float value
     
        - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
        Converts object to a string description
     
        - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
        Default initialiser
    
        Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
        Designated initialiser
     
        Numerator and denominator values are passed in the arguments of the initialiser.
     
        - parameter num: Fraction's numerator
        - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
        Convenience initialiser
    
        Numerator is set to some value, denominator
        is set to 1.
    
        - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
}

In Swift, if there is no explicit specification of whether the computed property's code is a setter or a getter, Swift assumes it's the latter. And so, both computed properties specify their getter functions - the first one with an explicit get block, the other with an implicit get block.

The description property, which returns a string representation value of an object's state is analogous to Java's toString() method. Printing the fractions is much easier now, as it is not necessary for the programmer using a Fraction object to know how to convert object's internal state to a string. The description method gives a string representation of the object that can be used for printing to output.

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.description)")
print("decimal value of u=\(u.decimal)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.description)")
print("decimal value of x=\(x.decimal)")

If you run this program now, you'll get additional output that shows the float value of the corresponding fraction.

Methods

It's time to implement methods that do something with these fractions. Namely, we want methods that perform fraction arithmetic according to the following rules:

The code below gives an implementation of four methods for the arithmetic operations from the image above:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
        Converts fraction to a Float value
     
        - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
        Converts object to a string description
     
        - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
        Default initialiser
    
        Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
        Designated initialiser
     
        Numerator and denominator values are passed in the arguments of the initialiser.
     
        - parameter num: Fraction's numerator
        - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
        Convenience initialiser
    
        Numerator is set to some value, denominator
        is set to 1.
    
        - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
        Adds a fraction to self.
     
        - parameter f: Fraction to add to self
     
        - returns: Fraction The result of adding f to self.
     */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
        Subtracts fraction from self.
     
        - parameter f: Fraction to subtract from self
     
        - returns: Fraction The result of subtracting f from self.
     */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
        Multiplies self by a fraction.
     
        - parameter f: Fraction to multiply self by
     
        - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
        Divides self by a fraction.
     
        - parameter f: Fraction to divide self by
     
        - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
}

Each of the methods takes a Fraction object as an argument, performs an arithmetic operation, and returns a new Fraction. The return call invokes the designated initialiser to create a new instance of a Fraction that is to be returned. Now, in the main.swift program, we can add, subtract, multiply and divide fractions. The programer using Fraction objects does not need to understand the details of the fraction arithmetic. They just need to know how to initialise and manipulate Fraction objects with their methods (and trust that these methods do the arithmetic operations correctly).

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.description)")
print("decimal value of u=\(u.decimal)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.description)")
print("decimal value of x=\(x.decimal)")

var v = Fraction(num: -7, den: 9)

print("v=\(v.description)")
print("decimal value of v=\(v.decimal)")

var f: Fraction

f = u.add(v)
print("(" + u.description + ")+(" + v.description + ")=" + f.description)

f = u.subtract(v)
print("(" + u.description + ")-(" + v.description + ")=" + f.description)

f = u.multiply(v)
print("(" + u.description + ")*(" + v.description + ")=" + f.description)

f = u.divide(v)
print("(" + u.description + ")/(" + v.description + ")=" + f.description)

Whereas initialisers require the name of the arguments to be specified in the initialisation call, Objective-C does not need the first argument of a regular method call to have a name, even though (confusingly) the arguments that follow the first one do need names. Swift used to follow the Objective-C way of doing things. However, it was a bit strange, so Swift 3 changed to require all method parameters to have a name. You can explicitly indicate that a name is not needed by using an underscore (_) which is what has been done in this case.

u=2/3
decimal value of u=0.666667
x=-5/1
decimal value of x=-5.0
v=-7/9
decimal value of v=-0.777778
(2/3)+(-7/9)=-3/27
(2/3)-(-7/9)=-39/27
(2/3)*(-7/9)=-14/27
(2/3)/(-7/9)=18/-21

Static methods

Invocation of a method on an object is analogous to sending that object a message: the method name being the subject of the message, and the value of the arguments being the body. Upon receiving that message, the object performs a computation and sends back a reply. Typically, the output of that computation will depend on the state of the object. For instance, taking func add(f: Fraction) -> Fraction as an example, the code of that method does a fraction addition on f and the object receiving the add message.

Static methods are less like messages, and more like class functions that need not be associated with a specific instance of the object (of that class). These methods still can accept objects as arguments, and manipulate their state, but there is no concept of self within the scope of the static method. Here's how to implement the same four methods we just added as static methods:

Fraction.swift
001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
}

Note the static keyword - as soon as you use it, the compiler will not let you use a self reference inside the function body. Still, you can send messages to the objects that got passed in as arguments. The static func add(f1: Fraction, to f2: Fraction) -> Fraction method performs the entire add operation returning the result through an initialiser. The other static methods just send appropriate messages to one of the passed-in arguments.

All the new methods carry two names for the second argument - the first name is the external name, used by the method caller, the second is the internal name used to refer to the argument inside the body of the method. The external/internal method name split has nothing to do with the methods being static - it's just a Swift feature that allows you to separate those two names. It nicely decouples the function signature for those using the object from those who write it. If only one name is given to the argument, as has been our practice up to this point, then the external and internal names are taken to be the same. Remember that Swift 3 by default gives external and internal names to all arguments, including the first: Objective-C compatibility will dictate many uses that do not have a named first argument, however within the Swift libraries there has been a significant effort to rewrite names to put the "name all paramters" default to good use, such as increasing code clarity.

Static methods are not invoked on an object instance, but on the class itself. The code in main.swift can be modified as follows, while still providing the same output:

main.swift
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.description)")
print("decimal value of u=\(u.decimal)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.description)")
print("decimal value of x=\(x.decimal)")

var v = Fraction(num: -7, den: 9)

print("v=\(v.description)")
print("decimal value of v=\(v.decimal)")

var f: Fraction

f = Fraction.add(u, to: v)
print("(" + u.description + ")+(" + v.description + ")=" + f.description)

f = Fraction.subtract(v, from: u)
print("(" + u.description + ")-(" + v.description + ")=" + f.description)

f = Fraction.multiply(u, by: v)
print("(" + u.description + ")*(" + v.description + ")=" + f.description)

f = Fraction.divide(u, by: v)
print("(" + u.description + ")/(" + v.description + ")=" + f.description)

Whereas the code "f = u.add(v)" means "send an add message to u with argument v", the code "f = Fraction.add(u, to: v)" means "call Fraction's static add method with arguments u and v". Note that the external name of the second argument must be specified before the argument is given. Coupled with the fact that the first argument is not named (although remember that by default Swift 3 expects a name for the first argument too), this makes the call very readable: "add u to v", "subtract v from u", "multiply u by v", "divide u by v". Of course, to make those calls so nice for reading, you have to pick good method and parameter names when you define them.

Method overloading

The Fraction class has now two add methods (one of them static), as well as two subtract, multiply and divide methods. The practice of creating a number of methods with the same name, but different type or number of parameters, is called method overloading. For instance, we can overload the static methods by creating the version of add, subtract, multiply and divide that accept an Int as an argument:

Fraction.swift
001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
    /**
    Adds an integer to self.
    
    - parameter x: Integer to add to self
    
    - returns: Fraction The result of adding x to self.
    */
    func add(_ x: Int) -> Fraction {
        return Fraction(num: self.num + self.den*x,
            den: self.den)
    }
    
    /**
    Subtracts an integer from self.
    
    - parameter x: Integer to subtract from self
    
    - returns: Fraction The result of subtracting x from self.
    */
    func subtract(_ x: Int) -> Fraction {
        return Fraction(num: self.num - self.den*x,
            den: self.den)
    }
    
    /**
    Multiplies self by an integer.
    
    - parameter x: Integer to multiply self by
    
    - returns: Fraction The result of multiplying self by x.
    */
    func multiply(_ x: Int) -> Fraction {
        return Fraction(num: self.num*x, den: self.den)
    }
    
    /**
    Divides self by an integer.
    
    - parameter x: Integer to divide self by
    
    - returns: Fraction The result of dividing self by x.
    */
    func divide(_ x: Int) -> Fraction {
        return Fraction(num: self.num, den: self.den*x)
    }
    
}

Add the following code to the main program:

main.swift
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.description)")
print("decimal value of u=\(u.decimal)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.description)")
print("decimal value of x=\(x.decimal)")

var v = Fraction(num: -7, den: 9)

print("v=\(v.description)")
print("decimal value of v=\(v.decimal)")

var f: Fraction

f = Fraction.add(u, to: v)
print("(" + u.description + ")+(" + v.description + ")=" + f.description)

f = Fraction.subtract(v, from: u)
print("(" + u.description + ")-(" + v.description + ")=" + f.description)

f = Fraction.multiply(u, by: v)
print("(" + u.description + ")*(" + v.description + ")=" + f.description)

f = Fraction.divide(u, by: v)
print("(" + u.description + ")/(" + v.description + ")=" + f.description)

f = u.add(3)
print("(" + u.description + ")+3=" + f.description)

let z: Int = 2 
f = u.divide(z)
print("(" + u.description + ")/\(z)=" + f.description)

Because the 3 literal corresponds to an Int and z variable is of Int type, the compiler knows that the new add and divide methods correspond to the ones that take an Int as the argument, and not the ones that take a Fraction as the argument. The output of the program should be now:

u=2/3
decimal value of u=0.666667
x=-5/1
decimal value of x=-5.0
v=-7/9
decimal value of v=-0.777778
(2/3)+(-7/9)=-3/27
(2/3)-(-7/9)=-39/27
(2/3)*(-7/9)=-14/27
(2/3)/(-7/9)=18/-21
(2/3)+3=11/3
(2/3)/2=2/6

Operator overloading

In Swift you can write functions that define what certain operators, such as +,-,* and /, do to different type of variables and objects. This is called operator overloading. The functions that overload the four mentioned operators for two Fraction objects are given below:

Note that the operator functions are written outside the class definition.

Fraction.swift
001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    var num: Int;   // Numerator
    var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
    /**
    Adds an integer to self.
    
    - parameter x: Integer to add to self
    
    - returns: Fraction The result of adding x to self.
    */
    func add(_ x: Int) -> Fraction {
        return Fraction(num: self.num + self.den*x,
            den: self.den)
    }
    
    /**
    Subtracts an integer from self.
    
    - parameter x: Integer to subtract from self
    
    - returns: Fraction The result of subtracting x from self.
    */
    func subtract(_ x: Int) -> Fraction {
        return Fraction(num: self.num - self.den*x,
            den: self.den)
    }
    
    /**
    Multiplies self by an integer.
    
    - parameter x: Integer to multiply self by
    
    - returns: Fraction The result of multiplying self by x.
    */
    func multiply(_ x: Int) -> Fraction {
        return Fraction(num: self.num*x, den: self.den)
    }
    
    /**
    Divides self by an integer.
    
    - parameter x: Integer to divide self by
    
    - returns: Fraction The result of dividing self by x.
    */
    func divide(_ x: Int) -> Fraction {
        return Fraction(num: self.num, den: self.den*x)
    }
    
}

/**
+ operator between two Fractions
*/
func +(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.add(f2)
}

/**
- operator between two Fractions
*/
func -(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.subtract(f2)
}

/**
* operator between two Fractions
*/
func *(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.multiply(f2)
}

/**
/ operator between two Fractions
*/

func /(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.divide(f2)
}
Now we can change the code in main.swift as follows, while retaining the same program output:
main.swift
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)

// Show the state of the object's internal variables
print("u=\(u.description)")
print("decimal value of u=\(u.decimal)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.description)")
print("decimal value of x=\(x.decimal)")

var v = Fraction(num: -7, den: 9)

print("v=\(v.description)")
print("decimal value of v=\(v.decimal)")

var f: Fraction

f = u+v
print("(" + u.description + ")+(" + v.description + ")=" + f.description)

f = v-u
print("(" + u.description + ")-(" + v.description + ")=" + f.description)

f = u*v
print("(" + u.description + ")*(" + v.description + ")=" + f.description)

f = u/v
print("(" + u.description + ")/(" + v.description + ")=" + f.description)

f = u.add(3)
print("(" + u.description + ")+3=" + f.description)

let z: Int = 2 
f = u.divide(z)
print("(" + u.description + ")/\(z)=" + f.description)
For more information on existing operator overloading, look up Swift documentation on operator overloading. If you feel adventurous, you can also take a look at custom operators.

Exercise

Write functions that will overload the +,-,* and / operators for operations between a Fraction and an Int. Change main.swift to use appropriate operators instead of the add and divide methods when doing Fraction and Int arithmetic.

Access control

Sometimes it makes sense to prevent a developer (who is creating objects of the class you implemented) from direct access to object's internal variables. For instance, in the Fraction class, the denominator value of 0 is considered invalid - that's why there is an assertion in the designated initialiser that will not allow it being set to 0. However, there is nothing stopping the developer from changing the denominator value to whatever they want after initialisation, like so:

01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
import Foundation

// Create and initialise a new Fraction object
var u: Fraction

// Initialise the object
u = Fraction(num: 2,den: 3)
u.den = 0 

// Show the state of the object's internal variables
print("u=\(u.description)")
print("decimal value of u=\(u.decimal)")

// Define and create a new instance of a Fraction object
var x: Fraction = Fraction(num: -5)

print("x=\(x.description)")
print("decimal value of x=\(x.decimal)")

var v = Fraction(num: -7, den: 9)

print("v=\(v.description)")
print("decimal value of v=\(v.decimal)")

var f: Fraction

f = u+v
print("(" + u.description + ")+(" + v.description + ")=" + f.description)

f = v-u
print("(" + u.description + ")-(" + v.description + ")=" + f.description)

f = u*v
print("(" + u.description + ")*(" + v.description + ")=" + f.description)

f = u/v
print("(" + u.description + ")/(" + v.description + ")=" + f.description)

f = u.add(3)
print("(" + u.description + ")+3=" + f.description)

let z: Int = 2 
f = u.divide(z)
print("(" + u.description + ")/\(z)=" + f.description)

When you run this program, you'll get an assertion. Notice that the computation of u.decimal goes through returning infinity. The assertion happens when u is added to another fraction. The common denominator of any number with u is 0, and the designated initialiser, called from within the add method, throws the assertion.

Private properties

One way to prevent others from direct access to internal variables of our class, is to change their privacy settings, like so:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    private var num: Int;   // Numerator
    private var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
    /**
    Adds an integer to self.
    
    - parameter x: Integer to add to self
    
    - returns: Fraction The result of adding x to self.
    */
    func add(_ x: Int) -> Fraction {
        return Fraction(num: self.num + self.den*x,
            den: self.den)
    }
    
    /**
    Subtracts an integer from self.
    
    - parameter x: Integer to subtract from self
    
    - returns: Fraction The result of subtracting x from self.
    */
    func subtract(_ x: Int) -> Fraction {
        return Fraction(num: self.num - self.den*x,
            den: self.den)
    }
    
    /**
    Multiplies self by an integer.
    
    - parameter x: Integer to multiply self by
    
    - returns: Fraction The result of multiplying self by x.
    */
    func multiply(_ x: Int) -> Fraction {
        return Fraction(num: self.num*x, den: self.den)
    }
    
    /**
    Divides self by an integer.
    
    - parameter x: Integer to divide self by
    
    - returns: Fraction The result of dividing self by x.
    */
    func divide(_ x: Int) -> Fraction {
        return Fraction(num: self.num, den: self.den*x)
    }
    
}

/**
+ operator between two Fractions
*/
func +(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.add(f2)
}

/**
- operator between two Fractions
*/
func -(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.subtract(f2)
}

/**
* operator between two Fractions
*/
func *(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.multiply(f2)
}

/**
/ operator between two Fractions
*/

func /(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.divide(f2)
}

In Swift, private variables can be only accessed from the code written in the same file as the class definition. Xcode will now complain that the u.den=0 statement in main.swift is invalid. The error message should be something along the lines that 'den' is inaccessible due to 'private' protection level.

It's probably a good time now to make a clear distinction between two roles a programmer can take on: that of a programmer who creates programming tools and the programmer who writes programs using those tools. Let's refer to these roles as Toolmaker and Builder. In OOP Toolmaker writes classes so that Builder can create objects of those classes and do something useful with them. Often it happens, as it does in this lab, that the Toolmaker and the Builder are the same person - still, it's quite useful to keep these roles separate in your mind.

And so, in this lab, whenever you edit Fraction.swift you are taking on the role of the Toolmaker - you decide what Fraction objects do and how they behave, for which you need to understand the inner workings of the Fraction class. Whenever you edit main.swift, you are taking on the role of the Builder, developing a program that does something with Fraction objects. The Builder creates and uses the objects, so he/she needs to understand what can be done with them, but not necessarily what is going on inside. A world analogy would be the distinction between a car Mechanic and a car Driver. The first one can be also the latter, but the latter doesn't necessarily need to be the first one. Thus, the Driver does not need to know how car engine works in order to drive the car.

Access control is something that Toolmaker can use to decide what is visible to the Builder and what remains "under the hood" for a given class. In this exercise, the stored properties are made private, because the Toolmaker does not want the Builder to meddle with the internal state in any other way other than through the initialisers. This makes it harder for Builders to do something wrong with the object.

OK, since den is now private, if you want your program to compile, you have to comment out or remove the line "u.den = 0 " from main.swift. The only way now to set the value of denominator is through an initialiser.

Private methods

Access control can be applied to methods as well. Let's create a private method that reduces the fraction by dividing its numerator and denominator by the greatest common denominator:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    private var num: Int;   // Numerator
    private var den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den

        self.reduce()
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
    /**
    Adds an integer to self.
    
    - parameter x: Integer to add to self
    
    - returns: Fraction The result of adding x to self.
    */
    func add(_ x: Int) -> Fraction {
        return Fraction(num: self.num + self.den*x,
            den: self.den)
    }
    
    /**
    Subtracts an integer from self.
    
    - parameter x: Integer to subtract from self
    
    - returns: Fraction The result of subtracting x from self.
    */
    func subtract(_ x: Int) -> Fraction {
        return Fraction(num: self.num - self.den*x,
            den: self.den)
    }
    
    /**
    Multiplies self by an integer.
    
    - parameter x: Integer to multiply self by
    
    - returns: Fraction The result of multiplying self by x.
    */
    func multiply(_ x: Int) -> Fraction {
        return Fraction(num: self.num*x, den: self.den)
    }
    
    /**
    Divides self by an integer.
    
    - parameter x: Integer to divide self by
    
    - returns: Fraction The result of dividing self by x.
    */
    func divide(_ x: Int) -> Fraction {
        return Fraction(num: self.num, den: self.den*x)
    }

    /**
    Reduce self by greatest common denominator found.
    */
    private func reduce() {
        if(den < 0) {
            // If the denominator is negative
            // multiply the numerator and
            // denominator by -1 - this
            // ensures the denominator is
            // always positive, and numerator
            // carries the appropriate sign
            num = -num
            den = -den
        }
        
        // Find greatest common denominator
        for gcd in (1...den).reversed() {
            if(num%gcd == 0 && den%gcd==0) {
                // Common denominator found,
                // divide numerator and denominator
                num /= gcd
                den /= gcd
                break
            }
        }
    }    
}

/**
+ operator between two Fractions
*/
func +(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.add(f2)
}

/**
- operator between two Fractions
*/
func -(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.subtract(f2)
}

/**
* operator between two Fractions
*/
func *(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.multiply(f2)
}

/**
/ operator between two Fractions
*/

func /(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.divide(f2)
}

The new method is "private func reduce()". Note that inside the method, when we refer to num and den, it's the same as referring to self.num and self.den, since there are no shadowing variables. There is a reason why the self keyword has been omitted in this case, and it has to do with the fact that we'll be pasting this code somewhere else in a bit.

Since the value of numerator and denominator can change only through initialisers, it makes sense to do reduction in the designated initialiser, as shown in the code above. Also, since the new method is private, it cannot be invoked from main.swift, only from a function or method implemented in Fraction.swift. The output of main.swfit should be now like this:

u=2/3
decimal value of u=0.666667
x=-5/1
decimal value of x=-5.0
v=-7/9
decimal value of v=-0.777778
(2/3)+(-7/9)=-1/9
(2/3)-(-7/9)=-13/9
(2/3)*(-7/9)=-14/27
(2/3)/(-7/9)=-6/7
(2/3)+3=11/3
(2/3)/2=1/3

Mutability

Did you notice that the the numerator and denominator values are changing only inside initialisers? Even the methods implementing fraction arithmetic combine existing fractions by creating new ones, thus setting the new values through an initialiser. Since the stored properties remain constant after initialisation of a given object instance, might as well make these properties constant. Properties that are constant can have their values set only once and it must be in an initialiser. A class whose internal state is constant is said to be immutable. To make the Fraction class immutable, all we need is to change the var keyword to a let keyword when declaring the properties, as shown below:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    private let num: Int;   // Numerator
    private let den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")
        
        self.num = num
        self.den = den

        self.reduce()
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
    /**
    Adds an integer to self.
    
    - parameter x: Integer to add to self
    
    - returns: Fraction The result of adding x to self.
    */
    func add(_ x: Int) -> Fraction {
        return Fraction(num: self.num + self.den*x,
            den: self.den)
    }
    
    /**
    Subtracts an integer from self.
    
    - parameter x: Integer to subtract from self
    
    - returns: Fraction The result of subtracting x from self.
    */
    func subtract(_ x: Int) -> Fraction {
        return Fraction(num: self.num - self.den*x,
            den: self.den)
    }
    
    /**
    Multiplies self by an integer.
    
    - parameter x: Integer to multiply self by
    
    - returns: Fraction The result of multiplying self by x.
    */
    func multiply(_ x: Int) -> Fraction {
        return Fraction(num: self.num*x, den: self.den)
    }
    
    /**
    Divides self by an integer.
    
    - parameter x: Integer to divide self by
    
    - returns: Fraction The result of dividing self by x.
    */
    func divide(_ x: Int) -> Fraction {
        return Fraction(num: self.num, den: self.den*x)
    }

    /**
    Reduce self by greatest common denominator found.
    */
    private func reduce() {
        if(den < 0) {
            // If the denominator is negative
            // multiply the numerator and
            // denominator by -1 - this
            // ensures the denominator is
            // always positive, and numerator
            // carries the appropriate sign
            num = -num
            den = -den
        }
        
        // Find greatest common denominator
        for gcd in (1...den).reversed() {
            if(num%gcd == 0 && den%gcd==0) {
                // Common denominator found,
                // divide numerator and denominator
                num /= gcd
                den /= gcd
                break
            }
        }
    }    
}

/**
+ operator between two Fractions
*/
func +(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.add(f2)
}

/**
- operator between two Fractions
*/
func -(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.subtract(f2)
}

/**
* operator between two Fractions
*/
func *(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.multiply(f2)
}

/**
/ operator between two Fractions
*/

func /(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.divide(f2)
}

At this point Xcode will start complaining. The problem is that the reduce method attempts to change the numerator and denominator values, which are now defined as constants. Xcode doesn't care that this method is invoked form an initialiser - in theory it can be invoked from anywhere from within the class, and since num and den have been declared as constants, this method is not allowed to change them. The solution here is to move the code of the reduce method and insert it into the designated initialiser before the properties are set, like so:

001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
import Foundation

/**
Represents a number as a fraction of two integers

*/
class Fraction {
    
    // STORED PROPERTIES
    
    private let num: Int;   // Numerator
    private let den: Int;   // Denominator
    
    // COMPUTED PROPERTIES
    
    /**
    Converts fraction to a Float value
    
    - returns: Float Decimal value of the fraction
    */
    var decimal: Float {
        get {
            // num and den are of type Int, therefore,
            // they need to be explicitly converted to Floats
            return Float(self.num)/Float(self.den);
        }
    }
    
    /**
    Converts object to a string description
    
    - returns: String String representation of a fraction
    */
    var description: String {
        return "\(self.num)/\(self.den)"
    }
    
    // INITIALISERS
    
    /**
    Default initialiser
    
    Numerator gets set to 0 and denominator gets set to 1
    */
    init() {
        // Setting numerator to 0 and
        // denominator to 1 is equivalent
        // to setting the fraction to zero
        self.num = 0 
        self.den = 1 
    }
    
    /**
    Designated initialiser
    
    Numerator and denominator values are passed in the arguments of the initialiser.
    
    - parameter num: Fraction's numerator
    - parameter den: Fraction's denominator
    */
    init(num : Int, den : Int) {
        
        // Check the denominator...
        assert(den != 0, "Denominator cannot be zero")

        // Arguments are constants, redefine them
        // as variables
        var num = num;
        var den = den;

        if(den < 0) {
            // If the denominator is negative
            // multiply the numerator and
            // denominator by -1 - this
            // ensures the denominator is
            // always positive, and numerator
            // carries the appropriate sign
            num = -num
            den = -den
        }
        
        // Find greatest common denominator
        for gcd in (1...den).reversed() {
            if(num%gcd == 0 && den%gcd==0) {
                // Common denominator found,
                // divide numerator and denominator
                num /= gcd
                den /= gcd
                break
            }
        }
        
        self.num = num
        self.den = den
    }
    
    /**
    Convenience initialiser
    
    Numerator is set to some value, denominator
    is set to 1.
    
    - parameter num: Fraction's numerator
    */
    convenience init(num : Int) {
        self.init(num: num, den: 1);
    }
    
    // METHODS
    
    /**
    Adds a fraction to self.
    
    - parameter f: Fraction to add to self
    
    - returns: Fraction The result of adding f to self.
    */
    func add(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den + self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Subtracts fraction from self.
    
    - parameter f: Fraction to subtract from self
    
    - returns: Fraction The result of subtracting f from self.
    */
    func subtract(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den - self.den*f.num,
            den: self.den*f.den)
    }
    
    /**
    Multiplies self by a fraction.
    
    - parameter f: Fraction to multiply self by
    
    - returns: Fraction The result of multiplying self by f.
    */
    func multiply(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.num, den: self.den*f.den)
    }
    
    /**
    Divides self by a fraction.
    
    - parameter f: Fraction to divide self by
    
    - returns: Fraction The result of dividing self by f.
    */
    func divide(_ f: Fraction) -> Fraction {
        return Fraction(num: self.num*f.den, den: self.den*f.num)
    }
    
    /**
    Add a fraction to fraction.
    
    - parameter f1: Fraction to add to
    - parameter f2: Fraction to be added
    
    - returns: The result of f1 + f2.
    */
    static func add(_ f1: Fraction, to f2: Fraction) -> Fraction {
        return Fraction(num: f1.num*f2.den + f1.den*f2.num,
            den: f1.den*f2.den)
    }
    
    /**
    Subtract a fraction from fraction.
    
    - parameter f1: Fraction to subtract
    - parameter f2: Fraction to subtract from
    
    - returns: The result of f2 - f1.
    */
    static func subtract(_ f1: Fraction, from f2: Fraction) -> Fraction {
        return f2.subtract(f1);
    }
    
    /**
    Multiply a fraction by fraction.
    
    - parameter f1: Fraction to multiply
    - parameter f2: Fraction to multiply by
    
    - returns: The result of f1*f2.
    */
    static func multiply(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.multiply(f2)
    }
    
    /**
    Divide a fraction by fraction.
    
    - parameter f1: Fraction to divide
    - parameter f2: Fraction to divide by
    
    - returns: The result of f1/f2.
    */
    static func divide(_ f1: Fraction, by f2: Fraction) -> Fraction {
        return f1.divide(f2)
    }
    
    /**
    Adds an integer to self.
    
    - parameter x: Integer to add to self
    
    - returns: Fraction The result of adding x to self.
    */
    func add(_ x: Int) -> Fraction {
        return Fraction(num: self.num + self.den*x,
            den: self.den)
    }
    
    /**
    Subtracts an integer from self.
    
    - parameter x: Integer to subtract from self
    
    - returns: Fraction The result of subtracting x from self.
    */
    func subtract(_ x: Int) -> Fraction {
        return Fraction(num: self.num - self.den*x,
            den: self.den)
    }
    
    /**
    Multiplies self by an integer.
    
    - parameter x: Integer to multiply self by
    
    - returns: Fraction The result of multiplying self by x.
    */
    func multiply(_ x: Int) -> Fraction {
        return Fraction(num: self.num*x, den: self.den)
    }
    
    /**
    Divides self by an integer.
    
    - parameter x: Integer to divide self by
    
    - returns: Fraction The result of dividing self by x.
    */
    func divide(_ x: Int) -> Fraction {
        return Fraction(num: self.num, den: self.den*x)
    }
}

/**
+ operator between two Fractions
*/
func +(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.add(f2)
}

/**
- operator between two Fractions
*/
func -(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.subtract(f2)
}

/**
* operator between two Fractions
*/
func *(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.multiply(f2)
}

/**
/ operator between two Fractions
*/

func /(f1: Fraction, f2: Fraction) -> Fraction {
    return f1.divide(f2)
}

Remove the reduce method, it's not necessary anymore. Also notice the redefinition of method arguments as variables. Swift takes function arguments to be constants (prior to Swift 3 there was a syntax to take arguments as variables). The new code, pasted in from the reduce method changes the value of num and den, which now, due to shadowing, are the arguments redefined as variables, not the class properties. We do actually want to be changing the arguments, because multiple changes might be needed, and self.num and self.den can be set only once. The main.swift program should compile now and give the same output as before.

Immutability might be desirable for several reasons. Constant properties can be made public, and so be visible to the Builder for reading but not allowed to be overwritten. The compiler can do better optimisation on variables that it knows are constant, and so immutable objects (in theory) lead to faster programs. Also, whereas access control can prevent a Builder from misusing the internal state of an object, it does not put restrictions on the way the state is used by the Toolmaker inside the class. For big projects, where there might be many developers creating a complex class, it might be desirable to put some restrictions on the Toolmakers. Immutable state assures that the only way a Toolmaker can change it is by using an initialiser.

Of course immutability is not appropriate in every situation - many classes must be mutable in order to do what they are supposed to do. Hence, you shouldn't necessarily strive to make your classes immutable in every situations - only consider this option whenever appropriate.

Exercise

This is the end of this lab. We will use the Fraction class in later labs, so just to make it a bit better, try to change the description property so that it represent the state in a nicer format. Have it make strings of whole numbers (where denominator is 1) as "-5" and "3" as opposed to "-5/1" and "3/1". Also, factor out integers, so that "11/3" is printed as "3 2/3". Finally, whenever numerator is zero, print the state as "0" and not "0/1".