How to Reason When I Have to Choose Between a Class, Struct and Enum in Swift

How should I reason when I have to choose between a class, struct and enum in Swift?

ChristopheD's and Jack Wu's answers are good, but I feel they don't touch on enums, or miss their importance. Swift enums are (meant to be) a full implementation of algebraic data types. Classes and structs are traditionally used to model data in object-oriented languages, but enums are usually limited to being used as a convenient way to limit the value of a variable to a limited number of possibilities. E.g. (C++):

enum MaritalStatus { Unmarried, Married, Divorced, WidowedOrWidowered };
MaritalStatus m = Unmarried;

Swift enums can do the above but they can do a lot more. Of course the Language Guide has a pretty good barcode modelling example but the best example I know of that really drives home the point of modelling data with algebraic data types is Scott Wlaschin's presentation: http://www.slideshare.net/ScottWlaschin/ddd-with-fsharptypesystemlondonndc2013

You would probably benefit from going through the whole presentation but really to 'get' the point all you need to see is slide 60 where he shows how to model a 'payment method' in a typical line-of-business app.

The examples in the presentation are in F# but F# isn't that far off from Swift and you can pretty easily map between them. E.g., the payment methods enum in Swift would look like:

enum PaymentMethod {
case cash // No extra data needed.
case cheque(Int) // Cheque #.
case card(CardType, CardNumber) // 2 pieces of extra data.
}

The point of the above is that each order's payment method can be only one of the above three methods. Anything else will not be allowed by the compiler. This is a very succinct alternative to building entire class hierarchies to model these almost trivial things.

The presentation really takes off from there and the best part is Swift can do almost everything that F# can in terms of data modelling, using optional types, etc.

Whats the difference between Enum, Structs, Classes

I think Chris Upjohn gives a rather simple yet great explanation on the topic in the Treehouse:

Enums

An enum is considered as a structured data type that can be modified without needing to change say a String or Int multiple times within your code, for example, the below shows how easy it would be to change something by accident and forget to change it somewhere else.

let myString = "test"

if myString == "ttest" {
// Doesn't execute because "ttest" is the value assigned to "myString"
}

With an enum we can avoid this and never have to worry about changing the same thing more than once.

enum MyEnum: String {
case Test = "test"
}

let enumValue = MyEnum.Test

if enumValue == MyEnum.Test {
// Will execute because we can reassign the value of "MyEnum.Test" unless we do so within "MyEnum"
}

Structs

I'm not sure how much you know about the MVC pattern but in Swift, this is a common practice, before I explain how structs are useful I'll give a quick overview of MVC in Swift.

Model - struct, useful for managing large amounts of data

View - Anything that extends UIView, more often than not this is a controller you manage on the storyboard

Controller - class, typically used only for views such as UIView controllers and UITableView

Moving on a struct as I said is used for managing large amounts of data, for instance, humans are a good example as we can use a struct to manage each person in a contact list.

struct Birthday {
var day: Int
var month: Int
var year: Double
}

struct Person {
var firstName: String
var lastName: String
var birthday: Birthday
var phoneNumber: String
var emailAddress: Int
}

For each contact you have you would create a new Person object that contains basic details along with a Birthday struct for complete reusability, the benefit to using the Birthday struct is the fact we can extend it without breaking our code, for example, if we needed an easy way to format the person's birthday we can add an additional function without affecting the rest of our code.

Classes

More often than not you would only find classes bound views, when bound to a view iOS will automatically assign a new instance of the class whenever a view is called, the second time the view is called it requests the already created instance of the class.

Other uses for a class is utility helpers which you can create as singletons, this is more of an advanced concept and generally you only need to create code like this on larger applications as generally everything you need is already built-in however it's recommend if you do need additional functionality that you use an extension which allows you to add to any built-in object and create your own subscripts.


Disclaimer: The following text belongs to Chris Upjohn. I could not have explained it any better in terms of Swift (maybe in general CS terms, using other languages), therefore I did not see any point rephrasing something similar to this.


Hope it helps!

Why Choose Struct Over Class?

According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift (video, transcript), Swift provides a number of features that make structs better than classes in many circumstances.

Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment. If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.

With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable. (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied).

Classes can also become bloated because a class can only inherit from a single superclass. That encourages us to create huge superclasses that encompass many different abilities that are only loosely related. Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.

