Struct vs. Class in Swift Explained

Written by LearnAppMaking on January 21 2021 in App Development, Swift

Struct vs. Class in Swift Explained

What’s the difference between classes vs. structs? They’re so alike! It’s best to use structs by default, but why? And when should you use classes, then?

In this tutorial, we’re going to take a look into structs vs. classes. When do you use a class, and when do you use a struct? What’s the difference between a struct and a class, and how does that affect practical iOS development?

Structs are a fundamental aspect of Swift, and they can help to make your code more reusable, more flexible, and less tightly coupled. And last but not least, they help you sharpen your skills as an app developer!

Let’s dive in!

  1. At A Glance: Classes vs. Structs
  2. When Should You Use Structs?
  3. When Should You Use Classes?
  4. Further Reading

Want to know more about structs and how to use them, before you move on with classes vs. structs? Check out this tutorial: Structs in Swift Explained

At A Glance: Classes vs. Structs

First, let’s take a look at what features classes and structs have in common:

  • Both structs and classes can define properties to store values, and they can define functions
  • They can define subscripts to provide access to values with subscript syntax
  • They can define initializers to set up their initial state, with init()
  • They can be extended with extension (this is important!)
  • They can conform to protocols, for example to support Protocol Oriented Programming
  • They can work with generics to provide flexible and reusable types

Classes support a few more capabilities that structs don’t have:

That last point is important: classes are reference types, and structs are value types. Here’s what’s what:

  • Value Type: When you copy a value type (i.e., when it’s assigned, initialized or passed into a function), each instance keeps a unique copy of the data. If you change one instance, the other doesn’t change too.
  • Reference Type: When you copy a reference type, each instance shares the data. The reference itself is copied, but not the data it references. When you change one, the other changes too.

Let’s take a look at an example:

  • Reference Type: Bob has a phone number, written on a piece of paper. He shares it with Alice. Alice doesn’t write down the phone number for herself, but instead remembers that Bob has it. When she needs the phone number, she uses Bob’s piece of paper. When Alice accidentally changes one digit of the phone number, Bob’s phone number changes too. Picture this as Bob and Alice both holding the piece of paper the phone number is written on.
  • Value Type: Bob has a phone number, and he gives it to Alice. Alice writes it down and now has her own copy. When she accidentally changes it, only her copy changes, and not the original phone number Bob has. Both Bob and Alice have their unique copy of the phone number.

In Swift, structs are value types whereas classes are reference types. When you copy a struct, you end up with two unique copies of the data. When you copy a class, you end up with two references to one instance of the data. It’s a crucial difference, and it affects your choice between classes or structs.

A smart shorthand to remember the difference, is that a value type copies the value, and a reference type copies the reference.

When Should You Use Structs?

It’s recommended to use struct in Swift by default. Structs are helpful in these scenarios, too:

  • Simple Data Types
    Use structs for simple data types. Think about database objects you want to pass around in your code, like NewsItem, Task or User. Since they’re so well-defined, and often don’t need to accommodate complex relationships between objects, it’s simpler to use structs.
  • Thread Safety
    In a multi-threaded environment, for instance with a database connection that’s opened in a different thread, structs are safer. They can be copied from one thread to another thread, without running the risk of a race condition or deadlock. Classes do not have this inherent safety, unless they’re deliberately made thread-safe.
  • Mostly Structs Scenario
    When the properties of a struct are mostly value types too, like String, it makes sense to wrap them in a struct instead of a class. It’s OK to use structs within class types, but pay extra attention if you use classes within struct types. Classes are reference types, so if you’re unaware that your struct references a shared class instance, and your struct gets copied, both structs share a reference to that class!
  • Don’t Need Inheritance
    Structs cannot inherit code from other structs. If you don’t need inheritance, it’s smarter to use structs (instead of classes). Structs can still conform to protocols, and they can be extended, so you still have a few options for creating flexible, composable code.

Using structs has a huge benefit: it’s easier to reason about data changes in your code. When a type is a struct, you can be certain that no other part of your code can hold on to a reference to that struct. Unless it’s explicitly coded, a struct cannot be changed by some other part of your code.

Remember that metaphor we used earlier, with Bob and Alice and the phone number? Imagine that the phone number is written down on a Note, a struct. When Bob passes a Note object to Alice, the object is copied, which creates two unique instances. When Alice changes the phone number, Bob’s copy does not change. This means Bob can more easily reason about his code, because he can be certain that his copy cannot be changed without him knowing. If there’s code that changes Bob’s copy, it’ll need to be explicit code.

