Back to blog

Object-Oriented Programming (OOP) in Swift


Aasif Khan
By Aasif Khan | Last Updated on December 7th, 2023 8:32 am | 5-min read

Object-Oriented Programming (OOP) helps you structure your Swift code with so-called classes. These classes have properties and functions, and classes can inherit attributes from each other.

This app development tutorial is an introduction of Object-Oriented Programming with Swift. We’ll dive into classes, objects, properties, functions, and a concept called inheritance. You’ll learn what the purpose of Object-Oriented Programming is, with live examples, and why it’s important in practical iOS development.

When you don’t use a structure like Object-Oriented Programming in your code, it’s like a waterfall: the code flows from top to bottom. Object-Oriented Programming, on the other hand, is more like a house: you define your code’s foundation, and build on top of that, room by room, one building block at a time.

Describe your app idea
and AI will build your App

OOP in Swift: Classes and Instances

Let’s start with the most important aspect of Object-Oriented Programming: classes. They are the building blocks of your code.

Here’s an example of a class in Swift:

class Car
{

}

Inside the squiggly brackets you can define properties and functions, that belong to this class Car. You can see a class as a structure for code, like a toolbox that contains a screwdriver, wrench, hammer and drill.

In Swift, you can create instances of a class. Like this:

class Car
{

}

let ferrari:Car = Car()

The constant ferrari now contains an instance of the class Car. You’ve created an instance with the initializer Car(), and with the assignment operator = you’ve assigned that instance to the constant ferrari.

You can now pass that instance of Car around in your code, just like any other value. The class Car is the type of ferrari, much like a variable score can be of type Int.

You can create as much instances of one class as you want. That’s the power of Object-Oriented Programming! You create the Car class once, and then use it to create an entire garage of cars. This is called code reuse, and it’s a staple in practical iOS development.

Instances of a class are often simply called objects.

It can be challenging to visualize abstract concepts like classes and instances. Let’s make an analogy:

  • A class is like the blueprint for a building. An architect draws the blueprint, and then passes it on to a foreman or construction worker.
  • An instance is like the building itself. The construction worker uses the blueprint to create the building.

A building isn’t an exact copy of its blueprint. It’s just a live representation of the blueprint, with windows, doors, a roof, etcetera.

When coding your app, you’re building an entire city by using a great number of blueprints. Some of these blueprints are provided by the “government” (i.e., Apple’s Cocoa Touch SDKs), such as the blueprint for a standard fire station and train station. Other blueprints are designed by you, the architect.

Your city is not just a pile of blueprints. You create instances of these blueprints: the actual buildings! And you create the blueprint for an apartment complex once, and then use it to build apartments all over the city.

Makes sense? Let’s move on to the next aspect of Object-Oriented Programming: properties and functions.

Swift has another important building block: structs. Structs (short for “structure”) are similar to classes, with a few exceptions: structs cannot be used for subclassing, and they are value types. Swift favors structs over classes for simple data types. You can learn more here: Struct vs. Class In Swift Explained.

Properties and Functions in OOP

A class can define properties and methods.

  • A property is like a variable that belongs to a class
  • A method is like a function that belongs to a class.

Properties store information that belongs to an instance of a class, and methods can execute tasks that belong to an instance of a class. This is very similar to variables and functions, except that the variables and functions are now part of a bigger structure – the class.

Do you see how a class structures information and tasks in your code? The class “wraps” variables and functions that belong together.

A method is a function that belongs to a class, but for the sake of simplicitly, we’re just going to call them functions.

Let’s look at an example:

class Car
{
var wheels:Int = 0
var maxSpeed:Int = 0
}

Here’s what happens:

  • You declare a class called Car with class Car. The class body goes between the squiggly brackets { and }.
  • You declare two properties: a property called wheels of type Int and another property called maxSpeed of type Int, both with a default value of 0.

It’s important to note that, in the above code, the wheels and maxSpeed properties are declared at the class level. They’re added right within the squiggly brackets of the class body.

Anything you can do with a variable, you can do with a property. A property has a type and you declare it with var, just like a variable. You can use type inference and declare constants with let.

The big difference, of course, is that these properties belong to a class. Every instance of the class Car will now have the properties wheels and maxSpeed.

We can create a fast Ferrari, like this:

let ferrari = Car()
ferrari.wheels = 4
ferrari.maxSpeed = 300

And we can create a slower All Terrain Vehicle (ATV), like this:

let atv = Car()
atv.wheels = 8
atv.maxSpeed = 80

That’s pretty cool, right? You use dot-syntax to access properties on an object, such as ferrari.wheels. Most properties can be read from (called getting) and written to (called setting), unless otherwise specified.

Want to learn more about variables? Check this tutorial: Variables and Constants in Swift Explained.

OK, now let’s continue with functions. Everything we’ve done with properties so far, applies to functions too. When a class declares a function, that function is available on every instance of that class.

Like this:

class Car
{
func drive()
{
print(“VROOOOOOM!!!”)
}
}

In the above example we’re declaring a function drive() on the class Car. It prints a simple line of text to the Console when the function is executed.

