Software, Tech & Coding simplified.

Classes in Swift

In Swift, you can implement custom types using classes. A class acts as a factory for creating objects.

For example, let’s create a Fruit class and two Fruit objects:

class Fruit {
    let name: String
    var color: String

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

let banana = Fruit(name: "Banana", color: "Yellow")
let apple = Fruit(name: "Apple", color: "Red")

This is the quick answer to what is a class in Swift. But if you are unfamiliar with classes, there is so much more to learn.

In this guide, you are going to learn everything you need to know about classes in Swift.

Classes in Swift

A class acts as a blueprint for objects in Swift. You can create custom types by implementing classes.

For example, a social media app could have a User class for creating user objects.

To implement a class in Swift, use the class keyword and the following syntax:

class TypeName {
    // Class implementation here
}

Where:

  • The class keyword lets Swift compiler know that the type is a class.
  • The TypeName is the desired name of the class. Usually, the name begins with capital letter.
  • The implementation of the class goes in-between the curly braces {}. A class can contain variables, constants, and functions.

Here is an illustration of a class in Swift.

Classes in Swift
A class is used as a factory for creating objects.

Before writing your first class in Swift, you need to understand the initialization process of the class.

Class Initialization Process

Whenever you create an instance of a class, it has to be initialized. This means assigning the initial values to the object.

Initialization can be thought of as preparing the object for use.

A Swift class is initialized in the init() method.

The init() Method

The init() method is used to prepare an object. You should assign initial values to all the properties present in the object in the init() method.

The init() method is automatically called whenever you create an object.

For instance, let’s create a Fruit class that can be initialized by giving it a name and color arguments:

class Fruit {
    let name: String
    var color: String

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

Let’s examine the init() method to understand what is happening there.

  • The init() method takes two arguments: name and the color. These are the arguments you give as an input in when creating the Fruit object.
  • The self keyword refers to the instance itself. You can think of it as “This very Fruit object”.
  • In the init(), you save the user-specified name and color to the object. This happens with self.name = name and self.color = color.

Now, when you create a Fruit object, the init() method is triggered to prepare the object behind the scenes:

let apple = Fruit(name: "Apple", color: "Red")

Now you can access the properties of the Fruit instance. To access a member of a class, use the dot notation:

print("This is a \(apple.color) \(apple.name)")

Output:

This is a Red Apple

Initialization Rules

You can place any valid Swift code in the init() method.

The only thing required is to assign values for each property in the class before returning from the init() method.

To demonstrate this, let’s accidentally forget to assign a value to the color property in the initializer:

class Fruit {
    let name: String
    var color: String

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

This shows an error in the editor:

An error about unset stored properties in the init() method

This error makes sure both name and color have been assigned a value before exiting the init() method.

However, there is one exception to this rule: optionals.

An optional is a type that can be either a value or nil.

If a class has an optional property, you can leave it unspecified in the init() method.

For example, let’s modify the Fruit class such that the color property is optional. This makes it ok to leave it undefined in the initializer:

class Fruit {
    let name: String
    var color: String?

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

Other than this limitation, you can do pretty much anything in the init() method.

For instance, let’s capitalize the name and color properties automatically when creating a Fruit object:

class Fruit {
    let name: String
    var color: String

