Codementor Events

Understanding Swift types

Published May 28, 2018Last updated Nov 24, 2018
Understanding Swift types

The other day, I was discussing with some friends just how great of a language Swift is. They were not really happy with what I was saying. Well, how could they? They are die-hard Java developers. However, I'm flattered they showed some interest in iOS, and, especially Swift.

Coming from everything–is-an-object-land, it's kind of difficult to remember that value types exist. It is in Swift that they really shine.

Let me show you how Swift manages types.

Value

A value is a representation of some entity, like a number or text.

They don't change — once created, they stay the same forever.

true
"Hello!"
[1,2,3,4]

But what do we do with just values? We need to be able to identify them so we can move them around.

Variable

When we give a name to a value, we are making use of variables.

var a = "Hello world"

Since arrays are values, and don't change, when we call a mutating function of the value, we don't change it's original value, but instead create a new one and replace the content of the variable that holds it.

var b = ["Hello", "World"]
b.append("!") 
// b = ["Hello", "World", "!"]

Constants

Constants are variables that, once assigned a value, cannot be reassigned. In Swift they are declared with let.

let c = 1

trying to assign c = 2 will result in a compiler error

Passing Values Around

When we assign a variable to another, the value is copied from one to the other. While the two variables may have the same value, changing one won't affect the other one.

var d = b // ["Hello", "World", "!"]
d.append("from Italy") // ["Hello", "World", "!", "from Italy"]

Notice how b stays the same, while the value of d changed.

Some common value types are struct and enum.

Reference

A reference is link to a value. Through a reference, we can access the value. The most common reference type in Swift is classes, declared with class. Instances of a class are called "objects" and can be referenced with variables. You cannot directly assign an object to a variable.

class MyClass {
    var a = 1
}

Unlike value types, references do change their referenced value when the assigned variable is mutated.

var myClassInstance = MyClass()
var anotherInstance = myClassInstance
anotherInstance.a = 2
myClassInstance.a

Even when a reference is assigned with let, we can still modify the referenced object.

let constantReference = MyClass()
constantReference.a // = 1
constantReference.a = 3
constantReference.a // = 3

That means that the constant is the reference, not the object. With value types we wouldn't be able to do this.

struct MyStruct {
    var b = 5
}

let myValue = MyStruct()
myValue.b = 8 // Error! 👎

Trying to change the value of b in myValue.b = 8 would result in a compiler error.

Now let's complicate things a little. Let's create a value type that contains a reference type.

class ReferenceType {
    var aValueProperty = 5
}

struct ValueType {
    var aReference = ReferenceType()
    var aValue = "hello"
}

// Create the value
let valueInstance = ValueType()

// copy it
var anotherValueInstance = valueInstance
// change it's properties
anotherValueInstance.aReference.aValueProperty = 1000
anotherValueInstance.aValue = "bye"

// Now lets check what changed in the original
valueInstance.aReference.aValueProperty // 1000
valueInstance.aValue // "hello"

Notice how aValue maintained it's original value in valueInstance, whereas aReference.aValueProperty changed for both the original and the copied value, even when it was only modified in the copy.

This behaviour is what we call a "shallow copy", where we copy all the values but the referenced value stays the same. This is because we only copied the references. On the other hand, when we copy absolutely everything, including the referenced values, we call this a "deep copy".

This may be a little confusing, so take your time to understand it.

Equality of Reference Types

When we use the equals operator (==), we are checking if two values are the same.

1 == 1 // true

let x = 400
let y = 250

x == y // false

The same behaviour doesn't occur when comparing custom value types.

struct AnotherValueType {
    let aValue = 10
}

let m = AnotherValueType()
let n = m

m == n // Error: Binary operator '==' cannot be applied to two 'AnotherValueType' operands

This behaviour happened because AnotherValueType doesn't implement the Equatable protocol, but that's a topic for another time.

The same happens for reference types. What we can do with reference types is check if they are the same with the === operator

class AnotherClass {
    let x = [1,2,3]
}

let j = AnotherClass()
let k = j
j == k // Error!
j === k // 👍

The === operator checks if two references point to the same value.

Functions

The final value type that we are going to talk about are functions. In Swift, functions are also values — specifically reference types.

You can assign them to variables:

let aFunction = {
    return 50
}

You can pass them as parameters:

func anotherFunction(aFunctionAsParameter: () -> Int) -> Int {
    return aFunctionAsParameter()
}

anotherFunction(aFunctionAsParameter: aFunction)

These kinds of functions that receive another function as a parameter are called "higher order functions". Some famous ones are: map, reduce, filter, among others.

let grades = [10,5,3,9]

// lets see who passed:
let passed = grades.filter { (aGrade) -> Bool in
    return aGrade >= 5
}

// passed = [10,5,9]

Functions can be declared inside other functions, like in a for-loop or inside many other scopes. But since functions are reference types when they are declared inside another scope and passed further, after the local scope ends, the local variables used inside the function aren't destroyed.

func outerFunction() -> () -> String {
    let aVariable = "Hello from outer function"
    let innerFunction = {
        return aVariable
    }
    return innerFunction
}
outerFunction()()

The functions that hold variables outside of their scope are usually called closures. But it doesn't mean that other functions declared with func aren't also closures.

Not The End

Having a good understanding of how types work in Swift will help you decide when to use what, and more significantly, to better architect your apps.

I hope you liked this brief study on Swift types.

Please, let me know your thoughts and questions.

Discover and read more posts from Oscar Vicente Gonzalez Greco
get started