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

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.

Swift
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.

Swift
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.

Swift
print(swiftBook.title)
print(swiftBook.pageCount)
Output✓ Tested · Swift 6.4
Learning Swift
320

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.

Swift
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.

Swift
struct User {
    var name: String
    var age: Int
}

let user = User(name: "Maya", age: 28)
print(user.name)
print(user.age)
Output✓ Tested · Swift 6.4
Maya
28

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.

Swift
struct Temperature {
    var celsius: Double

    var fahrenheit: Double {
        (celsius * 9 / 5) + 32
    }
}

let today = Temperature(celsius: 21)
print(today.fahrenheit)
Output✓ Tested · Swift 6.4
69.8

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.

Swift
struct Counter {
    var count = 0

    mutating func increment() {
        count += 1
    }
}

var counter = Counter()
counter.increment()
print(counter.count)
Output✓ Tested · Swift 6.4
1

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.

Swift
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)
Output✓ Tested · Swift 6.4
0
10

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.

Swift
struct Counter {
    var count = 0

    func increment() {
        count += 1
    }
}
Compiler output✓ Tested · Swift 6.4
error: left side of mutating operator isn't mutable: 'self' is immutable

Fix: Mark the method as mutating so Swift knows the struct value will change.

Swift
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.

Swift
struct Task {
    var title: String
}

let item = Task(title: "Write article")
item.title = "Review article"
Compiler output✓ Tested · Swift 6.4
error: cannot assign to property: 'item' is a 'let' constant

Fix: Use var for the struct value if you need to modify any of its properties.

Swift
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.

Swift
struct Score {
    var points: Int
}

var original = Score(points: 10)
var copy = original
copy.points = 20
print(original.points)
Output✓ Tested · Swift 6.4
10

Fix: Treat structs as independent values after assignment. If you need shared identity and shared mutation, consider whether a class is the better model.

Swift
struct Score {
    var points: Int
}

var original = Score(points: 10)
var copy = original
copy.points = 20

print(original.points) // 10
print(copy.points) // 20
Output✓ Tested · Swift 6.4
10
20

The 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.

Swift
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.

Swift
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.

Swift
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.

FeatureStructClass
SemanticsValue typeReference type
Copy behaviorCopied on assignmentShared reference on assignment
InheritanceNoYes
IdentityNo shared object identityHas reference identity
Common useData values and modelsShared 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.

Swift
struct Position {
    var x: Int
}

var a = Position(x: 1)
var b = a
b.x = 5
print(a.x) // 1
Output✓ Tested · Swift 6.4
1

With 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.

Swift
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())
Output✓ Tested · Swift 6.4
Chris has $120.0

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.

Show solution
Swift
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())
Output✓ Tested · Swift 6.4
Skyline (2024) - Rating: 7.2
Skyline (2024) - Rating: 8.1

Where AI Coding Assistants Get This Wrong

Patterns AI tools commonly produce for this topic, and what to use instead.

Often suggested: Declares public struct Point { public var x: Int; public var y: Int } in a package and has client code call Point(x:y:).

Instead: Write an explicit public init(x: Int, y: Int), because the synthesized memberwise initializer is at most internal.

Clients fail to compile with "'Point' initializer is inaccessible due to 'internal' protection level".

Often suggested: Claims that a struct wrapping a class, or any large struct, automatically gets copy-on-write, or that copying a struct deep-copies everything inside it.

Instead: Only standard-library types such as Array, Dictionary, and String implement CoW. For custom reference storage, check isKnownUniquelyReferenced(&storage) before mutating and clone the storage when it's shared.

Without that check, copies share one class instance and mutate each other.

Often suggested: Expects a public struct whose stored properties are all Sendable to be Sendable in other modules automatically, or patches the error with @unchecked Sendable.

Instead: Declare : Sendable explicitly on public types, and the compiler will check the stored properties for you.

Implicit Sendable inference covers only non-public types and @frozen public ones. Other modules get "type 'X' does not conform to the 'Sendable' protocol".

Check Your Understanding

Pick an answer for each question, then check your score.

1. What kind of type is a Swift struct?
Show answer

Answer: A value type. Assigning or passing a struct creates an independent copy.

2. Given struct Point { var x: Int }, after var a = Point(x: 1); var b = a; b.x = 5, what is a.x?
Show answer

Answer: 1. b is a copy of a, so changing b doesn't affect a.

3. Which keyword does a struct method need in order to change the struct's own properties?
Show answer

Answer: mutating. Struct methods can't modify self unless they're marked mutating.

4. When is a class a better choice than a struct?
Show answer

Answer: When you need shared identity, inheritance, or reference semantics. Structs are the usual starting point for data models. Choose a class when several parts of the code must share one instance.

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.