Swift Basic Types: Value Semantics by Default
Swift's basic types are value type structs, so assignment copies, not shares, a reference. You feel this when passing Strings into functions or choosing Int over Double. The footgun is treating them as free to copy; large values cost memory and speed.
WHY IT EXISTS: Swift was designed to replace Objective-C's loose type safety and reference-heavy semantics. The language needed basic data types that feel like primitives but behave predictably across threads and scopes. By making Int, Double, String, and Bool value-semantic structs, Swift eliminates accidental shared state at the lowest level of the type system. Even the simplest variable follows the same ownership rules as a custom struct, removing an entire class of mutation bugs.
THE MENTAL MODEL: Think of Swift's basic types as self-contained envelopes. When you hand one to another variable or function, you hand over a duplicate envelope, not a shared mailbox. The recipient can change the contents without affecting your original. This is the opposite of class instances, where multiple variables point to the same object in the heap.
HOW IT WORKS: Under the hood, Swift implements Int, Double, String, Bool, and related types as structs in the standard library. Because they are structs, assignment performs a bitwise copy of the instance. For small fixed-size types like Int or Bool, this copy is effectively free. For String and other larger value types, Swift uses copy-on-write optimization. The data stays shared until one copy mutates it, at which point a true copy is made. This keeps pass-by-value semantics cheap while avoiding unnecessary duplication.
WHEN TO USE IT: Use Int for countable discrete values and Double for measurements or fractions where precision matters. Use String for text and Bool for binary state. Prefer these value types whenever you want local reasoning; a function that takes an Int cannot secretly modify the caller's variable. This makes value types the default choice for data models, configuration, and anything you want to reason about in isolation.
WHEN NOT TO USE IT: Do not use value types when you genuinely need shared mutable state across multiple owners. If three view controllers must observe and mutate the same data source, a struct will force manual synchronization or large copies. In those cases, a class or actor is more appropriate. Also avoid assuming value semantics are free for massive collections; copying a large array on every assignment will burn CPU and memory even with copy-on-write.
ONE CANONICAL EXAMPLE: Imagine a function that formats a user display name. You pass a String into the function, which uppercases it locally. Because String is a value type, the original variable in the caller remains unchanged. If String were a reference type, the caller's string would mutate unexpectedly. This local safety is why SwiftUI state and UIKit data sources rely on value semantics for predictable UI updates.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.