Swift Structs Explained: Properties, Methods, and Value Types
Swift structs are one of the most important tools for creating your own types. They let you group related data and behavior into a single reusable unit, and they are used throughout Swift because they are safe, fast, and predictable. In this article, you will learn what structs are, how to define them, how their value semantics work, and how to avoid the most common mistakes developers make when using them.
Quick answer: A Swift struct is a custom value type that can store properties, define methods, and represent related data as one unit. When you copy a struct or assign it to another variable, Swift copies the value instead of sharing the same instance.
Difficulty: Beginner
Helpful to know first: You will understand this better if you already know basic Swift syntax, variables and constants, functions, and simple types like String, Int, and Bool.
1. What Is a Struct?
A struct in Swift is a custom type that groups related values and behavior together. You can think of it as a blueprint for creating values that have named properties and methods.
- A struct is declared with the struct keyword.
- Structs are value types, which means copies are independent.
- They can contain stored properties, computed properties, methods, and initializers.
- They are commonly used for models like users, products, points, sizes, and settings.
- They are often compared with classes because both define custom types, but structs copy values while classes share references.
For example, if you want to represent a user with a name and age, a struct gives you a clean way to keep that data together instead of storing separate unrelated variables.
2. Why Structs Matter
Structs matter because they match the way many real program values behave. A screen size, a date range, a shopping cart item, or a point on a graph is usually best treated as a value, not as a shared object.
Swift encourages using structs by default because they help reduce accidental shared state. When values are copied instead of shared, code becomes easier to reason about and bugs caused by unexpected mutation become less common.
Use structs when:
- You want to model data as a value.
- Each copy should be independent from the original.
- You do not need inheritance.
- You want simpler, safer data modeling.
Structs are not always the right choice. If you need shared identity, inheritance, or reference semantics, a class may be more appropriate. That comparison appears again later in this article because it is one of the most common Swift design decisions.
3. Basic Syntax or Core Idea
The basic syntax of a struct starts with the struct keyword, followed by a name and a body in braces.
Declaring a simple struct
This example defines a Book struct with two stored properties.
struct Book {
var title: String
var pageCount: Int
}This does not create a book yet. It only defines the shape of the type.
Creating a value from a struct
Because both properties are stored properties and no custom initializer is defined, Swift provides a memberwise initializer automatically.
let swiftBook = Book(title: "Learning Swift", pageCount: 320)The memberwise initializer lets you pass values for each stored property by name.
Accessing properties
You use dot syntax to read properties.
print(swiftBook.title)
print(swiftBook.pageCount)This prints the title and page count stored inside the struct value.
Adding methods
Structs can also define methods, which are functions that belong to the type.
struct Rectangle {
var width: Double
var height: Double
func area() -> Double {
return width * height
}
}The area() method uses the struct's own properties to calculate a result.
4. Step-by-Step Examples
The best way to understand structs is to see them in realistic examples.
Example 1: Storing related user data
Here, a struct groups a user's name and age into one value.
struct User {
var name: String
var age: Int
}
let user = User(name: "Maya", age: 28)
print(user.name)
print(user.age)This is cleaner than passing separate name and age values all over your code.
Example 2: Using a computed property
A computed property calculates a value instead of storing it directly.
struct Temperature {
var celsius: Double
var fahrenheit: Double {
(celsius * 9 / 5) + 32
}
}
let today = Temperature(celsius: 21)
print(today.fahrenheit)The struct stores one value and derives another when needed.
Example 3: Mutating a struct
Because structs are value types, methods that change properties must be marked with mutating.
struct Counter {
var count = 0
mutating func increment() {
count += 1
}
}
var counter = Counter()
counter.increment()
print(counter.count)The mutating keyword tells Swift that the method changes the struct's stored data.
Example 4: Demonstrating value semantics
This example shows one of the most important differences between structs and classes.
struct Point {
var x: Int
var y: Int
}
var start = Point(x: 0, y: 0)
var end = start
end.x = 10
print(start.x)
print(end.x)Changing end does not change start because assigning a struct creates a copy.
5. Practical Use Cases
Structs are useful in many real projects, especially when modeling values.
- Representing app data such as users, products, orders, and addresses.
- Creating small geometry types such as points, sizes, and rectangles.
- Modeling settings or configuration values that should be copied safely.
- Returning grouped results from functions instead of multiple separate values.
- Defining domain-specific value types such as money amounts, coordinates, or scores.
- Creating simple data containers with helper methods for formatting or validation.
Swift's own standard library uses structs heavily. Types like String, Int, Array, and Dictionary are all value-oriented types, which matches Swift's general design style.
6. Common Mistakes
Mistake 1: Forgetting mutating on a method that changes properties
Methods inside a struct are non-mutating by default. If a method changes a stored property, Swift requires the mutating keyword.
Problem: This method changes count without being marked as mutating, so Swift reports an error similar to Cannot assign to property: 'self' is immutable.
struct Counter {
var count = 0
func increment() {
count += 1
}
}Fix: Mark the method as mutating so Swift knows the struct value will change.
struct Counter {
var count = 0
mutating func increment() {
count += 1
}
}The corrected version works because mutating allows the method to modify the struct's stored properties.
Mistake 2: Trying to change a struct stored in a constant
Even if the properties inside a struct are declared with var, the whole value becomes immutable when the struct instance is stored in a let constant.
Problem: This code tries to change a property on a constant struct value, so Swift reports an error such as Cannot assign to property: 'item' is a 'let' constant.
struct Task {
var title: String
}
let item = Task(title: "Write article")
item.title = "Review article"Fix: Use var for the struct value if you need to modify any of its properties.
struct Task {
var title: String
}
var item = Task(title: "Write article")
item.title = "Review article"The corrected version works because the struct value itself is mutable.
Mistake 3: Expecting two struct variables to share changes
Beginners often assign one struct value to another and expect both names to refer to the same underlying object.
Problem: This code assumes that changing copy will also change original, but structs are copied on assignment.
struct Score {
var points: Int
}
var original = Score(points: 10)
var copy = original
copy.points = 20
print(original.points)Fix: Treat structs as independent values after assignment. If you need shared identity and shared mutation, consider whether a class is the better model.
struct Score {
var points: Int
}
var original = Score(points: 10)
var copy = original
copy.points = 20
print(original.points) // 10
print(copy.points) // 20The corrected version works because it reflects the real behavior of value semantics.
7. Best Practices
Use structs by default for simple data models
In Swift, starting with a struct is often the best choice. It gives you predictable value semantics and avoids unnecessary shared state.
struct Product {
var name: String
var price: Double
}This is a strong default for data that represents a value rather than a shared object.
Keep related behavior inside the struct
If a piece of logic naturally belongs to the data, place it on the struct as a method or computed property. This keeps code organized and easier to read.
struct Circle {
var radius: Double
func area() -> Double {
3.14159 * radius * radius
}
}This approach keeps the calculation close to the data it depends on.
Prefer constants when values should not change
If a struct instance is not supposed to change, store it in a let constant. This makes your intent clear and prevents accidental mutation.
struct Configuration {
var apiURL: String
}
let config = Configuration(apiURL: "https://api.example.com")Using a constant here communicates that the configuration value should remain stable.
8. Limitations and Edge Cases
- Structs do not support inheritance. If your design depends on subclassing, a class is required.
- Mutating methods cannot be called on struct values stored in a let constant.
- When a struct contains reference types as properties, the struct itself is copied, but the referenced objects may still be shared.
- Swift provides a memberwise initializer for many structs, but adding your own custom initializer can affect how that automatic initializer is available.
- Large structs are still value types, but Swift uses optimizations such as copy-on-write in some standard library types to keep performance practical.
- If your code relies on object identity, structs are the wrong tool because value types do not have reference identity in the same sense as classes.
9. Swift Structs vs Classes
Structs and classes are both custom types in Swift, but they are designed for different modeling needs.
| Feature | Struct | Class |
|---|---|---|
| Semantics | Value type | Reference type |
| Copy behavior | Copied on assignment | Shared reference on assignment |
| Inheritance | No | Yes |
| Identity | No shared object identity | Has reference identity |
| Common use | Data values and models | Shared mutable state or inheritance-based designs |
Choose a struct when each value should stand on its own. Choose a class when multiple parts of your program need to refer to the same shared object.
struct Position {
var x: Int
}
var a = Position(x: 1)
var b = a
b.x = 5
print(a.x) // 1With a struct, b becomes a separate copy.
A common Swift guideline is: start with a struct, and switch to a class only when you need reference semantics or inheritance.
10. Practical Mini Project
This mini project creates a simple bank account struct. It stores data, uses methods, and demonstrates mutation clearly.
struct BankAccount {
var owner: String
private(set) var balance: Double
mutating func deposit(amount: Double) {
if amount > 0 {
balance += amount
}
}
mutating func withdraw(amount: Double) {
if amount <= balance {
balance -= amount
}
}
func summary() -> String {
"\(owner) has $\(balance)"
}
}
var account = BankAccount(owner: "Chris", balance: 100)
account.deposit(amount: 50)
account.withdraw(amount: 30)
print(account.summary())This example shows several useful ideas together: stored properties, access control, mutating methods, and a regular method that returns a formatted description. It is a good example of a value type because each account value can be copied safely and changed independently.
11. Key Points
- A Swift struct is a custom type that groups related data and behavior.
- Structs are value types, so assignment and passing create independent copies.
- They can contain stored properties, computed properties, methods, and initializers.
- Methods that change stored properties must be marked with mutating.
- A struct stored in a let constant cannot have its properties changed.
- Structs are usually the best starting point for data models in Swift.
- Classes are better only when you need shared identity, inheritance, or reference semantics.
12. Practice Exercise
Try building your own struct to reinforce the core ideas.
- Create a struct named Movie.
- Add stored properties for title, year, and rating.
- Add a method named description() that returns a formatted string.
- Add a mutating method named updateRating(to:) that changes the rating.
- Create a movie, print its description, update the rating, and print the description again.
Expected output: Two printed lines showing the same movie before and after the rating change.
Hint: If a method changes a property inside a struct, you need the mutating keyword.
struct Movie {
var title: String
var year: Int
var rating: Double
func description() -> String {
"\(title) (\(year)) - Rating: \(rating)"
}
mutating func updateRating(to newRating: Double) {
rating = newRating
}
}
var movie = Movie(title: "Skyline", year: 2024, rating: 7.2)
print(movie.description())
movie.updateRating(to: 8.1)
print(movie.description())13. Final Summary
Swift structs are a core part of the language and one of the best tools for modeling data clearly. They let you define custom value types with properties, methods, and computed values, while giving you the safety and predictability of value semantics.
In this article, you learned how to declare structs, create instances, use methods, write mutating behavior, and understand how copying works. You also saw common mistakes such as forgetting mutating or trying to change a struct stored in a constant.
A good next step is to learn more about Swift classes and initializers so you can choose the right custom type for each situation. Once you understand both structs and classes well, designing Swift models becomes much easier.