The talk lays out these scenarios where classes are preferred:

  • Copying or comparing instances doesn't make sense (e.g., Window)
  • Instance lifetime is tied to external effects (e.g., TemporaryFile)
  • Instances are just "sinks"--write-only conduits to external state (e.g.CGContext)

It implies that structs should be the default and classes should be a fallback.

On the other hand, The Swift Programming Language documentation is somewhat contradictory:

Structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.

As a general guideline, consider creating a structure when one or more
of these conditions apply:

  • The structure’s primary purpose is to encapsulate a few relatively simple data values.
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an
    instance of that structure.
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
  • The structure does not need to inherit properties or behavior from another existing type.

Examples of good candidates for structures include:

  • The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double.
  • A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int.
  • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double.

In all other cases, define a class, and create instances of that class
to be managed and passed by reference. In practice, this means that
most custom data constructs should be classes, not structures.

Here it is claiming that we should default to using classes and use structures only in specific circumstances. Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes. Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.

Swift constants: Struct or Enum

Both structs and enumerations work. As an example, both

struct PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

and

enum PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

work and define a static property PhysicalConstants.speedOfLight.

Re: A struct will be copied every time i use it or not?

Both struct and enum are value types so that would apply to enumerations as well. But that is irrelevant here
because you don't have to create a value at all:
Static properties (also called type properties) are properties of the type itself, not of an instance of that type.

Re: What advantages has the choice of a struct or enum?

As mentioned in the linked-to article:

The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.

So for a structure,

let foo = PhysicalConstants()

creates a (useless) value of type PhysicalConstants, but
for a case-less enumeration it fails to compile:

let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers

Why Swift library uses enum CommandLine instead of struct CommandLine ?

Ok, based on some research, I found this:

The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace. (Ref: https://github.com/raywenderlich/swift-style-guide#constants)

I believe this could possibly be one of the reasons.

Why would I prefer an enum to a struct with constant values

Is there any other benefit of preferring enums besides this one?

Clarity.

Suppose you have a field or a method parameter which will always have one of those three values. If you make it the enum type, then:

  • You can't accidentally assign it some arbitrary integer value. (You can deliberately cast any int value to the enum type, but you'd have to do so explicitly.)
  • It's clear what kind of information that value is meant to represent

These are incredibly important benefits in writing code which is easy to maintain in the future. The more you can make your code naturally describe what you're trying to achieve, the better.

Is there a technical reason to use Swift's caseless enum instead of real cases?

Enum with cases

It is better to create an enum with cases in the following scenarios:

  • It is mutually exclusive
  • Finite set of values that you know at compile time
  • You are the one defining it (If an enum is defined in a framework you wouldn't be able to extend it to add more cases)

Advantage of an enum are:

  • Since the values are a finite set, you can write exhaustive switch statements
  • cleaner code

Static values:

When a struct / class is defined in a framework and you want to extend it to add more values.

Example of where this approach is used is Notification.Name in Foundation

Note:

  • enums in swift are quite powerful
  • enums can have associated values
  • enums can have other functions. (if you are defining states like start, inProgress, finished, you could define a function called next, which could return the next state. start.next()
  • if you are in a scenario where values are not mutually exclusive, like it could a combination of values then use OptionSet instead

Conclusion

  • It all depends on your intent

  • If you know the values before hand and they will not change, then create an enum

  • If that is not possible then create static values.

  • If you are creating static values, you are compromising so you don't have to use it in an enum, you could define it as a struct so that the intent is clearer.

  • This is as of now, there is a swift proposal for extendable enums

Struct or Enum to use for serialization keys?

I do the same thing, sometimes, and here's why.

With a struct, my values are directly available: so, if SerializationKeys is a struct, then SerializationKeys.text is a string.

But with an enum, the enum is the value. If SerializationKeys is an enum, then SerializationKeys.text is not a string; it's an enum. If I want the string, I have to fetch it explicitly, as the enum's rawValue. Sometimes, that's just too nutty. On the other hand, if it's acceptable, or if there's another reason why this makes a good enum, then fine, I'll use an enum.

To put it another way: if this is just a glorified namespace for some constants, a struct with static members seems simplest. An enum is for a switch, i.e. something that needs to exist in exactly one of several possible states.



Related Topics



Leave a reply



Submit