So far you've stored data in separate variables, arrays and dictionaries. Real apps deal with things: a product with a name, price and stock level; a message with a sender, text and time; a point with x and y. A struct groups related values into one new type, together with the functions that work on them.
Structs are the most common way to model data in Swift. Apple's own frameworks use them heavily: String, Array, Int and most SwiftUI views are structs. They're value types: when you assign a struct to another variable or pass it to a function, the receiver gets its own independent copy, just like arrays.
In this lesson you'll define structs with stored and computed properties, add methods, change properties with mutating methods, write initializers that validate data, react to changes with didSet, and see how value semantics keeps data safe.
Defining a struct
struct Product {
let name: String
var price: Double
var stock: Int = 0
}
var mug = Product(name: "Mug", price: 8.5, stock: 12)
let pen = Product(name: "Pen", price: 1.2) // stock uses its default
mug.price = 9.0
print(mug.name, mug.price)- Type names use UpperCamelCase; properties use lowerCamelCase.
- Swift writes a memberwise initializer for you, with a labelled argument for each property. Properties with default values can be left out.
letproperties can't change after creation;varproperties can, but only if the instance itself is avar.
Computed properties and methods
A computed property calculates its value each time it's read:
struct Rectangle {
var width: Double
var height: Double
var area: Double { width * height }
func describe() -> String { "\(width)x\(height), area \(area)" }
}Methods are functions inside the type and can read its properties directly. A method that changes properties must be marked mutating, because changing a struct changes the whole value:
mutating func scale(by factor: Double) {
width *= factor
height *= factor
}Initializers and property observers
Write your own init to validate or calculate values. A failable initializer, init?, returns nil when the input isn't valid:
struct Temperature {
let celsius: Double
init?(celsius: Double) {
guard celsius >= -273.15 else { return nil }
self.celsius = celsius
}
}Property observers run code when a stored property changes: didSet runs after the change, with the previous value available as oldValue; willSet runs just before. They're handy for keeping values in range or reacting to updates. static properties and methods belong to the type itself, such as Temperature.absoluteZero.
Value semantics
var a = Rectangle(width: 2, height: 3)
var b = a
b.width = 10
print(a.width) // 2.0: a is unchangedEach variable holds its own copy. A function that receives a struct can't change the caller's copy unless it's passed inout. This makes code predictable: data only changes where you can see it change. Apple recommends structs by default, using classes only when you need shared identity or inheritance, as the classes lesson explains.
Writing your own init replaces the memberwise one
The free memberwise initializer only exists while you haven't written an init inside the struct's braces. Once you add one, Swift assumes you want full control and removes it:
struct User {
let name: String
let joinedYear: Int
init(name: String) {
self.name = name
self.joinedYear = 2024
}
}
User(name: "Ada") // fine
User(name: "Ada", joinedYear: 2020) // error: extra argument 'joinedYear' in callIf you want both, put your custom init in an extension instead, and the memberwise initializer stays available. The extensions lesson shows this pattern.
Copy, change, keep both
Because assigning a struct makes a copy, you can create a variation of a value without touching the original. This is a common way to build a "changed version" of some data:
let mug = Product(name: "Mug", price: 10.0)
var saleMug = mug
saleMug.price *= 0.8
print(mug.price, saleMug.price) // 10.0 8.0The original mug can even stay a let. Only the copy needs to be a var, since that's the one you change. SwiftUI uses exactly this idea: each change produces a new value, and the screen updates to match it.
You can also give a property private(set), as in private(set) var balance = 0. Code outside the struct can read it, but only the struct's own mutating methods can change it.
Swift Structs Syntax
Struct syntax
struct Name {
let a: Int
var b: String = "default"
var computed: Int { a * 2 }
var watched = 0 { didSet { print(oldValue, watched) } }
static let shared = 1
func read() -> Int { a }
mutating func change() { b = "new" }
init?(a: Int) { guard a >= 0 else { return nil }; self.a = a }
}Swift Structs Summary
- A struct groups properties and methods into a new type; Swift provides a memberwise initializer.
- Computed properties calculate values; methods that change properties must be mutating.
- Custom init validates data; init? returns nil for invalid input.
- didSet and willSet observe changes; static members belong to the type.
- Structs are value types: copies are independent, and let instances can't change at all.
Swift Structs Examples
Each example shows the code, the output it produces and what each part does.
A product struct
struct Product {
let name: String
var price: Double
var stock: Int = 0
var isAvailable: Bool { stock > 0 }
var stockValue: Double { price * Double(stock) }
mutating func sell(_ quantity: Int) -> Bool {
guard quantity <= stock else { return false }
stock -= quantity
return true
}
}
var lamp = Product(name: "Desk lamp", price: 24.5, stock: 3)
print(lamp.sell(2))
print(lamp.sell(5))
print(lamp.stock, lamp.isAvailable, lamp.stockValue)true
false
1 true 24.5Line-by-line explanation
- Lines 6 to 7Computed properties are worked out each time they're read.
- Line 9sell changes stock, so it must be mutating; it can only be called on a var instance.
- Line 10guard refuses to sell more than is in stock.
- Line 19After selling 2 of 3, one lamp is left.
A failable initializer and didSet
struct Volume {
var level: Int {
didSet {
if level > 100 { level = 100 }
if level < 0 { level = 0 }
print("Volume \(oldValue) -> \(level)")
}
}
init?(level: Int) {
guard (0...100).contains(level) else { return nil }
self.level = level
}
}
var v = Volume(level: 40)!
v.level += 20
v.level = 150
print(Volume(level: 120) == nil)Volume 40 -> 60
Volume 60 -> 100
trueLine-by-line explanation
- Lines 3 to 7didSet runs after every change, clamps the value into range and reports it.
- Lines 10 to 13The failable initializer rejects levels outside 0 to 100.
- Line 16The ! is safe here because 40 is valid; in real code, use if let.
- Line 19Volume(level: 120) returns nil.
Value semantics in action
struct Point {
var x: Int
var y: Int
}
var start = Point(x: 0, y: 0)
var current = start
current.x += 5
current.y += 3
print(start)
print(current)
func moved(_ p: Point) -> Point {
var copy = p
copy.x += 1
return copy
}
print(moved(current), current)Point(x: 0, y: 0)
Point(x: 5, y: 3)
Point(x: 6, y: 3) Point(x: 5, y: 3)Line-by-line explanation
- Line 7current is a copy, so changing it leaves start alone.
- Lines 11 to 12Structs print their properties automatically.
- Lines 14 to 17The function works on its own copy and returns a new value; the caller's point is unchanged.
Common Swift Structs Mistakes
Changing a property of a let instance
let mug = Product(name: "Mug", price: 8.5)
mug.price = 9 // error: cannot assign to property: 'mug' is a 'let' constantvar mug = Product(name: "Mug", price: 8.5)
mug.price = 9Forgetting mutating
struct Counter {
var count = 0
func increment() { count += 1 } // error: left side of mutating operator isn't mutable
}struct Counter {
var count = 0
mutating func increment() { count += 1 }
}Expecting a shared change
var a = Point(x: 1, y: 1)
var b = a
b.x = 9
print(a.x) // 1, not 9// change the variable you actually want, or use a class when shared identity is needed
a.x = 9Swift Structs in Real Projects
Structs in Swift apps
Almost all data in modern Swift apps is modelled with structs: users, messages and settings decoded from JSON, rows in a list, and every SwiftUI view (struct ContentView: View). Because structs are value types, SwiftUI can compare old and new values to decide exactly what to redraw, and data can be passed around without worrying that another part of the app will change it unexpectedly.
Swift Structs FAQ
What is the difference between a struct and a class in Swift?
A struct is a value type: assigning or passing it makes an independent copy. A class is a reference type: assigning it shares the same object. Structs also get a free memberwise initializer and can't inherit, while classes support inheritance and deinit. Apple recommends starting with a struct.
Why do I need the mutating keyword in a Swift struct?
A struct method that changes a property is really replacing the whole value, so Swift asks you to mark it mutating. That tells the compiler it can only be called on a var instance, never on a let constant.
Can a struct have methods in Swift?
Yes. Structs can have methods, computed properties, initializers, static members and protocol conformances, just like classes. The main things they can't do are inherit from another struct and run a deinit.
Why does my struct lose its memberwise initializer?
Writing any init inside the struct's main declaration removes the automatically generated memberwise initializer. Move your custom init into an extension of the struct, and both initializers will be available.
Are Swift structs copied every time they are assigned?
Conceptually yes: each variable gets its own independent value. In practice, standard types such as arrays and strings use copy-on-write and only duplicate their storage when one copy changes, and the compiler removes many other copies, so structs stay fast.
Swift Structs Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the method so it can change the struct's property.
struct Counter {
var count = 0
func reset() { count = 0 }
}
What does this print?
struct Box { var w: Int; var h: Int; var area: Int { w * h } }
print(Box(w: 3, h: 4).area)
What does this print?
struct P { var x: Int }
var a = P(x: 1)
var b = a
b.x = 5
print(a.x + b.x)
What does init? mean?
When does Apple recommend using a struct instead of a class?
Swift Structs Quiz
5 questions to check what you learned.
What does Swift generate automatically for a struct?
- A class
- A memberwise initializer with a parameter for each property
- A copy method
- A protocol
What is a computed property?
- A property whose value is calculated each time it's read
- A property that stores a calculation once
- A static property
- A let property
When does
didSetrun?- Before the property changes
- Only in the initializer
- After the property changes, with oldValue available
- Never automatically
Can you call a mutating method on a let instance?
- Yes
- No, the instance must be a var
- Only in playgrounds
- Only if it returns a value
Why are structs called value types?
- They only hold numbers
- They have no methods
- They're stored in a database
- Assigning or passing one creates an independent copy