Chapter 81: Swift Class vs Struct

Class vs Struct in Swift — written exactly like a patient, experienced teacher sitting next to you with a playground open.

We are going to go very slowly, with many small examples, real-life analogies, real-world decision patterns, common beginner mistakes, and the modern mental model that most good Swift developers use every day in 2025–2026.

Let’s start from the very beginning.

1. The Core Difference — Value Type vs Reference Type

This is the single most important thing to understand. Everything else follows from this.

Property Struct (Value Type) Class (Reference Type) Real-life analogy
What is assigned / passed Full copy of all data Reference (pointer) to the same object Giving a photocopy vs giving the original photo
Changing one variable affects others? No — each copy is independent Yes — all variables point to the same object Changing one photocopy doesn’t change others
Identity comparison (===) Not possible (value types don’t have identity) Possible — checks if same instance in memory Asking “are these two the exact same photo print?”
Memory management Copied on write (Copy-on-Write for Array/Dict/String) Reference counting (ARC) Copies take more memory only when actually changed
Thread safety (mutable shared state) Safer — copies are independent Dangerous — needs careful synchronization Copies are safe to modify from multiple threads
Performance (small data) Usually faster (stack allocation possible) Usually slower (heap + reference counting) Small copies are cheap
Can inherit No Yes (single inheritance) Classes can have parents
Can be used as Dictionary key Yes (if Hashable) Yes (but need careful Hashable) Structs are easier for keys

Golden rule used by almost every experienced Swift developer today:

Default to struct for almost everything. Use class only when you really need one of these things:

  • reference semantics (shared mutable state)
  • inheritance
  • Objective-C interop / UIKit compatibility
  • identity comparison (===)

2. Side-by-side comparison with real examples

Example 1 – Mutability & copying

Swift
Swift

Key takeaway:

  • Struct → changing one copy never affects others
  • Class → changing through any reference affects all references

3. Real-life examples — when developers choose struct vs class

Example 1 – Data model (almost always struct)

Swift

Why struct here?

  • No shared mutable state needed
  • Copying is cheap (Copy-on-Write for Array)
  • Value semantics — very predictable & safe

Example 2 – ViewController / view model (often class)

Swift

Why class here?

  • Needs to be shared between views (@ObservedObject, @StateObject)
  • Identity matters (same view model instance)
  • Needs to notify observers when data changes (@Published)

3. When to choose struct vs class — decision checklist

Question / Need Choose Struct Choose Class Typical real-world decision
Do you need identity (===)? No Yes Shared state, view models
Do you need inheritance? No Yes UIKit/AppKit view controllers
Do you want value semantics (copy = independent)? Yes No Data models, DTOs, value objects
Is thread-safety important (mutable shared state)? Yes (safer) No (needs careful synchronization) Modern apps prefer struct
Is the data small & frequently copied? Yes (Copy-on-Write efficient) No (reference counting overhead) Almost always struct
Do you need Objective-C interop? No Yes UIKit, Core Data, etc.
Are you using SwiftUI / Combine heavily? Yes (structs + @State / @Binding) Sometimes (ObservableObject class) Structs for most data

Modern Swift recommendation (2025–2026):

Default to struct for almost everything Use class only when you really need:

  • reference semantics (shared mutable state)
  • inheritance
  • Objective-C compatibility
  • identity comparison (===)

4. Very Common Beginner Mistakes & Correct Habits

Mistake Wrong / Risky code Correct / Better habit Why?
Using class by default “just in case” class User { … } everywhere Default to struct unless you need reference semantics Value types are safer & more predictable
Force-unwrapping in class/struct self.user!.name guard let user = user else { … } Prevents crashes
Deep class inheritance 5+ levels of class inheritance Prefer composition or protocols Deep hierarchies become hard to reason about
Forgetting mutating in struct methods func increaseAge() { age += 1 } mutating func increaseAge() { age += 1 } Compile error if missing
Sharing mutable class state without care static let shared = MyManager() Prefer dependency injection over singletons Singletons make testing & reasoning hard

5. Small Practice — Try these

  1. Create a Personstruct with:
    • let name: String
    • var age: Int
    • mutating func haveBirthday()
  2. Create a BankAccountstruct like the example above
    • private var balance: Double
    • init(initialDeposit:)
    • mutating func deposit(amount:)
    • mutating func withdraw(amount:) -> Bool
  3. Create a Locationstruct
    • let latitude: Double, longitude: Double
    • var name: String?
    • computed property coordinateString

Paste your code here if you want feedback or want to see more polished versions!

What would you like to explore next?

  • Struct vs Class — detailed comparison & decision guide
  • Mutability inside structs (mutating methods)
  • Struct initializers (default, custom, memberwise)
  • Structs in SwiftUI (@State, value types, performance)
  • Or move to another topic (optionals, arrays, closures, switch…)

Just tell me — we’ll continue in the same clear, detailed, patient style 😊

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *