Classes

In Swift, a class acts as a blueprint for creating objects.

For example, here is a class that represents a person:

class Person {
    var name = "Alice"
    var age = 32
}

And here you create a Person object:

var person = Person()

In this guide, you learn how to create objects using classes in Swift.

Also, you are going to learn what is the distinction between a struct and a class, as they seem very similar.

This chapter is part of a completely free Swift for Beginners guide. Make sure you have completed all the chapters from the start before reading this one.

Disclaimer: To learn Swift, you have to repeat everything you see in this guide and in the following guides. If you only read the guides, you are not going to learn anything!

Swift Classes

The Swift programming language is also known as an object-oriented programming language.

In other words, Swift supports creating objects (using classes and structs).

An object is a collection of variables, constants, and methods and it represents some entity, such as a Person, Course, User, or anything that sort.

In Swift, a class acts as a blueprint for objects.

Real-Life Analogy

To clarify the relationship between a class and an object, let’s consider a real-life example.

In real life, a class could for example be a prototype of a house.

This prototype specifies the details of the house, such as the walls, roof, windows, colors, and so on.

Based on this prototype we then build a house.

Or if we are building a village, we can build multiple houses based on the same prototype.

Anyway, the house represents an object.

Syntax: How to Define a Class

To create a class in Swift, use the class keyword followed by the name of the class and the implementation:

class ClassName {
    // class implementation
}

For example, let’s create a class that represents a person:

class Person {
    var name = "Alice"
    var age = 32
}

As you can see, the variable name represents the name of the Person object. Similarly, the age represents the age of the Person.

Similar to structs, variables and constants that live inside a class are called properties.

Syntax: How to Create Objects

When you have implemented a class, you want to use it.

In other words, you want to create objects from the class.

Given a class called ClassName, here is the general syntax of how to create an object from it:

var object = ClassName()

Let’s see an example by creating a Person object based on a Person class:

// The class implementation
class Person {
    var name = "Alice"
    var age = 32
}

// Creating an object
var person1 = Person()

Here person1 is an object of a Person class.

Now you can use the person1 object to access its properties as described by the class.

Access Class Properties

To access the properties of a class in Swift, you need to use the dot notation.

This way you can either read or modify the properties of an object.

For instance, given the Person class and person1 object from the previous section, let’s change the name and display both the new name and the age:

person1.name = "Bob"

print(person1.name)
print(person1.age)

Output:

Bob
32

Here we used the person1.name to access the name that belongs to the person1 object. Similarly, we used person1.age to access the age property of the object.

Because both of these properties are declared as variables inside the class, we can modify both. If they were constants, you could not modify them.

Recap & Full Example Code

Here is all the code used in the previous sections:

class Person {
    var name = "Alice"
    var age = 32
}

var person1 = Person()

person1.name = "Bob"

print(person1.name)
print(person1.age)

To recap, we created a Person class with properties name and age. Then we created a Person object and changed its name. Finally, we inspected the properties of the object by printing them out.

Feel free to play with it as much as needed to understand what is going on.

Next, let’s create multiple objects from the same class.

Create Multiple Objects

As described earlier, you can use the class as a blueprint for creating objects.

In the previous chapter, you only created a single Person object. However, you can create multiple objects of the same class.

Instead of working with the Person class, let’s create a completely new class that represents buildings.

This class is called Building and it stores the height of a building.

class Building {
    var height = 0.0
}

Now, let’s create two Building objects and change their heights to something appropriate.

let house = Building()
let skyScraper = Building()

house.height = 10.0
skyScraper.height = 250.0

print("The height of the house is:", house.height)
print("The height of the sky scraper is:", skyScraper.height)

Output:

The height of the house is: 10.0
The height of the sky scraper is: 250.0

To take home, the objects are independent of one another. Also, you can create as many objects as you want.

If you change the height of the house, it has no effect on the height of the skyScraper or the underlying class called Building.

Next, let’s take a look at functions inside a class.

Functions in Class (Methods)

You can place a function inside a class.

You can then call this function with the dot notation, similar to how you access the properties of a class.

A function inside a class is called a method.

For instance, let’s create a class that represents a cuboid.

A cuboid is a 3D shape that is specified by width, height, and length.

