Swift tutorial: Reference types

The other day, I was chatting with some colleagues, who were interviewing people for a Swift Dev job opening. They said the killer question was, what’s the difference between a ‘struct’ and a ‘class’?. I was surprised. That’s something every swift developer should know. For that reason, I decided to write this post.

what is a reference?

Imagine a reference as a link to a value. With a reference, we access the data. Instances of a class are called “objects”, and you cannot directly assign them to a variable, you reference it.

The most common way to create a reference type in Swift is using a class

1
2
3
class MyClass {
var a = 1
}

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

1
2
3
4
var myClassInstance = MyClass()
var anotherInstance = myClassInstance
anotherInstance.a = 2
myClassInstance.a

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

1
2
3
4
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.

1
2
3
4
5
6
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
2
3
4
5
6
1 == 1 // true
let x = 400
let y = 250
x == y // false

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

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
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

Surprise! Yes, functions are reference types.

You can assign them to variables:

1
2
3
let aFunction = {
return 50
}

You can pass them as parameters:

1
2
3
4
5
func anotherFunction(aFunctionAsParameter: () -> Int) -> Int {
return aFunctionAsParameter()
}
anotherFunction(aFunctionAsParameter: aFunction)

They are one of the most relevant features of Swift.

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.

1
2
3
4
5
6
7
8
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.

1
2
3
4
5
6
7
8
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.

Please, let me know your thoughts and questions.

Partager Commentaires

Swift tutorial: Value types

I created this site to help people improve their code. And that’s still the mission today. Over the years, and especially in the recent months, I’ve seen so many young programmers get confused with some of the basic foundations of Swift. That’s why I decided to create this series of swift tutorials explaining the basics of the language.

So, let’s get started!

What is Swift and why Apple created it?

Swift is a programming language created by Apple. It’s easy to learn, but in my opinion difficult to master, however, the easy part will do for most of what you’ll need. Swift is a strongly typed language and has a familiar syntax for developers that come from other famous languages.

Swift was created, among other reasons, to make building apps for the Apple platform beginner friendly. Previously the scene was dominated by Objective-C. A powerful language with a syntax peculiar enough to confuse less experienced developers. Therefore, Apple came with this new pleasant-to-write language, not only attract young developers but also the very experienced ones.

If you want to learn more about what is Swift, you can visit the official website.

What is a type?

A type is an attribute we give to our data to tell the compiler how we intend to use it. In Swift, there are two kinds of data types: the “value” types and the “reference” types.

To keep the right pace, in this article we are going to talk about just value types.

What is a value?

A value is an instance of some data, like a number or text. In Swift, most of the commonly used value types are represented with struct, like Int, Bool, String, etc. But also another common way to represent a value type is enum. Here are some examples of values in Swift:

1
2
3
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.

Introducing Variables

So again, what do we do with a value? We store it and run operations on it. How? Well, first we need to label it.

A variable is just a label that helps you identify your value for later use.

1
var a = "Hello world"

It’s important to understand, that When we run an operation that mutates our value, we don’t change the original, but instead, a new one gets created and replaces the old value. Knowing this will help you decide what kind of data type to use when coding.

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

Constants

Constants are mostly like variables but, once they’re assigned with a value, they cannot be reassigned. In Swift, they are declared with the keyword let.

1
let c = 1

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

Passing Values Around

When working with value types, if we assign a variable to another, the value gets copied from one to the other. While the two variables may have the same value, changing one won’t affect the other.

1
2
3
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.

Conclusion

This is all for now about value types in Swift. Hope you liked it and feel excited to go and play with what you’ve learned. On the next part, I’m going to talk about reference types.

If you like what you just read or want me to continue writing about the Swift basics, please subscribe to receive on your email a monthly summary of the activity of the blog. Also, please share this content to keep me motivated. And if you have any questions or something to add, let me know on the comments section below.

Bye 😉

Partager Commentaires

Swift Tip - Using variadic parameters

Want your array parameters to look way cooler?

Boring

1
2
3
4
5
func trapInUpsideDown(people: [String]) {
// ... your code here
}
trapInUpsideDown(people: [“Dustin”,”Mike”,”Lucas”, "Will"])

Cooler look

1
2
3
4
5
func trapInUpsideDown(_ people: String...) {
// ... your code here
}
trapInUpsideDown(“Dustin”,”Mike”,”Lucas”, "Will")

😉

Partager Commentaires

Swift Tip - Printing multiple variables at once?

Wanna print multiple variables?

Problem

1
2
3
4
5
let name = "Tony"
let lastName: "Stark"
print(name)
print(lastName)

Better solution

Wrap them on a tuple.

1
2
3
4
let name = "Tony"
let lastName: "Stark"
print((name, lastName))

Awh, syntactic sugar ☺️

Partager Commentaires

Swift Tip - Ditch the semicolon

Every time you use a semicolon (;) in swift, a tree dies 🙄. Don’t roll your eyes! It’s true.

Don’t use them, they are a thing of the past. They make your code look ugly and nasty.

Look how ugly:

1
2
let lukeSkywalker = JediKnight();
lukeSkywalker.savePrincessLeia();

Now look how beautiful:

1
2
let lukeSkywalker = JediKnight()
lukeSkywalker.savePrincessLeia()

Nice, clean, perfect 😌.

Partager Commentaires

Swift Tip - Closure retain cycle

Avoid calling self inside your closure as much as possible.

1
2
3
let tower = { [weak self] rapunzel in
return self?.knight.save(rapunzel)
}

What if self is nil? I know what you’re thinking. Forget unowned! Just pass a capture list with the properties you need inside the closure.

1
2
3
let tower = { [knight] rapunzel in
return knight.save(rapunzel)
}

And that is how it is done.

Partager Commentaires