Just so we’re on the same page, this is how you define and use a struct:

struct NewsItem {
    var title: String = ""
    var url: String = ""
}

var item = NewsItem()
item.title = "Struct vs. Class in Swift Explained"
item.url = "https://learnappmaking.com/struct-vs-class-swift-how-to/"

print(item.title)
// Output: Comparing Classes vs. Structs in Swift

As you can see, the syntax is effectively the same as for defining and using a class. Instead of class name { ··· you write struct name { ···.

Want to learn more about structs? Check out this tutorial: Structs In Swift Explained

When Should You Use Classes?

It’s recommended to use a class if you need the specific features of a class. This is why working with structs is the default, and classes are a deliberate choice.

Classes have a few extra characteristics that structs don’t have:

  • Classes can inherit from another class, which you can’t do with structs. With classes, you can write class MyViewController : UIViewController to create a subclass of UIViewController. Structs can implement protocols, can be extended, and can work with generics, though!
  • Classes can be deinitialized, i.e. they can implement a deinit function. Also, you can make one or more references to the same class (i.e., classes are a reference type).
  • Classes come with the built-in notion of identity, because they’re reference types. With the identity operator === you can check if two references (variables, constants, properties, etc.) refer to the same object.

It’s important to note here that you need to use classes if you want to interop between Swift and Objective-C. If you need Objective-C interoperability, you’ll need to use classes. For example, if you want to use @objc and dynamic in a Realm data model, that model needs to be a class.

Next to reference vs. value types, inheritance is the most important difference between a class and a struct. With classes, you can clearly define a parent-child connection between subclass and superclass.

A few examples:

  • MyViewController inherits from UIViewController
  • MyTableViewController inherits from InfiniteTableViewController to adopt “infinite scrolling”, which in turn inherits from UITableViewController
  • Car and Bike both inherit from Vehicle, because they both use the same “basic” set of characteristics like numberOfWheels and speed.

In these last two examples lies a danger: you can end up with a whole bunch of inherited classes – top-to-bottom – that all “decorate” their superclass with different functionalities. Think about SuperCar and MuscleCar, that both inherit from Car, and from Vehicle. This gets tightly-coupled really fast.

It’s easy to get lost in which class inherits what, even though, at first sight, it makes sense to structure your classes like this. What if SuperCar inherits a function that it doesn’t need from Vehicle? What if you want to create a SuperBike, that’s similar to a SuperCar, but you can’t “inherit” or share those characteristics because they’re in different subclass-superclass hierarchies? In that case, it may make sense to use composition instead.

Based on what we’ve discussed about identity and references, we can assert that it’s best to use classes in these scenarios:

  • Copying Doesn’t Make Sense
    When copying or comparing instances doesn’t make sense, e.g. with Window or UIViewController. It doesn’t make sense to copy an app window, since there’s only one active at a time, and it often doesn’t make sense to copy a view controller either – you’d just create a new one.
  • External Effects or Data
    When the lifetime of an instance is tied to external effects, e.g. for DatabaseConnection or TemporaryFile. It doesn’t make sense to create two copies of a reference to a file on the disk, after all, they both reference the same data, and represent that data in code.
  • Intermediaries
    When instances are merely conduits for external states, e.g. for CGContext or PersistenceController. Sometimes you need a helper or wrapper class to get things done: an API or a reference to an online resource. In those cases the class is only a conduit, something that passes along information, and it doesn’t make sense to create a copy of that.

To summarize, it’s smart to use classes if you need the features that only classes provide: inheritance, identity, Objective-C interoperability, and scenarios where copying a value doesn’t make sense.

Quick Tip: In many scenarios, you can “upgrade” a struct to a class by merely changing its signature from struct ··· { to class ··· {. Going from class to struct is harder. When you follow the struct-default approach, you keep the option to upgrade to a class later on.

Further Reading

So… that’s structs vs. classes for ya! You learned this:

  • The differences and similarities between structs and classes
  • When it’s best to use structs, for example for simple data types
  • When it’s best to use classes, for example to use inheritance and identity

Want to learn more? Check out these resources:

LearnAppMaking

LearnAppMaking

At LearnAppMaking.com, app developers learn how to build and launch awesome iOS apps.