Welcome to the third tutorial in the series. Here we will cover functions and structures. This will put us in good stead for part four where we’ll finally get onto the subject of object-oriented programming.
What you will learn…
- How to define and call functions
- How to work with structures
What you should know…
During this series we’ve used several global functions provided by Swift, such as println
and count
. Now we’ll see how to write our own custom functions in order to build modular code that can accomplish specific tasks. We’ll also spend considerable time exploring structures, which will effectively let you define your own custom types within Swift.
Functions and structures are both large topics and we certainly won’t cover every aspect of them here. However, both lead very nicely into the subject of object-oriented programming and therefore deserve particular attention before we can begin to work with classes in part four.
Getting Started
By now you should feel comfortable with the content covered in the first two tutorials. If not then please revisit them:
To try out this tutorial’s code examples, install Xcode 6 and create a playground.
Detail regarding that can be found in the first tutorial.
Functions
We’ll begin by writing the simplest form of function: one that takes no parameters and doesn’t return anything. The func
keyword is used to define a function:
println("Greetings Master Jedi!")
}
greetJedi
rather than GreetJedi
or greetjedi
. The code above defines a function named greetJedi
. Enter it into your playground then call the function by typing:
This will result in the following message being displayed within the sidebar:
Parameters
You can optionally define one or more typed parameters for your function. Here’s an alternative version of the greetJedi
function that takes a Jedi’s name as a parameter:
println("Greetings Master \(name)!")
}
Now you can pass a name as an argument to the greetJedi
function:
The line above will result in the following being printed to the appropriate output:
Let’s write another version of greetJedi
that takes two parameters: a forename and a surname.
println("Greetings Master \(forename)!")
}
Here are a few examples of our greetJedi
function being called:
greetJedi("Qui-Gon", "Jinn")
Our function actually ignores the second argument and only prints the Jedi’s forename. The two calls above will result in the following being displayed:
Greetings Master Qui-Gon!
Return Values
You can also define a type of value that will be returned from your function. This is done by placing the return arrow ->
and the name of the type to return immediately after your function’s list of parameters. Here’s an example:
return "Greetings Master \(forename)!"
}
Of course, ActionScript and TypeScript developers will already find this syntax familiar as both languages deal with return types in a similar manner to Swift.
The latest version of our greetJedi
function now takes two parameters (a forename and surname) and returns a String
that represents a greeting: previous incarnations of greetJedi
had simply written the greeting directly to the appropriate output. Let’s see a few examples, where greetJedi
is called, and the string it returns is output via the println
function:
println(greetJedi("Anakin", "Skywalker"))
println(greetJedi("Qui-Gon", "Jinn"))
This would result in the following being displayed in the sidebar of your playground:
Greetings Master Anakin!
Greetings Master Qui-Gon
Void
. This is simply an empty tuple, which can also be written as ()
.
Take this function for example:
println("Hullo World!")
}
Although there is no need to do so, it could also be written as:
println("Hullo World!")
}
Returning Tuples
If you wish to return more than one value from a function then consider returning a tuple. The following example takes an array of names and returns the first and last name from the array:
return (names[0], names[names.count – 1])
}
let names = ["Luke", "Ben", "Anakin", "Yoda"]
let result = getFirstAndLast(names)
println("\(result.first) and \(result.last)")
The code snippet above will print the following in your playground’s sidebar:
getFirstAndLast
function above isn’t particularly robust. If you pass an empty array to it you’ll receive an error.
Re-write the function to safeguard against this by returning nil
when there are fewer than two names within the array. You’ll need to return an optional tuple to achieve this.
Local and External Parameter Names
Most of the functions we’ve written so far have defined parameter names. These names are local parameters however and can only be used within the function’s body.
With Swift you can also define external parameter names, which are used when calling the function and help to clarify the purpose of each parameter. When defining a function, the parameter’s external name is placed directly before its local name.
Here’s our greetJedi
function but with an external name defined for each parameter:
return "Greetings Master \(forename)!"
}
And here is the function being called:
As a comparison, here’s how the call looked before external parameter names were defined:
If an external name is defined for a parameter then it must be used when calling its function.
In the example above, our external parameter names were identical to the function’s local parameter names, however they can be different. Here’s another example to illustrate this:
// Implementation goes here
}
As an alternatively we can alter the function’s name and apply an external name to just the second parameter:
// Implementation goes here
}
If you wish to provide an external parameter name that is identical to the function’s local parameter name then there is no need to write the same name twice. Instead you can prefix your local parameter with a hash symbol (#
). By using this shorthand external parameter name syntax, we can change our greetJedi
function from:
return "Greetings Master \(forename)!"
}
to this:
return "Greetings Master \(forename)!"
}
Default Parameter Values
A default value can be assigned to a function’s parameter. Any parameters with a default value should be placed at the end of the function’s parameter list. Here’s an example where the energy
parameter is defaulted to 50
:
var description = "\(name) is "
if energy < 40 {
description += "weak."
} else if energy < 80 {
description += "an average Jedi."
} else {
description += "a powerful Jedi."
}
return description
}
The following call:
Will return Luke Skywalker is an average Jedi.
(he’s actually an awesome Jedi: this is just a silly example remember) Because the function call’s second argument was omitted, a default value of 50
was used.
Of course, you can explicitly specify the second argument:
Notice the use of the second parameter’s external name in the call above. You may have spotted that we didn’t actually define an external name for this parameter. For parameters with default values, Swift automatically assigns an external name that matches the parameter’s local name. You can of course, explicitly assign an external parameter name of your own choosing. Alternatively you can opt-out of Swift’s default behaviour by placing an underscore (
// Implementation goes here
}
Your call to describeJedi
would now look like this:
Variadic Parameters
Swift provides support for variadic parameters. A function can have at most one variadic parameter, which accepts zero or more values of a specified type. Variadic parameters are ideal when writing a function that can be passed a varying number of input values. Place three period characters (
var total: Double = 0
for number in numbers {
total += Double(number)
}
return total / Double(numbers.count)
}
The function above is used to determine the average number of starfighters the Rebel Alliance lose during space battles. It accepts a list of integers, where each integer represents the number of fighters lost in a particular battle.
Each of the values passed to a variadic parameter are made available within the function as an array of the appropriate type. In the example above, the number of starfighters lost in each battle are made available as an array of integers. The array uses the variadic parameter’s name (in this case numbers
).
When calling averageStarfightersLost
, each of the integers passed should be separated by a comma. Here are a few examples:
println("The rebel’s are losing \(avg) X-wings per battle.")
avg = averageStarfightersLost(20, 15, 4, 0, 8)
println("The rebel’s are losing \(avg) Y-wings per battle.")
If your function has more than one parameter, and one of those parameters is a variadic parameter, then place the variadic parameter last in the parameter list. This is also the case even if some of your other parameters have been assigned default values.
averageStarfightersLost
function however seems to have a problem when zero input values are passed to it: it returns (nan)
instead of the value 0.0
.
See if you can identify why this is and make a fix to your function to ensure that 0.0
is returned when the following call is made:
avg = averageStarfightersLost()
Constant and Variable Parameters
Unlike many languages, parameters in Swift are constants by default. Take the following example:
name += " has an energy of \(energy)"
return name
}
It will return the following compile-time error: Cannot assign to 'let' value 'name'
.
If you want to change the value of a parameter within the body of your function then you must explicitly specify that parameter as a variable parameter by prefixing it with the var
keyword:
name += " has an energy of \(energy)"
return name
}
Now the name
parameter’s value can be modified within the body of your function.
In-Out Parameters
Variable parameters can only be changed within a function’s body. Changes in value do not persist outside of the function. If you want a change to a parameter’s value to persist after the function call has taken place then define the parameter as an in-out parameter.
To specify an in-out parameter, place the inout
keyword at the beginning of the parameter’s definition:
let temp: String = lightsaber1
lightsaber1 = lightsaber2
lightsaber2 = temp
}
The function above takes two string parameters that each represent the colour of a Jedi’s lightsaber, and swaps them. After the function has executed, the first lightsaber will have the colour of the second lightsaber and vice versa. Here’s how to call swapLightsabers
:
var yodasLightsaber = "green"
println("Luke’s lightsaber is \(lukesLightsaber)")
println("Yoda’s lightsaber is \(yodasLightsaber)")
println("Luke’s lightsaber is now \(lukesLightsaber)")
println("Yoda’s lightsaber is now \(yodasLightsaber)")
Typing the above into your playground file will result in the following being output:
Yoda’s lightsaber is green
Luke’s lightsaber is now green
Yoda’s lightsaber is now blue
Notice in the example above that two variables (lukesLightsaber
and yodasLightsaber
) are passed as arguments to the swapLightsabers
function. You cannot pass a constant or a literal value as an argument since their values cannot be modified. Also notice that an ampersand (
The point to take from this example is that the function doesn’t return anything. By specifying both parameters as in-out parameters, the changes are made directly to the variables (lukesLightsaber
and yodasLightsaber
) that are passed in as arguments to our function.
Structs
If you’re from an Objective-C or C background then you’ll be familiar with structures (often referred to as structs). In fact, unlike some languages, structures in Swift share many of the powerful capabilities you’d normally associate with classes.
If on the other hand, you develop with an ECMAScript-based language such as JavaScript or ActionScript then you may not have come across structures. However, they aren’t difficult to grasp.
A structure is declared using the struct
keyword and its definition is placed within a pair of braces. Its general form looks like this:
// Implementation goes here
}
Here’s a concrete example of a structure that represents a two-dimensional point:
var x = 0.0, y = 0.0
}
This struct contains two stored properties named x
and y
. Both are variables of type Double
and have default values of 0.0
.
Instantiating a Struct
To use a structure you must create an instance of it using initializer syntax. Here’s an example where we instantiate our Point
structure:
This creates a variable named target
that represents a Point
instance. The instance’s x
and y
properties are set to their default value of 0.0
.
Structures also provide a memberwise initializer, which can be used to initialise each of the structure’s stored properties:
The snippet above instantiates an instance that has a default x
value of 64
and a default y
value of 12.5
. You must provide a value for every one of the structure’s stored properties. Take a look at the following:
This will result in the following compile-time error: Missing argument for parameter 'y' in call
.
Accessing Properties
You can access and modify an instance’s stored properties using dot syntax. The code snippet below shows how to query the value of your Point
instance’s x
and y
properties:
var x =
var y =
Here’s how you assign new values to your instance’s stored properties:
target.y = 10.0
Structs are Value Types
Every type we’ve looked at in this series is what is known as a value type. Structs are no different.
A value type is a type whose value is copied when it is assigned to a variable or constant, or when it is passed as an argument to a function. This can best be illustrated with the following example:
var point2 = point1
The code above creates an instance of a Point
structure and assigns it to a variable named point1
. A second variable named point2
is then created and it is set to equal point1
.
Now take a look at the following few lines of code:
println("point1.x: \(point1.x)")
println("point2.x: \(point2.x)")
Those from an ECMAScript background may reasonably expect that any changes to the properties of point1
will also be reflected in the variable point2
. In other words, they may expect the following to be displayed:
point2.x: 128
However, since the point2
variable holds its own copies of the x
and y
properties, the following result will actually be written to the appropriate output:
point2.x: 64
We’ll discover in part four that classes are handled differently (they are reference types) and this can lead to some confusion since classes and structures are similar in so many ways.
The following code snippet will demonstrate this for arrays:
var arrayB = arrayA
arrayA[0] = "d"
println(arrayA[0]) // Will output ‘d’
println(arrayB[0]) // You may expect ‘d’ here but ‘a’ will actually be output
If you’re familiar with Objective-C then this may seem counterintuitive since NSString
, NSArray
and NSDictionary
are all implemented as classes and are therefore reference types.
Custom Initialisation
When an instance of a structure is created, its stored properties are assigned an initial value as part of the initialisation process. All properties must have an initial value or instantiation cannot take place. One way of doing this is to set a default value when defining each property of your struct. We’ve already seen this with our Point
structure:
var x = 0.0, y = 0.0
}
var target = Point()
Alternatively, you can write an initializer and set default values for each of your properties within it:
var x, y
x = 0.0
y = 0.0
}
}
var target = Point()
Your initializer should be named init
and, unlike a regular function (or class method), isn’t preceded with the func
keyword. Also notice that we had to explicitly declare the type of both properties since a variable’s type cannot be inferred unless an initial value is provided with its definition.
Any custom initializers you write replace the struct’s default memberwise initializer. For example, the following attempt to create a Point
instance with a default x
value of 15.5
and a y
value of 0.75
will result in an error:
var x, y: Double
init() {
x = 0.0
y = 0.0
}
}
var target = Point(x: 15.5, y: 0.75)
The above will result in the following compile-time error: Extra argument 'x' in call
.
You can overcome this problem by creating a second initializer that accepts initialization parameters:
var x, y :Double
init() {
x = 0.0
y = 0.0
}
self.x = x
self.y = y
}
}
Notice the use of the self
property, which is a reference to the instance itself. In our initializer above, self
is used to distinguish between the parameters named x
and y
, and the class’ properties of the same name.
self
keyword. It is commonly known as this
in most other programming languages. We can now create two different Point
instances: one that has a default position of (0, 0)
and another where you can explicitly state its initial position. You can see this below:
var target = Point(x: 15.5, y: 0.75)
Notice that your custom initializer’s local parameters have been exposed as external parameters. Swift does this by default for all custom initializers that you write. If you prefer, you can suppress this by placing an underscore (
Of course, our current Point
example is rather contrived for the purpose of demonstrating custom initializers. The code example above is in fact identical to the much simpler version we saw at the beginning of this section. Here is our current version again:
var x, y :Double
init() {
x = 0.0
y = 0.0
}
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
and here is the identical simplified version:
var x = 0.0, y = 0.0
}
And with this simplified version of the Point
structure we can just as easily create both a default and a custom point:
var target = Point(x: 15.5, y: 0.75)
Initializers and External Parameter Names
Let’s now look at a more practical use for custom initializers. We’ll create a new structure that represents a rectangle:
var left = 0.0, top = 0.0
var width = 0.0, height = 0.0
}
The Rect
struct has four properties. Its left
and top
properties are used to define the position of the rectangle’s top-left corner. The width
and height
properties are used to define the rectangle’s dimensions. Let’s create a Rect
instance:
This creates a rectangle that has its top-left corner positioned at (0, 0)
and has a width of 200
and a height of 50
.
We also have our Point
struct, so let’s write a custom initializer that lets us specify our rectangle’s top-left corner using it. Here’s the code:
var x = 0.0, y = 0.0
}
var left, top
var width, height
left = p.x
top = p.y
width = w
height = h
}
}
We can now utilise our Point
structure when creating an instance of our Rect
structure:
While our custom initializer certainly gets the job done, we can actually improve it. We can assign external names to each of our initializer’s parameters to make our call to it more readable. This is particularly important because initializers, unlike functions, don’t have an identifiable name associated with them that can add additional meaning. Let’s make the changes:
var left, top: Double
var width, height: Double
init(
left = p.x
top = p.y
width = w
height = h
}
}
We can now create an instance with a much more natural initializer call:
Initializer Delegation
We saw earlier with our Point
structure that we can have more than one initializer. It’s also possible for one initializer to call another to help with the initialization of an instance. Let’s modify our Rect
structure to include a second initializer and also have one initializer call the other:
var left, top: Double
var width, height: Double
left = x
top = y
width = w
height = h
}
init(atPoint p: Point, withWidth w: Double, andHeight h: Double) {
}
}
Note the use of the self
property to call one initializer from within the body of another.
Computed Properties
So far we’ve only looked at stored properties within our structures. As a reminder, a stored property is a variable or constant that is stored as part of a structure’s instance. In addition to stored properties you can also define a computed property. Computed properties do not store their own value, instead they provide a getter and setter that is used to indirectly obtain and set other stored properties.
Take our Rect
structure as an example. It has a left
and top
property that represents the rectangle’s top-left corner. At present there are no right
and bottom
properties that represent the position of the rectangle’s bottom-right corner. We could create a stored property for each, but that would mean that we’d have to remember to also update each instance’s width
and height
properties every time we modified the right
and bottom
properties respectively (or vice versa). In such situations, rather than directly storing a value for right
and bottom
, it makes more sense to represent them as computed properties.
Let’s begin by defining a right
computed property and writing a getter and setter for it:
var left, top: Double
var width, height: Double
get {
return left + width
}
set(newRight) {
width = newRight – left
}
}
init(atX x: Double, andY y: Double, withWidth w: Double, andHeight h: Double) {
left = x
top = y
width = w
height = h
}
init(atPoint p: Point, withWidth w: Double, andHeight h: Double) {
self.init(atX: p.x, andY: p.y, withWidth: w, andHeight: h)
}
}
Our right
property has been defined as a Double
but notice that it doesn’t directly store a value. Instead, its getter uses the structure’s left
and width
properties to calculate and return the value of right
.
Our setter takes a value for our right
computed property, but doesn’t actually store that value. Instead it uses it, along with the left
stored property, to calculate and set a value for the rectangle’s width
property.
There’s a slight modification we can make to our setter. If you omit the parameter from the setter’s definition then Swift will provide a default parameter name of newValue
. We can therefore change our setter from:
width = newRight – left
}
to:
width =
}
Let’s now go ahead and define a bottom
computed property. We’ll also use the shorthand setter declaration just discussed:
var left, top: Double
var width, height: Double
var right: Double {
get {
return left + width
}
set {
width = newValue – left
}
}
get {
return top + height
}
set {
height = newValue – top
}
}
init(atX x: Double, andY y: Double, withWidth w: Double, andHeight h: Double) {
left = x
top = y
width = w
height = h
}
init(atPoint p: Point, withWidth w: Double, andHeight h: Double) {
self.init(atX: p.x, andY: p.y, withWidth: w, andHeight: h)
}
}
Now let’s create an instance of a Rect
and make use of our right
and bottom
computed properties:
println("The rectangle has a width of: \(target.width) and a height of: \(target.height)")
println("Its bottom-right corner is at (\(target.right), \(target.bottom))")
This will output the following:
Its bottom-right corner is at (210.0, 100.0)
Now change the width and height of your instance and once again query your properties. Enter the following into your playground:
target.height = 100
println("The rectangle has a width of: \(target.width) and a height of: \(target.height)")
println("Its bottom-right corner is at (\(target.right), \(target.bottom))")
The following will now be output:
Its bottom-right corner is at (190.0, 150.0)
Finally, set the right
and bottom
computed properties:
target.bottom = 75
println("The rectangle has a width of: \(target.width) and a height of: \(target.height)")
println("Its bottom-right corner is at (\(target.right), \(target.bottom))")
The following should now be output:
Its bottom-right corner is at (220.0, 75.0)
Read-Only Computed Properties
It’s also possible to define a read-only computed property, which has a getter but no setter. Here’s one that returns the area of our rectangle:
get {
return width * height
}
}
Alternatively, a shorthand version of a read-only computed property can be declared by removing the get
keyword and its braces:
return width * height
}
Property Observers
Swift provides property observers, which let you observe and respond to changes in a stored property’s value. You can define one or both of the following observers on a property: willSet
and didSet
.
The willSet
observer is called just before a property’s value is changed, while didSet
is called immediately after a property’s value has been modified.
Here’s a simple example where we define observers for our Rect
structure’s width
property:
willSet {
println("Setting width from: \(width) to: \(newValue)")
}
didSet {
println("Width changed from: \(oldValue) to: \(width)")
}
}
We’ve used shorthand declarations for both of our above observers. The willSet
observer exposes a default parameter named newValue
, which contains the value that the property is about to be set to. The didSet
observer exposes a default parameter named oldValue
, which contains the property’s previous value before it was set.
If you’d rather not use shorthand declarations for your observers then you can specify a parameter with each observer instead:
willSet
println("Setting width from: \(width) to: \(
}
didSet
println("Width changed from: \(
}
}
Notice that there’s no need to explicitly specify the type of each observer’s parameter as the type is inferred from the property that is being observed.
Before moving on, let’s look at a practical use for property observers. We’ll define a didSet
observer for our rectangle’s width
property that prevents it from being set to a negative value:
var left, top: Double
var width :Double
didSet {
if width < 0 {
width = 0
right = left + width
}
}
}
var height: Double
var right :Double {
get {
return left + width
}
set {
width = newValue – left
}
}
var bottom :Double {
get {
return top + height
}
set {
height = newValue – top
}
}
init(atX x: Double, andY y: Double, withWidth w: Double, andHeight h: Double) {
left = x
top = y
width = w
height = h
}
init(atPoint p: Point, withWidth w: Double, andHeight h: Double) {
self.init(atX: p.x, andY: p.y, withWidth: w, andHeight: h)
}
}
didSet
observer for our structure’s width
property now go ahead and add one for its height
property. As with the width
property, ensure that the rectangle’s height cannot be set to a negative value. Verify your code works by setting an instance’s height to a negative value then reading the current value back. Type Properties
While a stored property belongs to an instance of a struct, a type property belongs to the struct itself. It’s important to note that there will only ever be one copy of a type property, no matter how many instances of that type are created.
A type property is defined with the static
keyword and must also be given a default value when defined. Here’s an example of a type property that keeps track of the number of Rect
instances that have been created:
var top, left: Double
:
}
We can then increment it during every instance initialization:
static var instanceCount = 0
var top, left :Double
:
init(atX x: Double, andY y: Double, withWidth w: Double, andHeight h: Double) {
left = x
top = y
width = w
height = h
}
}
Dot syntax is used to get and set the value of type properties. However, unlike instances, type properties are queried and set on the type, rather than the instance itself. Here’s the line from our initailizer where our type property is incremented:
We can just as easily obtain the type property’s value:
Structs as Constants
Our Rect
and Point
examples have used variable stored properties. However, creating a constant instance of any of these structures will prevent you from being able to change the instance’s properties, even though those properties have been defined as variables.
Take the following for example:
origin.x = 100
Attempting to change the value of the instance’s x
property will result in the following compile-time error: Cannot assign to 'x' in 'origin'
.
Methods
Unlike languages such as C and Objective-C, Swift lets you define methods on a structure. In fact, you can even define a method on an enumeration too.
Since methods are more commonly associated with classes, and to give you time to digest what’s been covered so far, we’ll hold off and cover them in the next tutorial.
Next Time
We’ve seen just how flexible functions in Swift can be. ActionScript and TypeScript developers will find that the form of a basic Swift function isn’t a million miles away from what they’re used to. iOS developers on the other hand will no-doubt be delighted to find that complex Objective-C style functions with local and external parameter names for each parameter can also be constructed.
The second half of this tutorial was spent covering structures, which along with functions, are used as building blocks for any programs you write. Covering both functions and structures here was important as they both naturally lead onto the concept of classes, which we’ll cover in part four.
See you soon.