Understanding Classes in Swift


Introduction

In Swift, a class is a fundamental building block of object-oriented programming (OOP). Classes allow developers to create reusable code structures that define properties and methods, enabling efficient code organization and management. Unlike structures (structs), classes support inheritance, reference types, and deinitialization, making them a powerful choice for building robust applications.

Understanding Swift classes is essential for writing scalable and maintainable code, especially when working with complex data models and object-oriented principles.

Why Use Classes in Swift?


  • Encapsulation: Organize code into reusable units.
  • Inheritance: A class can inherit properties and methods from another class.
  • Reference Type: Unlike structs, classes are stored as references.
  • Deinitialization: Classes can implement a deinitializer (deinit) to free resources when an instance is no longer needed.
  • Polymorphism: Methods can be overridden in subclasses to change behavior dynamically.

Class vs. Struct in Swift


Feature Class Struct
Reference Type Yes No (Value Type)
Inheritance Yes No
Deinitialization Yes No
Mutability Can be mutable Immutable by default
Stored in Heap Yes No (Stack)

Syntax of a Class in Swift

class ClassName {
     var property: String

     init(property: String) {
         self.property = property
     }

     func display() {
         print("Property Value: \(property)")
     }
}

Example 1: Creating a Simple Class

import Foundation

class Car {
     var brand: String
     var model: String
     var year: Int

     init(brand: String, model: String, year: Int) {
         self.brand = brand
         self.model = model
         self.year = year
     }

     func carDetails() {
         print("Car: \(brand) \(model) - Year: \(year)")
     }
}

// Creating an object of the class
let myCar = Car(brand: "Tesla", model: "Model S", year: 2024)
myCar.carDetails()

Output

Car: Tesla Model S - Year: 2024

Example 2: Using Class Inheritance in Swift

Inheritance allows a class to inherit properties and methods from another class, enabling code reuse and extensibility.

import Foundation

class Vehicle {
     var speed: Int

     init(speed: Int) {
         self.speed = speed
     }

     func move() {
         print("Moving at \(speed) km/h")
     }
}

class Bike: Vehicle {
     var type: String

     init(speed: Int, type: String) {
         self.type = type
         super.init(speed: speed)
     }

     override func move() {
         print("\(type) bike moving at \(speed) km/h")
     }
}

let myBike = Bike(speed: 80, type: "Sports")
myBike.move()

Output

Sports bike moving at 80 km/h

Example 3: Swift Class with Computed Properties

import Foundation

class Rectangle {
     var width: Double
     var height: Double

     init(width: Double, height: Double) {
         self.width = width
         self.height = height
     }

     var area: Double {
         return width * height
     }
}

let rect = Rectangle(width: 10, height: 5)
print("Area: \(rect.area)")

Output

Area: 50.0

Example 4: Swift Class with deinit Method

The deinit method is called automatically when an instance of a class is deallocated.

import Foundation

class FileManager {
     let fileName: String

     init(fileName: String) {
         self.fileName = fileName
         print("\(fileName) opened")
     }

     deinit {
         print("\(fileName) closed")
     }
}

var file: FileManager? = FileManager(fileName: "document.txt")
file = nil // Object deallocated, `deinit` called

Output

document.txt opened
document.txt closed

Example 5: Using Class Methods (Static Methods)

Static methods belong to the class itself, not an instance.

import Foundation

class MathUtils {
     static func square(_ number: Int) -> Int {
         return number * number
     }
}

print(MathUtils.square(5)) // 25

Output

25

Best Practices for Using Classes in Swift


  • Use classes when reference semantics are needed (e.g., shared state).
  • Use structs when working with immutable data to avoid unnecessary memory overhead.
  • Avoid unnecessary inheritance; prefer composition over inheritance when possible.
  • Use final for classes that should not be inherited for performance optimizations.
  • Implement deinit only when necessary to free up resources.