You can now call that function drive() on the Car class with the same dot-syntax. Like this:

let ferrari = Car()
ferrari.drive()
// Output: VROOOOOOM!!!

Subclassing and Inheritance

You now know that Object-Oriented Programming is used to structure your code in classes. A class can have properties, to store information, and functions, to execute tasks.

Object-Oriented Programming also has several advanced concepts, such as inheritance, polymorphism, protocols, delegation and composition. We’ll focus on inheritance, for now.

Let’s say we’ve defined a simple class called Vehicle. We can then create subclasses of Vehicle, such as:

  • Bicycle – a vehicle with no engine, pedals and 2 wheels
  • RaceCar – a vehicle with a powerful engine and 4 wheels
  • Bus – a large vehicle with 8 wheels and space for 50 people

See how each of these subclasses is-a-kind-of vehicle, and also define some attributes of their own? That’s what subclassing and inheritance is about.

Here’s the gist of it:

  • A subclass inherits properties and functions from its superclass
  • A subclass can override properties and functions from its superclass, replacing them with its own implementation
  • A subclass can extend its superclass with new properties and functions
  • A subclass and superclass can be type cast within their own hierarchy

The principle of inheritance enables you to create hierarchy in your code, and helps you reuse parts of your code. It doesn’t make sense to copy the attributes that different kinds of vehicles share, when you can reuse them.

Let’s take a look at some code. Here’s the base class Vehicle:

class Vehicle
{
var wheels:Int = 0

func drive()
{
print(“Driving this vehicle!”)
}
}

Then, let’s create a subclass:

class Bus: Vehicle
{
var seats:Int = 0
var gears:Int = 0

func openDoors()
{
print(“Opening bus doors…”)
}

override func drive()
{
print(“Out of the waaaayyy! This bus is unstoppable!”)
}
}

Here’s what happens in the above code:

  • You declare a class named Bus and it subclasses Vehicle, with the class Bus: Vehicle { syntax.
  • You declare two properties on the Bus class, seats and gears, both of type Int.
  • You declare one function openDoors() on the Bus class, which opens the doors of the bus.
  • You declare another function drive() on the Bus class. This function is overridden. The superclass implementation of Vehicle is replaced with a new function.

To see how inheritance works, let’s create an instance of Bus.

let greyhound = Bus()
greyhound.wheels = 8
greyhound.seats = 200
greyhound.gears = 7

See what happens in the above code?

  • The greyhound bus has 8 wheels. This wheels property is inherited from Vehicle, because all vehicles have wheels. See how the property wheels isn’t declared on the Bus class? Instead, its declared on Vehicle, and inherited, because Bus subclasses Vehicle.
  • The seats and gears properties are declared on the Bus class. It’s important to note that the Vehicle class does not have these properties! They’re specific to the Bus class.

When you call the function drive() on the greyhound object, the overridden implementation is called. Like this:

greyhound.drive()
// Output: Out of the waaaayyy! This bus is unstoppable!

The output is not Driving this vehicle!, because the function drive() is overridden by the subclass implementation of Bus.

It’s important to note here that a bicycle, bus and racecar all have wheels, albeit a different number of wheels. Subclassing isn’t so much about the value of properties, such as a different number of wheels, but about the properties and functions themselves.

A bicycle has pedals and a frame, whereas a bus has doors that can swing open. It’s not about the number of wheels, but instead about the attributes that classes in the hierarchy share with each other.

Try Object-Oriented Programming for Yourself!

Alright, let’s put what you’ve learned into practice! You can use the Swift Sandbox below to try out Swift code. Play around with classes, properties, functions and subclassing.

class Vehicle
{
var wheels:Int = 0
var maxSpeed:Int = 0

func drive()
{
print(“This vehicle is driving!”)
}
}

class RaceCar: Vehicle
{
var hasSpoiler = true

override func drive()
{
print(“VROOOOM!!!”)
}
}

class Bus: Vehicle
{
var seats:Int = 0
var gear:Int = 1

func shiftGears()
{
gear += 1
}
}

let ferrari = RaceCar()
ferrari.wheels = 4
ferrari.hasSpoiler = false
ferrari.drive()

let bicycle = Vehicle()
bicycle.wheels = 2
bicycle.drive()

let greyhound = Bus()
greyhound.wheels = 8
greyhound.shiftGears()
greyhound.shiftGears()
print(“Gear: \(greyhound.gear)”)

Further Reading

Object-Oriented Programming helps you structure your code. It enables you to reuse and extend code, without unnecessarily repeating yourself. Ultimately, that helps you to avoid bugs and code more productively.

Think about what the words “object oriented” mean. Object-Oriented Programming is quite literally oriented around objects. Instead of letting your code flow like a waterfall, you organize it in classes and objects, with their properties and functions. Much clearer!

Object-Oriented Programming is a fundamental topic in iOS development. OOP is literally everywhere, which makes it all the more important to master properly.


Aasif Khan

Head of SEO at Appy Pie

App Builder

Most Popular Posts