Skip to content

Chapter 4 of 7

Types: Structs, Classes, and Enums

Swift has two fundamental categories of types based on how they are stored and passed. Value types, which include structs, enums, and tuples, are copied when assigned or passed into a function, so each copy is independent and modifications to one do not affect the original. Reference types, which include classes, share a single instance through references, so modifying the instance through one reference is visible to all other references. This difference influences when each type is appropriate.

A struct is a value type that encapsulates related properties and methods. Structs automatically receive a memberwise initializer that accepts each stored property as a parameter, and they are copied on assignment. When a method on a struct or enum needs to modify its own properties, it must be marked with the mutating keyword; without it, the compiler prevents any property changes. Apple recommends structs as the default choice in Swift because of their value semantics, simplicity, and inherent thread safety through copying. Classes, by contrast, are reference types that support inheritance, deinitializers, and reference counting, and they are best used when reference semantics, inheritance, or shared mutable state is needed. Swift's standard collections like Array, Dictionary, and Set use a copy-on-write optimization, where the underlying storage is shared between copies until one copy is mutated, at which point a true copy is made, preserving the performance benefits of value semantics.

Enums in Swift are far more powerful than in many other languages. Each case can carry associated values of any type, allowing enums to model states with additional context, such as a network result carrying either success data or an error. Associated values are extracted through pattern matching with a switch statement, where case .success(let data) binds the data for use in that branch. The if case let syntax provides a similar capability for checking a single case. Raw values, by contrast, are fixed, compile-time constants of the same type assigned to each case, useful for cases like case north = "N" in a string-backed enum. A single enum cannot use both raw values and associated values at the same time, since raw values are static while associated values are runtime data that can differ per instance.

All chapters
  1. 1Optionals and Safe Unwrapping
  2. 2Protocols and Extensions
  3. 3Closures and Functional Patterns
  4. 4Types: Structs, Classes, and Enums
  5. 5Generics and Type Abstraction
  6. 6Error Handling and Control Flow
  7. 7Memory Management, Properties, and Advanced Features

Drill it

Reading is not remembering. These come from the Swift Programming deck:

Q

What is an Optional in Swift?

An Optional in Swift represents a variable that can hold either a value or nil. It is declared using a ? after the type, e.g., var name: String?. Optionals enfo...

Q

How do you unwrap an Optional using if let?

You use optional binding with if let:if let unwrapped = optionalValue {  print(unwrapped)}This safely unwraps the optional and binds its value to a ne...

Q

What is the difference between Optional chaining and forced unwrapping?

Optional chaining uses ?. and returns nil if the optional is nil, failing gracefully.Forced unwrapping uses ! and crashes at runtime if the optional is nil.Exam...

Q

What is the nil-coalescing operator in Swift?

The nil-coalescing operator ?? provides a default value when an optional is nil.Example: let name = optionalName ?? "Unknown"If optionalName is nil, name is set...