Weekly Swift articles, podcasts and tips by John Sundell.

How to pick between using a struct or a class?

Answered on 14 Jul 2020

Deciding whether to use a struct or a class to implement a given type mostly comes down to the decision of whether we want that new type to have value or reference semantics. Class instances each have an identity and are passed by reference, while structs are handled and mutated as values.

Basically, if we want all of the changes that are made to a given object to be applied the same instance, then we should use a class — otherwise a struct will most likely be a more appropriate choice.

Structs also come with a few language features that are not available to classes — such as memberwise initializers, the option to decide if a given value should be mutable or not based on whether it was declared using var or let, and the fact that we can easily create a new copy of a given value simply by declaring a new variable:

struct Article {
    var title: String
    var description: String
    var authorName: String
}

// This value will be compltely immutable, since it was declared
// with 'let', and we can initialize it without having to add an
// initializer to our Article type:
let article = Article(
    title: "Opaque return types in Swift",
    description: """
    A deep dive into one of the features that power SwiftUI’s DSL.
    """,
    authorName: "John Sundell"
)

// Creating a new, mutable copy of the above value is as easy
// as declaring a new 'var':
var newArticle = article
newArticle.title = "More about opaque return types"

However, the opposite is also true, classes do have capabilities that structs don’t have as well — such as the ability to form hierarchies using subclassing, and the concept of weak references:

// When using inheritance, we have to use a class:
class ContentViewController: UIViewController {
    // Types implemented using classes can be stored using
    // weak references:
    weak var delegate: ContentViewControllerDelegate?
}

// This protocol can only be adopted by classes, since
// it's marked with 'AnyObject':
protocol ContentViewControllerDelegate: AnyObject {
    func contentViewControllerDidUpdate(
        _ viewController: ContentViewController
    )
}

So picking between a class or a struct mostly comes down to identifying what kind of capabilities that we need each given type to have. Even though it’s fair to say that most modern Swift code relies heavily on structs and other value types (such as enums), there’s nothing wrong with using a class if we want one of our types to either have reference semantics, or to use features that are only available to classes, such as subclassing.