Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a PR for a junior dev who was building a shopping cart system. They had created a CartItem as a class. It seemed logical—a cart item is a "thing," right? But they hit a weird bug: when they updated the quantity of an item in the cart, that same item's quantity was mysteriously updating in the "Recommended Products" list on the sidebar. They were scratching their heads for hours until we realized that both the cart and the recommendation engine were holding a reference to the exact same instance of that class. By changing the quantity in one place, they had mutated the object everywhere it existed in memory.

This is the quintessential "aha!" moment for understanding the difference between structs and classes in Swift. It comes down to how the computer handles the data: Value types versus Reference types.

The Independence of Value Types

Structs are value types. When you assign a struct to a new variable or pass it into a function, Swift creates a unique copy of that data. I like to think of this like sending someone a photocopy of a document; if they scribble on their copy, your original remains pristine.

struct Point {
    var x: Double
    var y: Double
}

var pointA = Point(x: 10, y: 10)
var pointB = pointA // A copy is made here

pointB.x = 50

print(pointA.x) // Still 10
print(pointB.x) // Now 50

Because structs are copied, they are incredibly safe. You don't have to worry about some distant piece of code changing your data behind your back. This makes your code much easier to reason about, especially when you start dealing with asynchronous tasks or multi-threaded code.

Shared State and Reference Types

Classes are reference types. When you assign a class instance to a new variable, you aren't copying the data itself; you're copying the reference (the memory address) to that data. Now, both variables are pointing to the same spot in memory. Using the document analogy, this is like sharing a Google Doc; if one person deletes a paragraph, everyone sees the change immediately.

class UserProfile {
    var username: String
    
    init(username: String) {
        self.username = username
    }
}

let userA = UserProfile(username: "SwiftCoder")
let userB = userA // Both point to the same instance

userB.username = "iOSMaster"

print(userA.username) // "iOSMaster" - it changed for both!

Reference types are powerful when you actually want shared state. For instance, if you have a DatabaseManager or a SessionManager, you generally want every part of your app to talk to the same single instance, not a bunch of disconnected copies.

Choosing the Right Tool for the Job

You'll hear people say "just use structs for everything," and honestly? They're mostly right. In modern Swift development, the default choice should almost always be a struct. They are faster to allocate and eliminate an entire category of bugs related to shared state.

So, when do you actually reach for a class? I use a simple rule of thumb: Identity vs. Value. If two instances are "equal" because they have the same properties (like two Coordinate objects at 10,10), use a struct. If two instances are only the same if they are the exact same object in memory (like two User objects that happen to have the same name but are different people), use a class.

Also, remember that classes give you inheritance (the ability to subclass), while structs do not. If you find yourself needing a complex hierarchy of types, a class might be necessary, though I'd encourage you to look into Protocols first—they usually provide a cleaner way to share behavior without the baggage of class inheritance.




📋 Practical Task

Fixing the Shared Scoreboard Bug

You are building a competitive quiz app. Currently, the PlayerScore is implemented as a class. Because of this, when the app creates a "Guest" player and a "User" player, it accidentally assigns them the same reference to a score object. When the Guest earns a point, the User's score increases too!

Your Task:

  1. Review the code below.
  2. Convert the PlayerScore from a class to a struct.
  3. Adjust the init method (remember that structs provide a memberwise initializer automatically, so you can simplify or remove the explicit init).
  4. Verify that changing guestScore.value no longer affects userScore.value.
class PlayerScore {
    var value: Int
    
    init(value: Int) {
        self.value = value
    }
}

var userScore = PlayerScore(value: 0)
var guestScore = userScore // Currently sharing a reference!

guestScore.value = 10

print("User Score: \(userScore.value)")   // Currently prints 10
print("Guest Score: \(guestScore.value)") // Currently prints 10
Rating
0 0

There are no comments for now.

to be the first to leave a comment.