Two types and operations

1: type conversion

Sometimes you have data in one format and need to convert it to another format.

Var INTEGER: Int = 100 var decimal: Double = 12.5 INTEGER = decimalCopy the code

If you do this and then get an error on the third line, Swift will prompt:

Cannot assign value of type 'Double' to type 'Int'
Copy the code

Some programming languages are less strict and perform such transformations silently. Experience has shown that such silent, automated transformations are a source of software bugs that often hurt performance. Swift avoids these problems by disallowing the assignment of a value of one type to another. (Swift is a strongly typed language)

Remember, computers rely on us programmers to tell them what to do. In Swift, this includes explicit type conversions. If you want the conversion to happen, you have to say so!

Instead of simply assigning, you need to say that you want to explicitly convert the type. You do this:

integer = Int(decimal)
Copy the code

The assignment on the third line now explicitly tells Swift that you want to convert from the original type Double to the new type Int.

Note: Assigning a decimal value to an integer in this case results in a loss of precision: the integer variable ends up with a value of 12 instead of 12.5. That’s why it’s important to be clear. Swift wants to make sure you know what you’re doing and that you can lose data when you perform a cast.

2: operator of mixed types

So far, you’ve only seen operators acting independently on integers or doubles. But what if you have an integer and you want to multiply it by a double? Here’s what you can do:

Let hourlyRate: Double = 19.5 let hoursWorked: Int = 10 Let totalCost: Double = hourlyRate * hoursWorkedCopy the code

If you try to do this, you’ll get an error in the last line:

Binary operator '*' cannot be applied to operands of type 'Double' and 'Int'
Copy the code

This is because in Swift, you cannot apply the * operator to mixed types. This rule also applies to other arithmetic operators. At first glance, this may seem surprising, but Swift is quite helpful.

Swift requires that you explicitly indicate what it means to multiply Int by Double, because the result can only be one type. Do you want the result to be Int, converting Double to Int before performing the multiplication? Or do you want the result to be a Double, converting Int to Double before performing the multiplication?

In this case, you want the result to be Double. You don’t need Int, because in this case Swift converts the hourlyRate constant to Int to perform the multiplication, rounding it to 19 and losing the precision of Double.

You need to tell Swift that you want it to double the hours, like this:

let totalCost: Double = hourlyRate * Double(hoursWorked)
Copy the code

Now, when Swift multiplies them, each operand is going to be a Double, so totalCost is also going to be a Double.

3: Type inference

So far, every variable or constant you’ve seen in this book contains a type annotation. You might ask yourself why you write :Int and :Double at all, since the right side of the assignment is already an Int or Double. To be sure, this is superfluous; Your super-smart brain can see that without much effort.

As it turns out, the Swift compiler can infer this as well. It doesn’t need you to keep telling it the type — it can figure it out. This is done through a process called type inference. Not all programming languages have this feature, but Swift does, and it’s a key component of Swift’s power as a language.

So, you can simply delete types in most places you see them. For example, consider the following constant declaration:

let typeInferredInt = 42
Copy the code
4. Strings

Numbers are important in programming, but they’re not the only data types you need to use in your application. Text is also a very common data type used to represent things like people’s names, addresses, and even words in books. All of these are examples of text that your application might need to process.

Like any good programming language, Swift can handle characters and strings directly. It is implemented through the data types Character and String, respectively. In this section, you’ll learn about these data types and how to use them.

####### 4.1 Characters and strings The character data type can store a single character. Such as:

let characterA: Character = "a"
Copy the code

It can store any character, even emoticons:

Let characterDog: Character = "🐶"Copy the code

But this data type is designed to hold only a single character. The String data type, on the other hand, stores multiple characters. Such as:

let stringDog: String = "Dog"
Copy the code

The right-hand side of this expression is a string literal; It is the syntax that Swift uses to represent strings. Of course, type inference also applies here. If you remove the type from the above declaration, then Swift has done the right thing by making stringDog a String constant:

let stringDog = "Dog" // Inferred to be of type String
Copy the code

