- July 25, 2025
- Mins Read
In Swift, structures (struct
) and classes (class
) are the two primary building blocks for creating your own data types. Both let you encapsulate related data (properties) and behavior (methods) under a single, coherent type. Use them to model domain concepts—points in a game world, user profiles, network responses, and more.
struct UserProfile {
var username: String
var age: Int
func greet() {
print("Hi, I'm \(username), \(age) years old.")
}
}
class GameCharacter {
var name: String
var health: Int
init(name: String, health: Int) {
self.name = name
self.health = health
}
func takeDamage(_ amount: Int) {
health -= amount
print("\(name) took \(amount) damage, health is now \(health).")
}
}
Feature | Structure (Value Type) | Class (Reference Type) |
---|---|---|
Memory Semantics | Copied on assignment—each variable has its own independent copy. | Shared reference—multiple variables can point to the same instance. |
Inheritance | Cannot inherit from other types. | Supports single inheritance; can subclass and override behavior. |
Deinitializers | No deinitializer; cleanup isn’t necessary since values go out of scope. | Can implement deinit to run code when an instance is deallocated. |
Type Casting | No runtime casting; cannot use is /as to check or convert. |
Can check & downcast with is , as? , as! . |
Mutability Control | Mark with var to allow mutation; let instances are entirely immutable (all var properties become read-only). |
Even a let class instance can have its var properties changed; let only prevents rebinding. |
Use Case Guidance | Best for lightweight, small data types without shared mutable state (points, colors, simple data containers). | Ideal when identity matters or you need shared, mutable state and inheritance hierarchies (view controllers, data managers, delegates). |
struct MyStruct {
// Stored properties
var text: String
let count: Int
// Computed property
var isEmpty: Bool { text.isEmpty }
// Method
func describe() -> String {
return "‘\(text)’ has \(count) items."
}
// Mutating method (modifies self)
mutating func update(text newText: String) {
text = newText
}
}
let
for constants, var
for variables.mutating
methods are required when a method changes self
or its properties.class MyClass {
// Stored properties
var title: String
let maxCount: Int
// Initializer
init(title: String, maxCount: Int) {
self.title = title
self.maxCount = maxCount
}
// Computed property
var isFull: Bool { title.count >= maxCount }
// Method
func describe() -> String {
return "‘\(title)’ (max \(maxCount))"
}
// Deinitializer
deinit {
print("MyClass titled ‘\(title)’ is being deallocated")
}
}
init
) must set all stored properties before self
is used.deinit
runs just before the instance is deallocated (no arguments, no return).var a = MyStruct(text: "Hello", count: 5)
var b = a // b is a copy of a
b.update(text: "Bye")
print(a.text) // "Hello"
print(b.text) // "Bye"
b
doesn’t affect a
; each has its own storage.let obj1 = MyClass(title: "Level 1", maxCount: 3)
let obj2 = obj1 // obj2 references the same instance
obj2.title = "Boss"
print(obj1.title) // "Boss"
obj2
affects obj1
; they share identity.struct
when:
class
when:
deinit
for cleanup (e.g., removing observers).Both structures and classes expose their data via properties, accessed with dot syntax.
struct Rectangle {
var width: Double
var height: Double
// Computed property
var area: Double {
return width * height
}
}
var rect = Rectangle(width: 10, height: 5)
print(rect.width) // 10
print(rect.height) // 5
print(rect.area) // 50
width
, height
) hold data.area
) calculate on-the-fly.rect.width = 12
(if var
), but if rect
were declared with let
, you couldn’t mutate any of its properties.Classes work the same way:
class Circle {
var radius: Double
var circumference: Double { 2 * .pi * radius }
init(radius: Double) {
self.radius = radius
}
}
let c = Circle(radius: 3)
print(c.circumference) // ~18.85
c.radius = 5 // OK, even though c is a let-constant
Swift automatically generates a memberwise initializer for structs that don’t define any custom initializers. This saves you from writing boilerplate.
struct Point {
var x: Double
var y: Double
}
let p = Point(x: 2.0, y: 4.5)
Point(x:y:)
“for free” as long as you don’t write your own init
.struct Color {
var red: Double = 0
var green: Double = 0
var blue: Double = 0
}
let black = Color() // uses all defaults
let magenta = Color(red: 1, blue: 1)
init()
).struct User {
var name: String
}
var u1 = User(name: "Alice")
var u2 = u1 // copy made here
u2.name = "Bob"
print(u1.name) // "Alice"
print(u2.name) // "Bob"
u2
never affect u1
.enum Status {
case active, inactive
}
var s1 = Status.active
var s2 = s1
s2 = .inactive
print(s1) // .active
Value types are ideal for small, immutable pieces of data (points, ranges, settings).
class Player {
var score: Int = 0
}
let p1 = Player()
let p2 = p1 // both refer to the same Player
p2.score = 10
print(p1.score) // 10
Swift provides two operators to compare whether two references point to exactly the same class instance:
===
returns true
if both sides refer to the identical object.!==
is the inverse.let a = Player()
let b = Player()
let c = a
print(a === b) // false: different instances
print(a === c) // true: c is the same instance as a
print(a !== b) // true
With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...
This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...
Install Swift Package Manager Open Xcode, go to File -> Swift Packages -> Add Package Dependency and enter https://github.com/akardas16/SideMenu.git as Branch main You need to add import ...