Question 1
Value vs reference semantics — struct, class, actor
When would you choose a struct vs a class vs an actor for a model type in an iOS app? What are the tradeoffs for identity, mutation, and concurrency?
Answer outline
Structs are value types: assignment logically copies (often optimized with copy-on-write). Prefer them for domain values, == semantics, and predictable copies.
They cannot inherit or participate in classic retain cycles the way classes can and as such are more suitable for Protocol-oriented-programming.
Classes are reference types: shared identity (===), inheritance, and Objective-C interop.
Use when you need shared mutable state or reference semantics. ARC applies; retain cycles with closures and delegates are a real risk.
Actors are reference types with isolated mutable state and compiler-enforced serial access.
They should be used for concurrent shared resources, not for every model.
SwiftUI view models are often structs; observable state may live in an ObservableObject (with Combine framework) or @Observable class on the main actor.
Principles
- Prefer structs for pure data and predictable copy behavior; classes for identity and shared lifecycle.
- Actors solve mutation under concurrency. They are not a drop-in replacement for classes in UI hierarchies.
struct Point: Equatable {
var x: Double
var y: Double
}
var a = Point(x: 0, y: 0)
var b = a
b.x = 1
// a is unchanged; independent copies
final class UserSession {
var token: String
init(token: String) { self.token = token }
}
let s1 = UserSession(token: "a")
let s2 = s1
s2.token = "b"
// s1.token is also "b" — same instance
actor Counter {
private var value = 0
func increment() { value += 1 }
func current() -> Int { value }
}
func useCounter() async {
let counter = Counter()
await counter.increment()
_ = await counter.current()
}
Follow-up angles
- SwiftUI: view bodies are structs; observable state often lives in
ObservableObjector@Observabletypes. - Bridging to Obj-C: classes subclass
NSObject.