    init(name: String, color: String) {
        // Capitalize the name and color arguments
        self.name = name.uppercased()
        self.color = color.uppercased()
    }   
}

Now the init() method always changes the name and the color to the upper case when you create Fruit objects:

let banana = Fruit(name: "banana", color: "yellow")
print(banana.name, banana.color)

Output:

BANANA YELLOW

Now you have a good understanding of the initialization process of a class in Swift.

Next, let’s take a look at how you can implement multiple init() methods for a class.

Convenience Initializers — Add Multiple init() Methods to a Class

In Swift, you can implement multiple init() methods into a class. Any additional initializer is called a convenience initializer. A convenience initializer has to be written separately into an extension (more about extensions later). To create a convenience initializer, use the convenience keyword.

Also, remember that the convenience initializer follows the rules of the normal initialization process. This means you have to assign values to all properties of the class.

Usually, a convenience initializer does some preparations before calling the original init() method to create the object.

Let’s see an example.

Earlier you have been working with the Fruit class that has a single init() method.

class Fruit {
    let name: String
    var color: String

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

You can create Fruit objects by:

Fruit(name: "banana", color: "yellow")

Now, let’s say you want to create Fruit objects by extracting the name and the color properties from a string that looks like this: “Banana,Yellow”.

To pull this off you need to extract the name and color info from a string before initializing a Fruit object.

let info = "Banana,Yellow"

// Extract name and color
let parts = info.components(separatedBy: ",")
let name = parts[0]
let color = parts[1]

// Initialize the Fruit object
let banana = Fruit(name: name, color: color)
print(banana.name, banana.color)

Output:

Banana Yellow

But this process is impractical—You always have to repeat these same actions before creating objects.

To fix the problem, let’s create a convenience init() that parses the name and color before initialization:

class Fruit {
    let name: String
    var color: String

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

extension Fruit {
    convenience init(info: String) {
        // Extract the name and color from 'info'
        let parts = info.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1]
        // Create the instance by calling the original init() method
        self.init(name: name, color: color)
    }
}

Now you can create Fruit objects in the two desired ways:

let banana = Fruit(info: "Banana,Yellow")
let apple = Fruit(name: "Apple", color: "Red")

print(banana.name, banana.color)
print(apple.name, apple.color)

Output:

Banana Yellow
Apple Red

Now you know how class initialization works in Swift.

Accessing Class Properties

Now that you know how to implement classes you might wonder what to do with them.

A class object is useful as it stores data. Commonly, you can use objects to store, read, and modify data.

With the Fruit objects from the previous examples, you can read the color/name information. This happens using the dot notation.

For example:

let apple = Fruit(name: "Apple", color: "Red")

print("\(apple.color) \(apple.name)")

Output:

Red Apple

You can also modify the properties of an object as long as they are variables. To modify a property, access it via the dot notation and assign a new value to it.

For instance, let’s change the color of the apple:

apple.color = "Green"

Next, let’s take a look at how you can implement functions into classes in Swift.

Functions in Classes

In Swift, you can implement functions to classes. You can then call these functions on the objects to perform actions on them.

Creating a function into a class works the same way as implementing any other function in Swift. The difference is you need to use the self keyword inside the functions to access the properties of the object.

For example, let’s create a Fruit class that has an info() method. This method prints information about the object:

class Fruit {
    let name: String
    var color: String

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

    func info() {
        print("Hi, I am a \(self.color) \(self.name)")
    }
}

The info() function uses self to access the name and the color of the object.

Now it is possible to create Fruit objects and call help() on them using the dot notation.

For example:

let banana = Fruit(name: "Banana", color: "Yellow")
banana.info()

This results in the following output:

Hi, I am a Yellow Banana

Now you understand the basics of classes in Swift.

To put it all together:

  • Classes are used to introduce custom types.
  • A class can have properties and methods that can be called on the objects.
  • A class has to be initialized using the init() method.
  • There can be multiple init() methods on a class. They need to be implemented separately in an extension as convenience initializers.

Next, let’s take a look at a fundamental concept related to classes called inheritance.

Class Inheritance

Inheritance means subclassing. It means a parent class inherits properties and functionality to the child class.

Inheritance is useful as you can avoid repetition by forwarding functionality from a parent class to a child class.

Inheritance follows this simple syntax:

class ChildClass: ParentClass {
    // Implementation of the class
}

Where the ChildClass inherits the ParentClass.

For example, a Cat class can inherit properties from the Pet class because each Cat is also a Pet.

Class inheritance in Swift

Here is how it looks in code:

class Pet {
    var type: String
    var name: String

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

    func introduce() {
        print("Hi, I'm a \(self.type) and my name is \(self.name)")
    }
}

class Cat: Pet {
    init(name: String) {
        super.init(type: "Cat", name: name)
    }
}

Now you can create Cat objects and call properties and methods from the Pet class because they are inherited from the Pet:

let cat = Cat(name: "Luna")
cat.introduce()

Output:

Hi, I'm a Cat and my name is Luna

Feel free to read more about class inheritance here.

Class Extensions

An extension gives a way to extend the functionality of a class in Swift.

To create an extension, use the extension keyword like this:

extension TypeName {
    // Implement the extended behavior here
}

Where:

