What is identity in Swift, and when do you use ===?
In Swift, identity is the idea that two references might point to the same object in memory, versus two objects merely having the same value. It only applies to reference types (classes) — value types like structs and enums don’t have identity, only equality, because each copy is independent.
Identity operators: === and !==
Swift gives you the identity operators === and !== to check whether two references point to the same instance:
class Person {
var name: String
init(name: String) { self.name = name }
}
let a = Person(name: "Alice")
let b = Person(name: "Alice")
let c = a
a === b // false — different instances, even though name matches
a === c // true — c points to the same instance as a
Identity vs. equality
This is different from ==, which checks equality (whether two things represent the same value) and requires conforming to Equatable. Two Person instances could be == if you defined equality based on name, while still being !== because they’re separate objects in memory. For classes, if you don’t implement Equatable yourself, == isn’t available by default — === works out of the box on any class because it’s just comparing memory addresses.
| Checks | Operator | Applies to | |
|---|---|---|---|
| Equality | Do these represent the same value? | == |
Any Equatable type |
| Identity | Are these literally the same object? | === |
Reference types (classes) only |
When identity is used
Mutation tracking — since classes are reference types, if a === c, mutating a.name also changes what c sees, because they’re the same object. Checking identity helps you reason about or guard against that kind of shared-state surprise.
Avoiding redundant work — e.g., in a didSet or update method, checking if self === sender { return } to avoid reacting to your own notification, a common pattern in delegate/observer setups.
Caching and deduplication — checking whether an object you’re about to insert into a cache or collection is literally the same instance already there, not just an equal-looking one.
Delegate patterns — verifying that a callback came from the delegate you expect: if delegate === self.expectedDelegate.
Using objects as dictionary/set keys by identity rather than value — Swift’s ObjectIdentifier wraps a class instance’s identity so it can be hashed and compared, useful when you want a Set or Dictionary keyed by “which specific object” rather than “which equal-valued object.”
Memory management debugging — confirming whether a weak reference still points to the original instance, or checking for accidental retain-cycle-related duplication.
Mental model
A useful mental model: == asks “do these represent the same value?” while === asks “are these literally the same object?” For structs, only the first question even makes sense, since there’s no shared instance to point to.