Swift Tutorial Part 2: Types and Operations

Welcome to the second part of this learning Swift mini-series, where you’ll learn to use strings, type conversion, type inference and tuples. By Lorenzo Boaro.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Multi-line Strings

Swift has a neat way to express strings that contain multiple lines. This can be rather useful when you need to put a very long string in your code.

You do it like so:

let bigString = """
  You can have a string
  that contains multiple
  lines
  by
  doing this.
  """
print(bigString)

The three double-quotes signify that this is a multi-line string. Handily, the first and final new lines do not become part of the string. This makes it more flexible as you don’t have to have the three double-quotes on the same line as the string.

In the case above, it will print the following:

You can have a string
that contains multiple
lines
by
doing this.

Notice that the two-space margin in the multi-line string literal is stripped out of the result. Swift looks at the number of leading spaces on the final three double-quotes line. Using this as a baseline, Swift requires that all lines above it have at least that much space so that it can remove it from each line. This lets you format your code with pretty indentation without affecting the output.

Tuples

Sometimes, data comes in pairs or triplets. An example of this is a pair of (x, y) coordinates on a 2D grid. Similarly, a set of coordinates on a 3D grid is comprised of an x-value, a y-value and a z-value.

In Swift, you can represent such related data in a very simple way through the use of a tuple.

A tuple is a type that represents data composed of more than one value of any type. You can have as many values in your tuple as you like. For example, you can define a pair of 2D coordinates wherein each axis value is an integer, like so:

let coordinates: (Int, Int) = (2, 3)

The type of coordinates is a tuple containing two Int values. The types of the values within the tuple, in this case Int, are separated by commas surrounded by parentheses. The code for creating the tuple is much the same, with each value separated by commas and surrounded by parentheses.

Type inference can infer tuple types, too:

let coordinatesInferred = (2, 3) // Inferred to be of type (Int, Int)

You could similarly create a tuple of Double values, like so:

let coordinatesDoubles = (2.1, 3.5) // Inferred to be of type (Double, Double)

Or you could mix and match the types comprising the tuple, like so:

let coordinatesMixed = (2.1, 3) // Inferred to be of type (Double, Int)

And here’s how to access the data inside a tuple:

let coordinates = (2, 3)
let x1 = coordinates.0
let y1 = coordinates.1

You can reference each item in the tuple by its position in the tuple, starting with zero. So, in this example, x1 will equal 2 and y1 will equal 3.

Note: Starting with zero is a common practice in computer programming and is called zero indexing.

In the previous example, it may not be immediately obvious that the first value, at index 0, is the x-coordinate and the second value, at index 1, is the y-coordinate. This is another demonstration of why it’s important to always name your variables in a way that avoids confusion.

Fortunately, Swift allows you to name the individual parts of a tuple and you can be explicit about what each part represents. For example:

let coordinatesNamed = (x: 2, y: 3) // Inferred to be of type (x: Int, y: Int)

Here, the code annotates the values of coordinatesNamed to contain a label for each part of the tuple.

Then, when you need to access each part of the tuple, you can access it by its name:

let x2 = coordinatesNamed.x
let y2 = coordinatesNamed.y

This is much clearer and easier to understand. More often than not, it’s helpful to name the components of your tuples.

If you want to access multiple parts of the tuple at the same time, as in the examples above, you can also use a shorthand syntax to make it easier:

let coordinates3D = (x: 2, y: 3, z: 1)
let (x3, y3, z3) = coordinates3D

This declares three new constants, x3, y3 and z3, and assigns each part of the tuple to them in turn. The code is equivalent to the following:

let coordinates3D = (x: 2, y: 3, z: 1)
let x3 = coordinates3D.x
let y3 = coordinates3D.y
let z3 = coordinates3D.z

If you want to ignore a certain element of the tuple, you can replace the corresponding part of the declaration with an underscore. For example, if you were performing a 2D calculation and wanted to ignore the z-coordinate of coordinates3D, then you’d write the following:

let (x4, y4, _) = coordinates3D

This line of code only declares x4 and y4. The _ is special and simply means that you’re ignoring this part for now.

Note: You’ll find that you can use the underscore — also called the wildcard operator — throughout Swift to ignore a value.

A Whole Lot of Number Types

You’ve been using Int to represent whole numbers. An Int is represented with 64-bits on most modern hardware and with 32-bits on older — or more resource-constrained — systems. Swift provides many more number types that use different amounts of storage. For whole numbers, you can use the explicit signed types Int8, Int16, Int32 and Int64. These types consume 1, 2, 4 and 8 bytes of storage, respectively. Each of these types use 1-bit to represent the sign.

If you are only dealing with non-negative values, there are a set of explicit unsigned types that you can use. These include UInt8, UInt16, UInt32 and UInt64. While you cannot represent negative values with these, the extra 1-bit lets you represent values that are twice as big as their signed counterparts.

Here is a summary of the different integer types and their storage characteristics. Most of the time, you’ll just want to use an Int. These become useful if your code is interacting with another piece of software that uses one of these more exact sizes, or if you need to optimize for storage size.

int_sizes

You’ve been using Double to represent fractional numbers. Swift offers a Float type, which has less range and precision than Double, but requires half as much storage. Modern hardware has been optimized for Double, so it is the one that you should reach for unless you have good reason not to.

float_sizes

Most of the time, you will just use Int and Double to represent numbers but, every once in a while, you might encounter the other types. You already know how to deal with them. For example, suppose you need to add together an Int16 with a UInt8 and an Int32. You can do that like so:

let a: Int16 = 12
let b: UInt8 = 255
let c: Int32 = -100000

let answer = Int(a) + Int(b) + Int(c) // Answer is an Int