  • extension is a keyword that tells the Swift to extend the functionality of a class.
  • TypeName is the name of the existing class.

To demonstrate, let’s extend a Fruit class with an info() method:

class Fruit {
    let name: String
    var color: String

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

extension Fruit {
    func info() {
        print("Hi, I am a \(self.color) \(self.name)")
    }
}

Now it is possible to create Fruit objects and call info() on them:

let banana = Fruit(name: "Banana", color: "Brown")
banana.info()

Output:

Hi, I am a Brown Banana

Using extensions can be useful if you want to extend the behavior of an existing type without modifying the original class.

Also, as you already saw, if you want to create a convenience init to a class, you have to do it in an extension.

Structs vs Classes in Swift

As you may know, Swift also has structures. A structure behaves similarly to a class.

This can cause confusion especially if you are new to these concepts.

Let’s take a moment to understand the key differences between classes and structures in Swift.

The difference between classes and structures in Swift is that a class is a reference type and a class is a value type.

As a rule of thumb, prefer structs over classes! Use a class only when you need to use a feature not supported by a structure.

Here is a comparison between classes and structures in Swift.

FeatureStructClass
Inheritance
Deinitialization
Reference type
Value type

Let’s take a deeper dive into reference types and value types in Swift.

Classes Are Reference Types

A class is a reference type in Swift.

This means that a class object is not copied on an assignment. Instead, it starts pointing to the same object in memory.

For example, given the Fruit class:

class Fruit {
    let name: String
    var color: String

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

Let’s create two Fruit instances such that the first one is assigned to the second one:

var fruit1 = Fruit(name: "Banana", color: "Yellow")
var fruit2 = fruit1

Now, let’s change the color of the first Fruit:

fruit1.color = "Brown"

Then, let’s print the colors of the Fruit instances:

print(fruit1.color)
print(fruit2.color)

Output:

Brown
Yellow

As you can see, the colors are the same. Changing the color of fruit1 changed the color in fruit2 too. This proofs that fruit1 and fruit2 reference the same object.

reference types in swift

This is what it means to be a reference type.

Structs Are Value Types

In Swift, structures are value types.

A value type is an object that is copied on an assignment.

Let’s repeat the example from the previous chapter.

This time, given the Fruit structure:

struct Fruit {
    let name: String
    var color: String
}

Let’s create two Fruit instances such that the first one is assigned to the second one:

var fruit1 = Fruit(name: "Banana", color: "Yellow")
var fruit2 = fruit1

Now, let’s change the first Fruit instance:

fruit1.color = "Brown"

Then, let’s print the colors of the Fruit instances:

print(fruit1.color)
print(fruit2.color)

Output:

Brown
Yellow

The colors are different!

Changing the color in fruit1 does not affect fruit2. This is proof that fruit1 and fruit2 are independent even though fruit2 was created by assigning fruit1 to it.

Here is an illustration:

value type in swift

This is what characterizes being a value type in Swift.

Finally, let’s take a look at some common built-in structures in Swift.

Swift Basic Types Are Structures, Not Classes

Unlike you might guess, all the basic types in Swift are structures.

Here is a list of data types implemented as structures in Swift:

Conclusion

Today you learned what are classes in Swift.

To recap, a class allows you to implement custom types. You can use these to create objects in the code.

For example, a social media app could have a User class that allows creating users to the app.

Each class object has to be initialized. This happens using the init() method.

A class can have properties and methods that can be accessed/called on the objects.

The key difference between a struct and a class is:

  • A structure is a value type.
  • A class is a reference type.

Use structures instead of classes except for when you need a feature that structures do not have.

Thanks for reading.

Happy coding!

Further Reading

50 Swift Interview Questions

Structs in Swift

Share

Share on twitter
Share on linkedin
Share on facebook
Share on pinterest
Share on email