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!
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
First, let’s take a look at what features classes and structs have in common:
init()
extension
(this is important!)Classes support a few more capabilities that structs don’t have:
deinit()
function before the class is destroyedThat last point is important: classes are reference types, and structs are value types. Here’s what’s what:
Let’s take a look at an example:
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.
It’s recommended to use struct
in Swift by default. Structs are helpful in these scenarios, too:
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.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!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://www.appypie.com/blog/swift/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
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:
class MyViewController : UIViewController
to create a subclass of UIViewController
. Structs can implement protocols, can be extended, and can work with generics, though!deinit
function. Also, you can make one or more references to the same class (i.e., classes are a reference type).===
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 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:
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.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.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.
So… that’s structs vs. classes for ya! You learned this:
Want to learn more? Check out these resources: