Structures and Classes
Structures and classes are general-purpose, flexible constructs that become the building blocks of your program's code. You define properties and methods on them just as you would on any other type. The crucial difference — value vs reference semantics — shapes how you design Swift programs, and Swift's bias toward structs is a defining trait of the language.
Comparing structures and classes
Structures and classes in Swift have a lot in common. Both can:
- Define properties to store values.
- Define methods to provide functionality.
- Define subscripts to access values with subscript syntax.
- Define initializers to set up their initial state.
- Be extended with
extension. - Conform to protocols.
Classes have additional capabilities that structures do not:
- Inheritance lets one class inherit the characteristics of another.
- Type casting lets you check and interpret a class instance's type at runtime.
- Deinitializers let an instance free resources when it's deallocated.
- Reference counting allows more than one reference to a class instance.
Definition syntax
Introduce structures with struct and classes with
class. Give types UpperCamelCase names and their
members lowerCamelCase names.
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
Creating instances and accessing members
Create an instance by writing the type name followed by parentheses. Access its properties — including nested ones — with dot syntax, and assign to them if the instance is mutable.
let someResolution = Resolution()
let someVideoMode = VideoMode()
print("width is \(someResolution.width)") // → width is 0
someVideoMode.resolution.width = 1280
print("width is now \(someVideoMode.resolution.width)") // → 1280
Memberwise initializers for structures
All structures get an automatic memberwise initializer that lets you initialize the member properties by name. Classes do not receive one — you must write your own initializer for a class (covered in the Initialization chapter).
let vga = Resolution(width: 640, height: 480)
print("\(vga.width) × \(vga.height)") // → 640 × 480
Structures and enumerations are value types
A value type is copied when it's assigned to a variable or
constant, or when it's passed to a function. All Swift structures and
enumerations are value types — each instance keeps its own independent copy
of its data. (So are the standard library's Array,
Dictionary, Set, and String.)
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd // cinema is a COPY of hd
cinema.width = 2048
print(cinema.width) // → 2048
print(hd.width) // → 1920 — original is unchanged
Tip: value semantics make code easier to reason about — passing a struct around can't let some far-off code mutate your copy out from under you. This is a big reason SwiftUI models so much state as structs.
Classes are reference types
A reference type is not copied when assigned or
passed; instead, the same existing instance is shared. Both names below
refer to one VideoMode, so a change through one is visible
through the other.
let tenEighty = VideoMode()
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty // same instance, not a copy
alsoTenEighty.frameRate = 30.0
print(tenEighty.frameRate) // → 30.0
Note that tenEighty and alsoTenEighty are declared
with let, yet you can still change their properties. The
constant holds the reference, and that reference never changes — but the
instance it points to is mutable.
Identity operators
Because class instances are shared, it's sometimes useful to ask whether two
constants refer to the exact same instance. Use the
identity operators === (identical to) and
!== (not identical to). These differ from ==, which
asks whether two values are equal.
if tenEighty === alsoTenEighty {
print("They refer to the same VideoMode instance.")
}
// → They refer to the same VideoMode instance.
let freshMode = VideoMode()
print(tenEighty === freshMode) // → false
Note: identity (===) only makes sense for
reference types. Value types have no identity — every copy is just as
"real" as any other.
Choosing between structures and classes
Apple's guidance for Swift 6 is to use structures by default. Reach for a class only when you have a specific reason. Use a structure when:
- The primary purpose is to encapsulate a few relatively simple data values.
- You expect copies to be independent rather than shared.
- The stored properties are themselves value types.
- The type doesn't need inheritance.
Use a class when:
- You need identity — the instance is unique and sharing it matters (e.g. a window, a file handle, a database connection).
- You need inheritance from another class.
- You need a deinitializer to release resources.
- You're interoperating with Objective-C frameworks that expect classes.
Watch out: under Swift 6 strict concurrency, sharing a
mutable class instance across concurrency domains is unsafe unless the type
is Sendable or isolated to an actor. Value-type structs sidestep
much of this because their copies don't share state — another reason structs
are the default.
Recap
Structures and classes share most of Swift's modeling features, but they
differ in the one thing that matters most: structs are copied (value
semantics) while classes are shared (reference semantics). Master that
distinction — and the === identity check — and you'll choose the
right tool every time. Next you'll see how both store and compute their data
with properties.