Advertisement
  1. Code
  2. Swift
Code

Swift From Scratch: An Introduction to Classes and Structures

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called Swift From Scratch.
Swift From Scratch: Closures
Swift From Scratch: Inheritance and Protocols

In the previous articles of this series, we covered the basics of the Swift programming language. If you followed along, you should now have a solid understanding of variables, constants, functions, and closures. It's now time to use what we've learned so far and apply that knowledge to the object-oriented concepts available in Swift.

To understand the concepts discussed in this tutorial, it's important that you have a basic understanding of object-oriented programming. If you're not familiar with classes, objects, and methods, then I recommend you first read up on these topics before continuing with this article.

1. Introduction

In this article, we're going to explore the fundamental building blocks of object-oriented programming in Swift, classes and structures. In Swift, classes and structures feel and behave very similar, but there are a number of key differences that you need to understand to avoid common pitfalls.

In Objective-C, classes and structures are very different. This isn't true for Swift. In Swift, for example, both classes and structures can have properties and methods. Unlike C structures, structures in Swift can be extended and conform to protocols.

The question is "What is the difference between classes and structures?" We'll revisit this question later in this article. Let's first explore what a class looks like in Swift.

2. Terminology

Before we start working with classes and structures, I'd like to clarify a few commonly used terms in object-oriented programming. The terms classes, objects, and instances often confuse people that are new to object-oriented programming and it's therefore important that you know how Swift uses these terms.

Objects and Instances

A class is a blueprint or template for an instance of that class. The term object is often used to refer to an instance of a class. In Swift, however, classes and structures are very similar and it's therefore easier and less confusing to use the term instance for both classes and structures.

Methods and Functions

Earlier in this series, we worked with functions. In the context of classes and structures, we usually refer to functions as methods. In other words, methods are functions that belong to a particular class or structure. In the context of classes and structures, you can use both terms interchangeably since every method is a function.

3. Defining a Class

Let's get our feet wet by defining a class. Fire up Xcode and create a new playground. Remove the contents of the playground and add the following class definition.

The class keyword indicates that we're defining a class named Person. The implementation of the class is wrapped in a pair of curly braces. Even though the Person class isn't very useful in its current form, it is a proper, functional Swift class.

Properties

As in most other object-oriented programming languages, a class can have properties and methods. In the updated example below, we define three properties:

  • firstName, a variable property of type String?
  • lastName, a variable property of type String?
  • gender: a constant property of type String

As the example illustrates, defining properties in a class definition is very similar to defining regular variables and constants. We use the var keyword to define a variable property and the let keyword to define a constant property.

The above properties are also known as stored properties. Later in this series, we'll learn about computed properties. As the name implies, stored properties are properties that are stored by the class instance. They are very similar to properties in Objective-C.

It's important to note that every stored property needs to have a value after initialization or be defined as an optional type. In the above example, we give the gender property an initial value of "female". This tells Swift that the gender property is of type String. Later in this article, we'll take a look at initialization in more detail and explore how it ties in with initializing properties.

Even though we defined the gender property as a constant, it is possible to change its value during the initialization of a Person instance. Once the instance has been initialized, the gender property can no longer be modified since we defined the property as a constant property with the let keyword. This will become clearer later in this article when we discuss initialization.

Methods

We can add behavior or functionality to a class through functions or methods. In many programming languages, the term method is used instead of function in the context of classes and instances. Defining a method is almost identical to defining a function. In the next example, we define the fullName method in the Person class.

The method fullName is nested in the class definition. It accepts no parameters and returns a String. The implementation of the fullName method is straightforward. Through optional binding, which we discussed earlier in this series, we access the values stored in the firstName and lastName properties.

We store the first and last name of the Person instance in an array and join the parts with a space. The reason for this somewhat awkward implementation should be obvious, the first and last name can be blank, which is why both properties are of type String?.

Instantiation

We've defined a class with a few properties and a method. How do we create an instance of the Person class? If you're familiar with Objective-C, then you're going to love the conciseness of the following snippet.

Instantiating an instance of a class is very similar to invoking a function. To create an instance, the name of the class is followed by a pair of parentheses, the return value is assigned to a constant or variable.

In our example, the constant john now points to an instance of the Person class. Does this mean that we can't change any of its properties? The next example answers this question.

We can access the properties of an instance using the convenience of the dot syntax. In the example, we set firstName to "John", lastName to "Doe", and gender to "male". Before we draw any conclusions based on the above example, we need to check for any errors in the playground.

Setting firstName and lastName doesn't seem to cause any problems. Assigning "male" to the gender property, however, results in an error. The explanation is simple. Even though john is declared as a constant, that doesn't prevent us from modifying the Person instance. There's one caveat though, only variable properties can be modified after initializing an instance. Properties that are defined as constant cannot be modified after initialization.

Note that the I emphasized after in the previous sentence. A constant property can be modified during initialization an instance. While the gender property shouldn't be changed once a Person instance has been created, the class wouldn't be very useful if we could only instantiate female Person instances. Let's make the Person class a bit more flexible.

Initialization

Initialization is a step in the lifetime of an instance of a class or structure. During initialization, we prepare the instance for use by populating its properties with initial values. The initialization of an instance can be customized by implementing an initializer, a special type of method. Let's add an initializer to the Person class.

Note that the name of the initializer, init, isn't preceded by the func keyword. In contrast to initializers in Objective-C, an initializer in Swift doesn't return the instance that's being initialized.

Another important detail is how we set the gender property with an initial value. Even though the gender property is defined as a constant property, we can set its value in the initializer. We set the gender property by using the property name, but it's also fine to be more explicit and write the following:

In the above example, self refers to the instance that's being initialized. This means that self.gender refers to the gender property of the instance. We can omit self, as in the first example, because there's no confusion what property we are referring to. This isn't always the case though. Let me explain what I mean.

Parameters

In many situations, you want to pass initial values to the initializer to customize the instance you're instantiating. This is possible by creating a custom initializer that accepts one or more arguments. In the following example we create a custom initializer that accepts one argument, gender, of type String.

There are two things to note. First, we are required to access the gender property through self.gender to avoid ambiguity since the local paramater name is equal to gender. Second, even though we haven't specified an external parameter name, Swift by default creates an external parameter name that is equal to the local parameter name. The result is the same as if we were to prefix the gender parameter with a # symbol.

In the following example, we instantiate another Person instance by invoking the custom initializer we just defined.

Even though the initial value of the gender property is set to "female" in the class definition, by passing a value for the gender parameter we can assign a custom value to the constant gender property during initialization.

Multiple Initializers

As in Objective-C, a class or structure can have multiple initializers. In the following example, we create two Person instances. In the first line, we use the default initializer. In the second line, we use the custom initializer we defined earlier.

4. Defining a Structure

Structures are surprisingly similar to classes, but there are a few key differences. Let's start by defining a basic structure.

At first glance, the only difference is the use of the struct keyword instead of the class keyword. The example also shows us an alternative approach to supply initial values to properties. Instead of setting an initial value for each property, we can give properties an initial value in the initializer of the structure. Swift won't throw an error, because it also inspects the initializer to determine the initial value—and type—of each property.

5. Classes and Structures

You may start to wonder what the difference is between classes and structures. At first glance, they look identical in form and function, with the exception of the class and struct keywords. There are a number of key differences though.

Inheritance

Classes support inheritance whereas structures don't. The following example illustrates this. The inheritance design pattern is indispensable in object-oriented programming and, in Swift, it's a key difference between classes and structures.

In the above example, the Person class is the parent or superclass of the Student class. This means that the Student class inherits the properties and behavior of the Person class. The last line illustrates this. We initialize a Student instance by invoking the custom initializer we defined earlier in the Person class.

Copying and Referencing

The following concept is probably the most important concept in Swift you'll learn today, the difference between value and reference types. Structures are value types, which means that they are passed by value. An example illustrates this concept best.

We define a structure, Point, to encapsulate the data to store a coordinate in a two-dimensional space. We instantiate point1 with x equal to 0 and y equal to 0. We assign point1 to point2 and set the x coordinate of point1 to 10. If we output the x coordinate of both points, we discover that they are not equal.

Structures are passed by value while classes are passed by reference. If you plan to continue working with Swift, you need to understand the previous statement. When we assigned point1 to point2, Swift created a copy of point1 and assigned it to point2. In other words, point1 and point2 each point to a different instance of the Point structure.

Let's now repeat this exercise with the Person class. In the following example, we instantiate a Person instance, set its properties, assign person1 to person2, and update the firstName property of person1. To see what passing by reference means for classes, we output the value of the firstName property of both Person instances.

The example proves that classes are reference types. This means that person1 and person2 refer to or reference the same Person instance. By assigning person1 to person2, Swift doesn't create a copy of person1. The person2 variable points to the same Person instance person1 is pointing to. Changing the firstName property of person1 also affects the firstName property of person2, because they are referencing the same Person instance.

As I mentioned several times in this article, classes and structures are very similar. What separates classes and structures is very important. If the above concepts aren't clear, then I encourage you to read the article one more time to let the concepts we covered sink in.

Learn More in Our Swift Programming Course

If you're interested in taking your Swift education to the next level, you can take a look at our full course on Swift development.

Conclusion

In this installment of Swift from Scratch, we've started exploring the basics of object-oriented programming in Swift. Classes and structures are the fundamental building blocks of most Swift projects and we'll learn more about them in the next few lessons of this series.

In the next article, we continue our exploration of classes and structures by taking a closer look at properties and inheritance.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.