Types are a fundamental aspect of the Swift language. Swift's type system was pretty easy to understand in the early days. As the language matured, the type system gained in complexity to increase its flexibility. That flexibility came at a cost, though. Today's type system is pretty advanced and some aspects make your head spin.
In this series, we explore Swift's type system. The goal is to better understand what types are, what role they play, and how we can use types to write code that is robust yet flexible. We start with the basics. It is important to learn the fundamentals first. Later in this series, we discuss the more advanced concepts, such as generics and existential types. We also explore the newest additions of the language, the any and some keywords.
Type Safety
Swift is a type-safe language, which means, simply put, that each value has a type. The benefit of type safety is that the code you write is safer and common type errors are caught easily. Fire up Xcode and create a playground. Add an import statement for the Foundation framework at the top.
import Foundation
Declare a variable with name message.
import Foundation
var message
The compiler throws an error if we don't specify the type of the variable. Because Swift is a type-safe language, we are required to declare the type of each constant and variable.

We have two options. We declare the type of the message variable or we assign a value to the variable so that Swift can infer its type. The latter is better known as type inference.
import Foundation
var message = "hello world"
Because we assign a string to message, Swift infers that the type of message is String. We can ask the variable what its type is by passing the variable to the type(of:) function.
import Foundation
var message = "hello world"
type(of: message)

The value of a type cannot change. The message variable is of type String and that remains true for as long as the message variable exists. The compiler throws an error if we attempt to assign an integer to message.
import Foundation
var message = "hello world"
message = 15

Type Inference
Swift's type inference is convenient, but it is sometimes necessary to explicitly declare the type of a value. In the following example, we declare a constant with name number and assign the value 15 to it. Swift infers that the type of number is Int.
import Foundation
let number = 15
type(of: number)

It is convenient, but it is possible we intended number to be of type Float. We can be more explicit about the type of number by annotating the declaration of number with its type.
import Foundation
let number: Float = 15
type(of: number)

Optionals
It is important to understand that the optionality of a value is part of that value's type. What does that mean? Take a look at the following example. We declare a variable, message, of type String?.
import Foundation
var message: String?
We assign a string, hello world, to the message variable.
import Foundation
var message: String?
message = "hello world"
We then define a function, printMessage(_:), that accepts a string as an argument. The printMessage(_:) function writes the string into the standard output. Let's pass the value stored in the message variable to the printMessage(_:) function.
import Foundation
var message: String?
message = "hello world"
func printMessage(_ message: String) {
print(message)
}
printMessage(message)
The compiler doesn't like this. The error the compiler throws explains why. The message variable is of type String? while the printMessage(_:) function expects an argument of type String. Even though the message variable stores a string, we cannot pass it as an argument to the printMessage(_:) function because the types don't match.

We can resolve the error by safely unwrapping the value the message variable stores. This pattern is better known as optional binding. We use an if statement to bind the value stored in the message variable to a constant, unwrappedMessage.
import Foundation
var message: String?
message = "hello world"
func printMessage(_ message: String) {
print(message)
}
if let unwrappedMessage = message {
printMessage(unwrappedMessage)
}
This is a very common pattern in Swift. It is so common that the Swift team decided to shorten the syntax as of Swift 5.7.
import Foundation
var message: String?
message = "hello world"
func printMessage(_ message: String) {
print(message)
}
if let message {
printMessage(message)
}
Static and Dynamic Typing
Swift is a statically typed language, which means that the compiler performs type checking at compile time. The code you write won't compile if it contains type errors. The compiler threw an error when we tried to pass the value stored in the message variable to the printMessage(_:) function because the types didn't match. Having the compiler check your code at compile time ensures type errors are caught early. The drawback of a statically typed language is reduced flexibility.
Dynamically typed languages, such as Ruby and JavaScript, perform type checking at run time. While the details are more nuanced, the moment type checking occurs is what separates statically typed languages from dynamically typed languages. Performing type checking at run time comes with a risk. Type errors are easier to miss because there is no compiler that catches them at compile time. The benefit of a dynamically typed language is more dynamism.
One is not better than the other, but it is an important factor to consider when choosing the technology stack for a project. The first version of the Cocoacasts website was powered by Ruby, a dynamically typed language. While I enjoyed the flexibility of a dynamically typed language, I missed the guard rails of a compiler. Last year, I rewrote the Cocoacasts website in Swift and I haven't looked back since. I have been using Swift since the early days so I am biased. I won't deny that. That said, it is nice to know that the compiler has your back, eliminating a whole category of bugs.
What's Next?
In this episode, we explored what makes Swift a type-safe, statically typed language. Type safety is a fundamental aspect of the language so it is important that you understand what it means. In the next episode, we take a look at the limitations of type safety and how we can bend the rules.