class Cuboid {
    var width = 0.0
    var height = 0.0
    var length = 0.0
}

Now, if you want to calculate the volume of a cuboid, you can do it outside the class.

For example:

var box = Cuboid()

box.width = 10.0
box.height = 10.0
box.length = 10.0

let volume = box.width * box.height * box.length
print(volume)

Output:

1000.0

However, if you have multiple cuboids, repeating the calculation of the volume is unnecessary.

Instead, you can specify a method that calculates the volume of a Cuboid object. Then you can just call the method on any Cuboid object to figure out its volume.

Let’s modify the Cuboid class by adding a method for calculating the volume:

class Cuboid {
    var width = 0.0
    var height = 0.0
    var length = 0.0

    func volume() -> Double {
        return width * height * length
    }
}

Now, let’s create a Cuboid object and calculate its volume with the volume() method:

var box = Cuboid()

box.width = 10.0
box.height = 10.0
box.length = 10.0

print(box.volume())

Output:

1000.0

Now, let’s talk about the class initialization process.

Class Initialization

In the previous examples, the class properties had some default initial values.

For example:

class Person {
    var name = "Alice"
    var age = 32
}

But the problem with this is that now each time you create a Person object, it is going to have the same property values.

In other words, you always create Alice who is 32 years old.

If you want to change the name or the age, you need to do it separately. This is not practical and it wastes lines of code.

What if you wanted to directly create an object with different property values?

To make this possible, you need to write a class initializer method.

The initializer method makes it possible to give the property values as arguments to the class when creating objects.

In Swift, the initializer method is called init().

It is a special method you can implement into your class to customize the initialization process.

For example, let’s create a class that represents a pet.

Furthermore, let’s make it possible to initialize Pet objects with different names by passing the name as an argument to the Pet class.

To do this, you need to implement the init() method.

Here is how it looks in code:

class Pet {
    var name: String

    init(name: String) {
        self.name = name
    }
}

Now, you can create Pet objects with different names:

let cat = Pet(name: "Luna")
let dog = Pet(name: "Spot")
let turtle = Pet(name: "Leonardo")

But how does it work?

Let’s inspect the implementation of the Pet class a bit closer:

class Pet {
    var name: String

    init(name: String) {
        self.name = name
    }
}

Here we create a new class called Pet.

The Pet class has a property called name.

However, instead of giving the name a default value, this time we only declare its data type to be a String. This way Swift knows it does not have a value yet but it is going to be a String.

Then we specify the init() method. Similar to any other function in Swift, this method takes a single argument, that is, the name of the Pet.

This is the argument you pass into the class when you create an object from it.

Now comes the most puzzling part where it says self.name = name. This assigns the user-specified name argument to the Pet object itself.

It is as if the new Pet object said “Take the name given by the user and assign it to the name property of mine”.

Notice that the name of the argument does not need to be the same as the name of the property in the class.

For example, the Pet class could just as well look like this:

class Pet {
    var name: String

    init(nameArg: String) {
        self.name = nameArg
    }
}

let cat = Pet(nameArg: "Luna")

However, you should use the same name to follow the best practices!

Doing this can be confusing at first, but when you have done it dozens of times, it all becomes evident.

Now you have a basic understanding of the initialization process of a class.

The initialization is commonly used just like above, that is, to assign initial values to the class object.

However, you can run any valid Swift code in the initializer if you wish to.

Let’s see an example.

Example: Initializer with Custom Code

For instance, let’s create a Weight class that represents weight in pounds.

However, let’s write an initializer such that someone who only knows kilos can still use the Weight class.

class Weight {
    var pounds: Double

    init(kilos: Double) {
        let kilosAsPounds = kilos * 2.208
        self.pounds = kilosAsPounds
    }
}

Now, you can initialize a Weight object in kilos but still have the Weight object using pounds:

let w1 = Weight(kilos: 100.0)
print(w1.pounds)

Output:

220.8

So when the user of this class specifies a weight in kilos, it automatically gets converted to pounds before it gets assigned to the pounds property.

This is a quick demonstration of how you can use the initializer to run custom code in addition to just assigning initial values.

Last but definitely not least, let’s compare structs and classes.

Structs vs Classes in Swift

In this chapter, you learned how to use classes to create objects in your code.

