Skip to content

Structs & Classes

Structs and classes are both building blocks in Swift used to create custom data types. You use them to group related variables and functions together.

Commonalities

Both can:

  • Define properties to store values.
  • Define methods to provide functionality.
  • Define initializers to set up their initial state.
  • Be extended to expand their functionality (explained later).

Properties and Methods

Properties are simply variables or constants associated with a particular class or struct. Just like global variables, they can be Stored or Computed.

On the other hand, Methods are the actions a class or struct can perform.

swift
struct Circle {
    // Stored Properties — the data this struct holds
    var radius: Double
    
    // Computed property — calculated from other properties, not stored
    var area: Double {
        3.14159 * radius * radius // Implicit return
    }

    // Method — a function defined inside the struct
    func perimeter() -> Double {
        2.0 * 3.14159 * radius
    }
}

Computed Properties vs Methods

Use a computed property when you are simply calculating and returning a value based on existing properties. Use a method when the action requires input parameters, performs complex work, or changes the state of the object.

Mutating Methods

Because structs are value types, their methods cannot modify their properties by default.

Whenever you assign a struct to a new variable or pass it to a function, Swift copies the entire object. To keep your code safe and predictable, Swift assumes that methods only read data. If methods could freely change properties, it could lead to unexpected bugs where you accidentally modify a copy instead of the original instance.

To explicitly tell the compiler that a method is intended to change the data, you must place the mutating keyword before the func keyword. Classes do not need this keyword because they are reference types and modify shared data directly.

swift
struct Counter {
    var count = 0
    
    mutating func increment() {
        count += 1
    }
}

Initializers

You can provide default values for properties directly when you define them. When a struct has default values for all its properties, Swift automatically gives you a default initializer that takes no arguments, alongside the standard memberwise initializer.

swift
struct Point {
    var x: Int = 0
    var y: Int = 0
}

// Uses the default initializer (x and y both equal 0)
let origin = Point() 

// The memberwise initializer is still automatically available!
let customPoint = Point(x: 5, y: 10)

You can also write multiple custom initializers to set up your struct or class in different ways. Creating multiple initializers gives you flexibility when creating new instances.

swift
struct Temperature {
    var celsius: Double = 0.0 // Default property value
    
    // Custom initializer for Fahrenheit
    init(fahrenheit: Double) {
        celsius = (fahrenheit - 32.0) / 1.8
    }
    
    // Custom initializer for Kelvin
    init(kelvin: Double) {
        celsius = kelvin - 273.15
    }
}

let temp1 = Temperature() // Uses the default value (0.0 Celsius)
let temp2 = Temperature(fahrenheit: 212.0) // Uses a custom initializer

The Key Differences: Struct vs Class

FeatureStructsClasses
Type CategoryValue Type: Stored directly.Reference Type: Stored as a pointer.
StorageCopied: Each variable gets its own unique copy of the data.Shared: Multiple variables point to the exact same instance.
Inheritance❌ No inheritance support.✅ Can inherit from other classes.
Initializers (init)🪄 Gets a Memberwise Initializer automatically.✍️ You must write your own initializers.
Mutability🔒 If the struct is let, you cannot change its properties.🔓 If the class is let, you can still change its var properties.
Cleanup (deinit)❌ No deinitializers.✅ Supports deinit for cleanup right before it is destroyed.
Identity Check (===)❌ Not available (value types have no identity).Classes Only: Use === to check if same instance.
MemoryFast: Stored on the Stack.🐢 Slower: Stored on the Heap.
Use CaseDefault Choice. Use for 90% of things (models, state).Use for complex objects with shared state or inheritance.

NOTE

Memberwise Initializers: A "magic" feature where the Swift compiler automatically generates an initializer for structs based on their properties. Classes do not get this.

Struct Example (Value Type)

swift
struct Resolution {
    var width = 0
    var height = 0
}

let hd = Resolution(width: 1920, height: 1080) // Memberwise Initializer: no manual 'init' needed
var cinema = hd      // 📦 A COPY is created here
cinema.width = 2048  // Only 'cinema' changes; 'hd' remains 1920

Class Example (Reference Type)

swift
class VideoMode {
    var resolution: Resolution
    var frameRate: Double

    // Classes need manual initializers
    init(resolution: Resolution, frameRate: Double) {
        self.resolution = resolution
        self.frameRate = frameRate
    }
}

let tenEighty = VideoMode(resolution: hd, frameRate: 60.0)
let alsoTenEighty = tenEighty  // 🔗 Both point to the SAME instance
alsoTenEighty.frameRate = 30.0 // Changing 'alsoTenEighty' also changes 'tenEighty'

When to use which?

  • Use Structs by default. They are safer and faster.
  • Use Classes when you need inheritance or shared identity (reference types).