NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

How to create your own structs

Paul Hudson    @twostraws   

Updated for Xcode 14.2

Swift’s structs let us create our own custom, complex data types, complete with their own variables and their own functions.

A simple struct looks like this:

struct Album {
    let title: String
    let artist: String
    let year: Int

    func printSummary() {
        print("\(title) (\(year)) by \(artist)")

That creates a new type called Album, with two string constants called title and artist, plus an integer constant called year. I also added a simple function that summarizes the values of our three constants.

Notice how Album starts with a capital A? That’s the standard in Swift, and we’ve been using it all along – think of String, Int, Bool, Set, and so on. When you’re referring to a data type, we use camel case where the first letter is uppercased, but when you’re referring to something inside the type, such as a variable or function, we use camel case where the first letter is lowercased. Remember, for the most part this is only a convention rather than a rule, but it’s a helpful one to stick with.

At this point, Album is just like String or Int – we can make them, assign values, copy them, and so on. For example, we could make a couple of albums, then print some of their values and call their functions:

let red = Album(title: "Red", artist: "Taylor Swift", year: 2012)
let wings = Album(title: "Wings", artist: "BTS", year: 2016)



Notice how we can create a new Album as if we were calling a function – we just need to provide values for each of the constants in the order they were defined.

As you can see, both red and wings come from the same Album struct, but once we create them they are separate just like creating two strings.

You can see this in action when we call printSummary() on each struct, because that function refers to title, artist, and year. In both instances the correct values are printed out for each struct: red prints “Red (2012) by Taylor Swift” and wings prints out “Wings (2016) by BTS” – Swift understands that when printSummary() is called on red, it should use the title, artist, and year constants that also belong to red.

Where things get more interesting is when you want to have values that can change. For example, we could create an Employee struct that can take vacation as needed:

struct Employee {
    let name: String
    var vacationRemaining: Int

    func takeVacation(days: Int) {
        if vacationRemaining > days {
            vacationRemaining -= days
            print("I'm going on vacation!")
            print("Days remaining: \(vacationRemaining)")
        } else {
            print("Oops! There aren't enough days remaining.")

However, that won’t actually work – Swift will refuse to build the code.

You see, if we create an employee as a constant using let, Swift makes the employee and all its data constant – we can call functions just fine, but those functions shouldn’t be allowed to change the struct’s data because we made it constant.

As a result, Swift makes us take an extra step: any functions that only read data are fine as they are, but any that change data belonging to the struct must be marked with a special mutating keyword, like this:

mutating func takeVacation(days: Int) {

Now our code will build just fine, but Swift will stop us from calling takeVacation() from constant structs.

In code, this is allowed:

var archer = Employee(name: "Sterling Archer", vacationRemaining: 14)
archer.takeVacation(days: 5)

But if you change var archer to let archer you’ll find Swift refuses to build your code again – we’re trying to call a mutating function on a constant struct, which isn’t allowed.

We’re going to explore structs in detail over the next few chapters, but first I want to give some names to things.

  • Variables and constants that belong to structs are called properties.
  • Functions that belong to structs are called methods.
  • When we create a constant or variable out of a struct, we call that an instance – you might create a dozen unique instances of the Album struct, for example.
  • When we create instances of structs we do so using an initializer like this: Album(title: "Wings", artist: "BTS", year: 2016).

That last one might seem a bit odd at first, because we’re treating our struct like a function and passing in parameters. This is a little bit of what’s called syntactic sugar – Swift silently creates a special function inside the struct called init(), using all the properties of the struct as its parameters. It then automatically treats these two pieces of code as being the same:

var archer1 = Employee(name: "Sterling Archer", vacationRemaining: 14)
var archer2 = Employee.init(name: "Sterling Archer", vacationRemaining: 14)

We actually relied on this behavior previously. Way back when I introduced Double for the first time, I explained that you can’t add an Int and a Double and instead need to write code like this:

let a = 1
let b = 2.0
let c = Double(a) + b

Now you can see what’s really happening here: Swift’s own Double type is implemented as a struct, and has an initializer function that accepts an integer.

Swift is intelligent in the way it generates its initializer, even inserting default values if we assign them to our properties.

For example, if our struct had these two properties

let name: String
var vacationRemaining = 14

Then Swift will silently generate an initializer with a default value of 14 for vacationRemaining, making both of these valid:

let kane = Employee(name: "Lana Kane")
let poovey = Employee(name: "Pam Poovey", vacationRemaining: 35)

Tip: If you assign a default value to a constant property, that will be removed from the initializer entirely. To assign a default but leave open the possibility of overriding it when needed, use a variable property.

Hacking with Swift is sponsored by Stream

SPONSORED Build a functional Twitter clone using APIs and SwiftUI with Stream's 7-part tutorial series. In just four days, learn how to create your own Twitter using Stream Chat, Algolia, 100ms, Mux, and RevenueCat.

Try now!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.3/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.