However, you could have used a struct in each of the examples and the code would have worked exactly the same way.

For instance, the Pet class could have been a struct:

struct Pet {
    var name: String

    init(name: String) {
        self.name = name
    }
}

let cat = Pet(name: "Luna")
print(cat.name)

Output:

Luna

Or the Cuboid could have been a struct too:

struct Cuboid {
    var width = 0.0
    var height = 0.0
    var length = 0.0

    func volume() -> Double {
        return width * height * length
    }
}

var box = Cuboid()

box.width = 10.0
box.height = 10.0
box.length = 10.0

print(box.volume())

Output:

1000.0

So what is the difference between a struct and a class in Swift?

Difference 1: Structs Support Memberwise Initialization

One key difference is you can initialize structs without specifying the init() method. In other words, the struct supports memberwise initialization unlike a class.

For example:

struct Person {
    var name: String
}

let p1 = Person(name: "Alice") // Works

The argument name “Alice” gets assigned to the name property of the struct.

However, if you try to do the same with a class, you are going to run into an error:

class Person {
    var name: String
}

let p1 = Person(name: "Alice") // Fails

Output:

error: 'Person' cannot be constructed because it has no accessible initializers

The only way to make this type of initialization work with a class is by writing an init() method to it.

class Person {
    var name: String

    init(name: String) {
        self.name = name
    }
    
}

let p1 = Person(name: "Alice") // Works

Difference 2: Classes Are Reference Types, Structs Are Value Types

Another key difference between a struct and a class is that:

  1. Classes are reference types.
  2. Structs are value types.

In other words, when you assign a class object to a variable, you are creating an alias to it that still refers to the original object.

For example:

class Person {
    var name: String

    init(name: String) {
        self.name = name
    }
}

var p1 = Person(name: "Alice")
// Assign p1 to p2
var p2 = p1

p1.name = "Bob"

// Both p1 and p2 names changed:
print(p1.name)
print(p2.name)

Output:

Bob
Bob

Here both p1 and p2 changed even though we only changed p1. This is because p1 and p2 are the same objects.

However, let’s repeat this with an identical struct:

struct Person {
    var name: String
}

var p1 = Person(name: "Alice")
// Assign p1 to p2
var p2 = p1

p1.name = "Bob"

// Both p1 and p2 names changed:
print(p1.name)
print(p2.name)

Output:

Bob
Alice

Here only p1 was changed.

This is because when dealing with structs, an assignment like var p2 = p1 creates an independent copy of the original object p1.

Difference 3: Classes Support Inheritance

Lastly, classes are more flexible than structs as they support inheritance.

In other words, you can create a subclass-parent class hierarchy with classes.

With structs, you cannot create any kind of hierarchies.

However, at this point, class inheritance is something you have not seen yet, so we are not going to dig deeper into it yet.

Now the big question is which one should you use, a class or a struct?

Should I Use Struct or a Class in Swift?

As a rule of thumb, use structs as much as you can.

Only use classes if you cannot get the job done with structs.

For example, if you need a reference type, use a class. Or if you need inheritance, use a class.

In each example you saw in this chapter, you should have used a struct!

But why did I introduce you to classes if you should only rarely use them?

Classes are a key feature of Swift. Even though you are going to use them less than structs, you are still going to use them a lot.

Also, classes are an extremely important feature in other object-oriented programming languages, such as Java, Python, or Kotlin.

Next Chapter: Subclassing in Swift

Conclusion

In this chapter, you learned how to use classes in Swift.

To recap, a class is a blueprint for creating objects.

Similar to structs, a class allows you to create a new data type in your program.

A class bundles together related variables, constants, and methods.

For example, a class can represent a person who has a name and age and can introduce himself.

Unlike structs, a class needs an initializer method called init(). This is used when you want to initialize objects by passing properties as arguments to the class.

Classes and structs look similar.

The main differences are:

  1. Structs support memberwise initialization, classes don’t.
  2. Classes are reference types, structs are value types.
  3. Classes support inheritance, structs don’t.

As a rule of thumb, structs over classes as much as you can!

We mentioned inheritance and subclassing when we discussed the differences between a struct and a class. In the next chapter, you are going to learn what is subclassing in Swift.

Next Chapter: Subclassing in Swift