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…

  • The basics of the Swift programming language from part one and two

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:

func greetJedi() {
  println("Greetings Master Jedi!")
}

Convention in Swift (as it is in many other languages) dictates that functions use lowerCamelCase names. For example, 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:

greetJedi()

This will result in the following message being displayed within the sidebar:

Greetings Master Jedi!

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:

func greetJedi(name: String) {
  println("Greetings Master \(name)!")
}

Now you can pass a name as an argument to the greetJedi function:

greetJedi("Obi-Wan Kenobi")

The line above will result in the following being printed to the appropriate output:

Greetings Master Obi-Wan Kenobi!

Let’s write another version of greetJedi that takes two parameters: a forename and a surname.

func greetJedi(forename: String, surname: String) {
  println("Greetings Master \(forename)!")
}

Here are a few examples of our greetJedi function being called:

greetJedi("Obi-Wan", "Kenobi")
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 Obi-Wan!
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:

func greetJedi(forename: String, surname: String) -> String {
  return "Greetings Master \(forename)!"
}

If you’re coming from a language such as Objective-C or C, where the return type is listed first in a function’s declaration, then you may find the order of Swift’s return type a little off putting. However, persevere and I promise you’ll grow to like it.

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("Obi-Wan", "Kenobi"))
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 Obi-Wan!
Greetings Master Anakin!
Greetings Master Qui-Gon

It’s beneficial to know that all functions actually return a value, even those that do not explicitly define one. Functions without a defined return type actually return a special value of type Void. This is simply an empty tuple, which can also be written as ().

Take this function for example:

func saySomething() {
  println("Hullo World!")
}

Although there is no need to do so, it could also be written as:

func saySomething() -> () {
  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:

func getFirstAndLast(names: [String]) -> (first: String, last: String) {
  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:

Luke and Yoda

Playground Experiment
Our 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:

func greetJedi(forename forename: String, surname surname: String) -> String {
  return "Greetings Master \(forename)!"
}

And here is the function being called:

greetJedi(forename: "Luke", surname: "Skywalker")

As a comparison, here’s how the call looked before external parameter names were defined:

greetJedi("Luke", "Skywalker")

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:

func setPoint(atX x: Float, andY y: Float) {
  // Implementation goes here
}

setPoint(atX: 10.5 andY: 15.25)

As an alternatively we can alter the function’s name and apply an external name to just the second parameter:

func setPointAtX(x: Float, andY y: Float) {
  // Implementation goes here
}

setPointAtX(10.5, andY: 15.25)

If you’re an Objective-C developer then external parameter names should help provide the same level of expressiveness that you’re used to when defining and calling Objective-C methods.

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:

func greetJedi(forename forename: String, surname surname: String) -> String {
  return "Greetings Master \(forename)!"
}

to this:

func greetJedi(#forename: String, #surname: String) -> String {
  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:

func describeJedi(name: String, energy: Int = 50) -> String {
  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:

describeJedi("Luke Skywalker")

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:

describeJedi("Yoda", energy: 100)

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 (_) where the external parameter name would normally be. Here’s an example:

func describeJedi(name: String, _ energy: Int = 50) -> String {
  // Implementation goes here
}

Your call to describeJedi would now look like this:

describeJedi("Yoda", 100)

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 () directly after a parameter’s type to indicate that it is a variadic parameter:

func averageStarfightersLost(numbers: Int...) -> Double {
  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:

var avg = averageStarfightersLost(10, 5, 0, 4, 1, 0, 0, 6)
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.

Playground Experiment
As stated, a variadic parameter can accept zero or more input values. Our 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:

func describeJedi(name: String, energy: Int) -> String {
  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:

func describeJedi(var name: String, energy: Int) -> String {
  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:

func swapLightsabers(inout lightsaber1: String, inout lightsaber2: String) {
  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 lukesLightsaber = "blue"
var yodasLightsaber = "green"

println("Luke’s lightsaber is \(lukesLightsaber)")
println("Yoda’s lightsaber is \(yodasLightsaber)")


swapLightsabers(&lukesLightsaber, &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:

Luke’s lightsaber is blue
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 (&) needs to be placed directly before the variable’s name when passing it as an argument to an in-out parameter.

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:

struct StructName {
  // Implementation goes here
}

Here’s a concrete example of a structure that represents a two-dimensional point:

struct 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:

var target = Point()

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:

var target = Point(x: 64, y: 12.5)

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:

var target = Point(x: 64)

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 target = Point()
var x = target.x
var y = target.y

Here’s how you assign new values to your instance’s stored properties:

target.x = 34.5
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 point1 = Point(x: 64, y: 32)
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:

point1.x = 128
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:

point1.x: 128
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:

point1.x: 128
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.

Arrays, dictionaries, and enumerations are also value types in Swift. This may come as a surprise to many since collection types such as arrays and dictionaries are typically represented by classes in other programming languages. Swift however opts to model them as structures instead.

The following code snippet will demonstrate this for arrays:

var arrayA = ["a", "b", "c"]
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:

struct Point {
  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:

struct Point {
  var x, y: Double
  init() {
    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:

struct Point {
  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:

struct Point {
  var x, y :Double
  init() {
    x = 0.0
    y = 0.0
  }
  init(x: Double, y: Double) {
    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.

Objective-C developers will be familiar with the 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:

let origin = Point()
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 (_) before each of your initializer’s local parameters. Alternatively you can explicitly define your own custom external parameter names. We’ll actually do this shortly with another struct that we’ll define.

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:

struct Point {
  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:

struct Point {
  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:

let origin = 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:

struct Rect {
  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:

var rectangle = Rect(left: 0, top: 0, width: 200, height: 50)

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:

struct Point {
  var x = 0.0, y = 0.0
}

struct Rect {
  var left, top: Double
  var width, height: Double
  init(p: Point, w: Double, h: Double) {
    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:

var rectangle = Rect(p: Point(x: 0, y: 0), w: 200, h: 50)

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:

struct Rect {
  var left, top: Double
  var width, height: Double
  init(atPoint p: Point, withWidth w: Double, andHeight h: Double) {
    left = p.x
    top = p.y
    width = w
    height = h
  }
}

We can now create an instance with a much more natural initializer call:

var rectangle = Rect(atPoint: Point(x: 0, y: 0), withWidth: 200, andHeight: 50)

Objective-C programmers will be comfortable with this much more verbose way of calling functions.

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:

struct Rect {
  var left, top: Double
  var width, height: Double
  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)
  }
}

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:

struct Rect {
  var left, top: Double
  var width, height: Double
  var right: 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:

set(newRight) {
  width = newRight – left
}

to:

set {
  width = newValue – left
}

Let’s now go ahead and define a bottom computed property. We’ll also use the shorthand setter declaration just discussed:

struct Rect {
  var left, top: Double
  var width, 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)
  }
}

Now let’s create an instance of a Rect and make use of our right and bottom computed properties:

var target = Rect(atPoint: Point(x: 10, y: 50), withWidth: 200, andHeight: 50)
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:

The rectangle has a width of: 200.0 and a height of: 50.0
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.width = 180
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:

The rectangle has a width of: 180.0 and a height of: 100.0
Its bottom-right corner is at (190.0, 150.0)

Finally, set the right and bottom computed properties:

target.right = 220
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:

The rectangle has a width of: 210.0 and a height of: 25.0
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:

var area: Double {
  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:

var area: Double {
  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:

var width: Double {
  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:

var width: Double {
  willSet(newWidth) {
    println("Setting width from: \(width) to: \(newWidth)")
  }
  didSet(oldWidth) {
    println("Width changed from: \(oldWidth) to \(width)")
  }
}

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:

struct Rect {
  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)
  }
}

Playground Experiment
We have a 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.

Type properties are commonly known as static member variables in other languages such ActionScript and C++.

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:

struct Rect {
  static var instanceCount = 0
  var top, left: Double
  :
}

We can then increment it during every instance initialization:

struct Rect {
  static var instanceCount = 0
  var top, left :Double
  :
  init(atX x: Double, andY y: Double, withWidth w: Double, andHeight h: Double) {
    Rect.instanceCount++
    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:

Rect.instanceCount++

We can just as easily obtain the type property’s value:

println("\(Rect.instanceCount) instances of Rect have been created.")

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:

let origin = Point()
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.