Struct vs. Class In Swift Explained

Written by Reinder de Vries on June 19 2019 in App Development

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 article, 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 very powerful feature of Swift, and they can help to make your code more reusable, more flexible, and less tightly coupled. And last but not least, they 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

At A Glance: Classes vs. Structs

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

  • They 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 instance to support Protocol Oriented Programming

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 referenve 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. He gives it to 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 asks Bob. 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.

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

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.

Learn how to build iOS apps

Get started with iOS 12 and Swift 5

Sign up for our iOS development course Zero to App Store and learn how to build professional iOS 12 apps with Swift 5 and Xcode 10.

When Should You Use Structs?

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

  • 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.
  • 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.
  • 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 – you’re in for trouble!

Using structs has an added 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 the object. Unless it’s explicitly coded, a struct cannot be changed by some other part of your code.

Compare it to the 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 = "Comparing Classes vs. Structs in Swift"
item.url = "https://learnappmaking.com/classes-structs-comparison-swift-programming"

print(item)
// Output: NewsItem(title: "Comparing Classes vs. Structs in Swift", url: "https://learnappmaking.com/classes-structs-comparison-swift-programming")

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 article: 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. As noted before, 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. Conversely, structs can implement protocols.
  • Classes can be deinitialized, i.e. they can implement a deinit function, and 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, that all “decorate” the subclass with different functionalities. Think about SuperCar and MuscleCar, that both inherit from Car, and from Vehicle.

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 and Protocol-Oriented Programming.

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

  • 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.
  • 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.
  • When instances are merely conduits for external states, e.g. for CGContext. 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.

Learn how to build iOS apps

Get started with iOS 12 and Swift 5

Sign up for our iOS development course Zero to App Store and learn how to build professional iOS 12 apps with Swift 5 and Xcode 10.

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:

Reinder de Vries

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.