Swift Classes

In this article, we will explain classes in Swift.

Basics of Swift Classes

In Swift, a class can group properties that store values and methods that provide related functionality, similar to structs. You can define a class and then create instances when needed.

The syntax for classes and structs is almost the same, but classes support inheritance and can define deinitializers, among other differences.

Properties can be initialized either at the declaration or within init(). If any property is left uninitialized when creating an instance, Swift will throw an error.

Unlike structs, which provide a default initializer automatically, classes require an initializer if properties don't have default values, otherwise you'll get an error.

Since classes are more complex, Swift's official documentation recommends using structs unless you specifically need class features.


To define a class in Swift, use the class keyword as follows:

class ClassName {
    var property1: Type1
    var property2: Type2

    init() {
        // initializer body
    }

    func methodName() {
        // method body
    }
}

For example, here's a class Person with name and age properties and a greet method:

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

If you want to define a deinitializer, you can use deinit { ... }.


How to Use Classes in Swift

To create an instance of a class, you can write:

var or let variableName = ClassName(property1: value1, property2: value2, ...)

To update a property, use ClassName.property = newValue. To call a method, use ClassName.method().


For example, let's use the Person class defined earlier:

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

let person1 = Person(name: "Daniel", age: 20)
person1.greet()

let person2 = Person(name: "Amelia", age: 15)
person2.greet()

The output will be:

Hello! My name is Daniel, and I'm 20 years old.
Hello! My name is Amelia, and I'm 15 years old.

Classes are Reference Types in Swift

Another key difference between classes and structs is that classes are reference types, while structs are value types.

For example, if you copy a class instance into a new variable and update a property, both variables will point to the same instance, so the original is updated as well:

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

var p1 = Person(name: "Emma", age: 15)

var p2 = p1
p2.age = 20

p1.greet()
p2.greet()

The result shows that updating p2 also updates p1:

Hello! My name is Emma, and I'm 20 years old.
Hello! My name is Emma, and I'm 20 years old.

If you change this code to use struct instead, p1 and p2 will be separate instances, so modifying one does not affect the other.


Also, unlike structs, class instances created with let can still have their property values modified, because the reference itself is constant, not the object it points to:

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

let p1 = Person(name: "Emma", age: 15)

let p2 = p1
p2.age = 20

p1.greet()
p2.greet()

The output is the same as before:

Hello! My name is Emma, and I'm 20 years old.
Hello! My name is Emma, and I'm 20 years old.

If you change this code to use struct, you'll get an error when trying to modify p2's property.

Swift Class Example 1


Inheriting Classes and Defining Subclasses

To define a subclass in Swift, you write class SubclassName: ParentClassName when declaring the class.

Within the init() method of the subclass, you can call the initializer of the parent class using super, like this:

class DerivedClassName: ParentClassName {
    init() {
      super.init()
   }
}

Now, let's actually create and use a subclass in Swift.

We will define a subclass called Employee that inherits from the Person class defined earlier.

The Employee class will add a department property in addition to the name and age properties inherited from Person.

The init() method of Employee will take name, age, and department as parameters. It will then call the base class initializer super.init() with just name and age.

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

class Employee: Person {
    var department: String
    
    init(name: String, age: Int, department: String) {
        self.department = department
        super.init(name: name, age: age)
   }
}

let emp1 = Employee(name: "Emma", age: 30, department: "Sales")

print(emp1.name)
print(emp1.age)
print(emp1.department)
emp1.greet()

The output will be:

Emma
30
Sales
Hello! My name is Emma, and I'm 30 years old.

In addition to department, the Employee class can also use the name and age properties and the greet() method inherited from the Person class.


Adding Methods to Subclasses in Swift

Next, let's add a method to the Employee subclass.

We'll use a property called paidVacationDays to represent the number of paid vacation days, with an initial value of 20.

Then we'll add a method called usePaidVacationDays(). This method takes a number of days as input: if there are enough vacation days left, it will deduct them and display the remaining number; otherwise, it will display a message saying there aren't enough vacation days.

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

class Employee: Person {
    var department: String
    var paidVacationDays: Int = 20
    
    init(name: String, age: Int, department: String) {
        self.department = department
        super.init(name: name, age: age)
    }
    
    func usePaidVacationDays(days: Int) {
        if paidVacationDays >= days {
            paidVacationDays -= days
            print("Remaining paid vacation days: \(paidVacationDays)")
        } else {
            print("Not enough paid vacation days.")
        }
    }
}

let emp1 = Employee(name: "Emma", age: 30, department: "Sales")
emp1.usePaidVacationDays(days: 3)
emp1.usePaidVacationDays(days: 20)

The output will be as follows:

Remaining paid vacation days: 17
Not enough paid vacation days.

On the first call, 3 days were subtracted from the initial 20, leaving 17. On the second call, it tried to subtract 20 days even though only 17 remained, so the message “Not enough paid vacation days.” was displayed.


Overriding Methods from a Base Class in Swift

If the methods of a base class do not fit your needs in a subclass, you can override them.

To override a method, define it again in the subclass using the override keyword. When that method is called, the subclass version will run instead of the base class version.


For example, here's how to override the greet() method from the base class:

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

class Employee: Person {
    var department: String
    var paidVacationDays: Int = 20
    
    init(name: String, age: Int, department: String) {
        self.department = department
        super.init(name: name, age: age)
    }
    
    func usePaidVacationDays(days: Int) {
        if paidVacationDays >= days {
            paidVacationDays -= days
            print("Remaining paid vacation days: \(paidVacationDays)")
        } else {
            print("Not enough paid vacation days.")
        }
    }
    
    override func greet() {
        print("Hello, I'm \(name). I work in the \(department.lowercased()) department.")
    }
}

let emp1 = Employee(name: "Emma", age: 30, department: "Sales")
emp1.greet()

The output is as follows, and the greet() method of the subclass class is executed, not the base class.

Hello, I'm Emma. I work in the sales department.

That wraps up our explanation of classes in Swift.