Other than pass by value vs pass by reference, what are the differences between structs and classes?
I wouldn’t consider the way that parameters are passed to be a key difference between structs are classes. Swift will happily pass a struct by reference if it can prove that doing so doesn’t break the struct’s semantics. Critically, structs (along with enums) are expected to exhibit value semantics, while clases exhibit reference semantics, per KMT’s explanation.
This distinction is trickier than you might think. For example:
You can create a struct that uses a class to hold its core data, using copy on write to preserve value semantics. Many of Swift’s standard types do this, String, Array, Data, and so on.
If you don’t implement copy on write properly, you can end up with struct that has reference semantics! Don’t do that. Anyone reading your code, including Future You™, will be very confused.
If you create an immutable class it effectively has value semantics (because all references are equivalent). The Foundation framework has taken advantage of this for years (for example, NSUUID).
IMO the key difference between a struct and a class relates to identity. If you use a class, you can maintain a persistent identity for your data over time. This is essentially for modelling various real world constructs.
For example, consider a TCP connection. This exists in the real world, outside of your app. If you use a struct, what does the struct’s value semantics mean? If you make a copy, does it ‘clone’ the TCP connection. Such a concept is nonsense, and thus it only makes sense to use a class.