Note: there are no literal characters in Swift. A character is a string of length 1. However, Swift deduces that any string literal is of type string, so if you want a character instead, you must make the type explicit.

####### 4.2 String Stitching

You can do much more than just create a simple string. Sometimes you need to manipulate a string, and a common way to do this is to combine it with another string. In Swift, you can do this in a fairly simple way: using the addition operator. Just as you can add numbers, you can add strings:

var message = "Hello" + " my name is "
let name = "Matt"
message += name // "Hello my name is Matt
Copy the code

You need to declare Message as a variable, not a constant, because you want to modify it. You can add string literals, as shown in the first line, or string variables or constants, as shown in the last line.

You can also add characters to a string. However, Swift’s strict typing requirements mean that this must be done explicitly, just as it must be done explicitly when dealing with one Int and another Double. To add a character to a string, you can do this:

let exclamationMark: Character = "!" Message += String(exclamationMark) // "Hello my name is Matt!"Copy the code

Using this code, you can explicitly convert a character to a string before adding it to the message.

####### 4.3 String Insertion

You can also build a string using interpolation, a special Swift syntax that lets you build a string in an easy-to-read way:

message = "Hello my name is \(name)!" // "Hello my name is Matt!"Copy the code

I’m sure you’ll agree that this is more readable than the examples in the previous section. It is an extension of the literal syntax of a string, and you can replace parts of a string with other values. The values to be inserted are enclosed in parentheses followed by backslashes. This syntax works in the same way as building strings from other data types, such as numbers:

Let oneThird = 1.0/3.0 let oneThirdLongString = "One third is \(oneThird) as a decimal.Copy the code

Here, you use Double in the interpolation. At the end of this code, your oneThirdLongString constant will contain the following:

One third is 0.33333333333333 as a decimal.Copy the code

Of course, it takes an infinite number of characters to represent a third as a decimal, because this is a repeating decimal. String interpolation with Double gives you no control over the precision of the resulting string. This is an unfortunate consequence of using string interpolation: it’s simple to use, but you can’t customize the output.

4.4 Multi-line Strings

Swift has a succinct way of expressing strings containing multiple lines. This can be useful when you need to put a very long string in your code. You do this:

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

Three double quotes indicate that this is a multi-line string. Conveniently, the first and last lines do not become part of the string. This makes it more flexible, because you don’t have to put three double quotes on the same line as the string. In the above example, it would print the following:

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

Notice that two blanks in the multi-line string literal are removed from the result. Swift looks at the number of leading Spaces in the last three double quote lines. Using this as a baseline, Swift requires that all lines above have at least enough space so that it can remove it from each line. This lets you format your code with nice indentation without affecting the output.

5. A tuple

Sometimes the data is paired or triplet. An example of this is a pair of (x, y) coordinates on a two-dimensional grid. Similarly, a set of coordinates on a 3D grid consists of x values, y values, and Z values. In Swift, you can represent this related data in a straightforward way by using tuples.

A tuple is a type that represents data composed of multiple values of any type. You can have as many values in a tuple as you want. For example, you can define a pair of 2D coordinates where each axis has an integer value, like this:

let coordinates: (Int, Int) = (2, 3)
Copy the code

The coordinates are of type (Int, Int). The value types within the tuple (Int in this case) are separated by commas and enclosed in parentheses. The code for creating a tuple is essentially the same, with each value separated by a comma and enclosed in parentheses.

Type inference can also infer tuple types:

let coordinates = (2, 3)
Copy the code

You can similarly create a two-valued tuple like this:

Let coordinatesDoubles = (2.1, 3.5) // DNA to be of type (Double, Double)Copy the code

Or you can mix and match the types that make up tuples, like this:

Let coordinatesMixed = (2.1, 3) // DNA to be of type (Double, Int)Copy the code

Here’s how to access data in a tuple:

let x1 = coordinates.0
let y1 = coordinates.1
Copy the code

You can refer to each item by its position in the tuple, starting at 0. In this case, x1 is equal to 2, y1 is equal to 3.

In the previous example, it may not be 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 example of why it is important to always name variables in a way that avoids confusion.

Fortunately, Swift allows you to name the individual parts of a tuple, so you know exactly what each part represents. Such as:

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

Here, the code annotates the coordinatesNamed value to contain the tag for each part of the tuple. Then, when you need to access each part of a tuple, you can access it by its name:

let x2 = coordinatesNamed.x
let y2 = coordinatesNamed.y
Copy the code

It’s clearer, it’s easier to understand. In general, it is helpful to name the components of a tuple. If you want to access multiple parts of a tuple at the same time, as in the example above, you can also use a shorthand syntax to simplify:

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

This declares three new constants, x3, y3, and z3, and assigns each part of the tuple to them in turn. This 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
Copy the code

If you want to ignore an element in a tuple, you can replace the corresponding part of the declaration with an underscore. For example, if you are performing a 2D calculation and want to ignore the Z coordinates of coordinates3D, you can write as follows:

let (x4, y4, _) = coordinates3D
Copy the code

This line of code only declares x4 and y4. _ is special, it just means you’re ignoring this part for now.

Five: A whole bunch of number types

You’ve been using ints for integers. Int is represented as 64-bit on most modern hardware and 32-bit on older or resource-constrained systems. Swift offers more digital types that use different storage capacities. For integers, you can use the explicit signed types Int8, Int16, Int32, Int64. These types consume 1, 2, 4, and 8 bytes of storage, respectively. Each type uses 1 bit to represent the symbol.

If you only deal with non-negative values, there is a set of explicit unsigned types that you can use. The values include UInt8, UInt16, UInt32, and UInt64. Although these values cannot be used to represent a negative number, the extra 1 bit can represent a value twice as large as the signed value. Below is a summary of the different integer types and their byte storage sizes. For the most part, you just need to use Int.

These can become useful if your code interacts with another software that uses these more precise sizes, or if you need to optimize the storage size.

You’ve been using Double for fractions. Swift provides a Float type that has less scope and precision than a Double, but requires half as much storage. Modern hardware has been optimized for Double, so it should be your first choice unless there’s a good reason to use Float.

Most of the time, you’ll only use ints and doubles for numbers, but you might occasionally run into other types. For example, suppose you need to add together an Int16, a UInt8, and an Int32. Here’s what you can do:

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

let answer = Int(a) + Int(b) + Int(c)  // answer is an Int
Copy the code
Type aliases

A useful feature of Swift is the ability to create your own type, which is essentially an alias for another type. This means you can give your type a more useful name to describe what it is, but really it’s just another type underneath it. This is called a type alias.

Creating a type alias is simple, as follows:

typealias Animal = String
Copy the code

This created a new type of animal called animal. When the compiler sees this type, it just treats it as a String. So here’s what you can do:

let myPet: Animal = "Dog"
Copy the code

This may not seem very useful now, but sometimes types get complicated, and creating aliases for them can give them a simpler and more explicit name. For example, you could do this:

typealias Coordinates = (Int, Int)
let xy: Coordinates = (2, 4)
Copy the code

This creates a type called Coordinates, which is a tuple of two ints, and then uses it.

As you see more and more Swift, you’ll see that type aliases can be very powerful and simplify code.

Seven separate Protocols

Although there are a dozen different number types, they are all easy to understand and use because they all support essentially the same operations. In other words, once you know how to use Int, it’s easy to use either style.

One of Swift’s really great features is that it formalizes the concept of type commonality through what’s called a protocol. By studying the protocol, you can immediately understand how the entire family of types that use the protocol works.

In the case of integers, functionality can be expressed as follows:

The arrows indicate compliance (sometimes referred to as adoption) with the protocol. While this diagram doesn’t show all the protocols that integer types follow – it gives you an idea of how things are organized.

Swift was the first protocol-based language. As you begin to understand the protocols behind these types, you can leverage the system in ways that other languages cannot.

By the end of this book, you’ll be exposed to existing protocols and even creating new ones of your own.