Why do enums have computed properties but not stored properties in Swift? Why do enums have computed properties but not stored properties in Swift? swift swift

Why do enums have computed properties but not stored properties in Swift?


enums do have stored type properties - i.e., static properties. They don't have stored instance properties. I don't know if there is a technical reason why stored instance properties are not available for enums. You may have to ask your question on the dev forum if you want a technical answer for "why".

In your question you ask if associated values work like stored properties. In fact, they do, and are more flexible (in some ways) than stored properties for structs and classes. Each case in an enum can have its own specialized set of data that is associated with it. Rather than have one set of stored properties that apply to all cases, you get to individualize the stored properties for each case.


Enums not allowing stored instance properties is a design choice. Having enum with stored instance properties makes it like a struct (with enum superpowers), but just from type perspective now enums will act like type multipliers. Basically consider

enum Set1 {    case a    case b    case c}enum Times {    case x    case y    var k: Set1}

What this actually mean that enum Times allows us having any combination of elements from Set1 and Set2 resulting in 6 distinct cases, but wait, we know actually this is a purpose of a tuple type like (Set1, Set2), where Times can be declared as

typealias Times = (Set1, Set2)

I hope this serves as a reasonable rationale for not allowing the former case.

That being said, swift enums allow us associating an arbitrary n-tuple with each case, thus allowing us to declare what is known as discriminated union in functional programming. Call it a stored property attached to case if you like. From types perspectives it now acts as type adder.

enum Add {    case lhs(Set1)    case rhs(Set2)}

We now have 5 different cases. If we now store 2-tuples:

enum AddTimes {    case lhs(Set1, Set2)    case rhs(Set3, Set4)}

we now basically have sum of multiplication (Set1 * Set2 + Set3 * Set4).This is a very powerful tool when it comes to pattern matching.

HOWEVER, there exist some real cases when you actually want to emulate the form of stored property inside enum. Consider this:

public enum Service {    case registerNewUser(username: String, password: String, language: String)    case login(username: String, password: String, deviceTokenº: String?)    case logout(sessionToken: String)    case sendForgotPassword(email: String)}

is a declarative way to define REST endpoints (in framework like Moya)When you want to fire a request you would do something like

MoyaProvider<Service>.request(.sendForgotPassword(email: "foo@foo.com"))

But now imagine you want to differentiate between you production and test server. If you add a server as another tuple element in each case:

case forgotPassword(sessionToken: String, serverBaseURLString: String)

this will be have a wrong semantics, since you originally intend each tuple to store request parameters, but now it stores a server base address.

To avoid things like this we can actually parametrize our type in the following way. Instead of having Server being defined as say:

enum Server: String {    case production = "https://service.info"    case test = "http://test.service.info"}

we can define it with distinct type for each case like:

public struct ProductionServer: ServerType {    public static var baseURLString: String { return "https://service.info" }}public struct TestServer: ServerType {    public static var baseURLString: String { return  "http://test.service.info" }}public protocol ServerType {    static var baseURLString: String { get }}

and finally parametrize our ServiceType as

public enum Service<T> where T: ServerType {    case registerNewUser(username: String, password: String, language: String)    case login(username: String, password: String, deviceTokenº: String?)    case logout(sessionToken: String)    case sendForgotPassword(email: String)    var serverURL: URL {        return T.baseURL    }}public typealias ProdutionService = Service<ProductionServer>public typealias TestService = Service<TestServer>


I use a small trick to store properties that are not accesible on initialization.

First, I create a class Future which will store the stored property in the future:

class Future<T> {  var value: T?  init(value: T? = nil) {      self.value = value  }}

Then I create my enum:

enum Sample {  case Test(future: Future<String>)}

Instantiate:

let enumTest = Sample.Test(future: Future())

Later in the code:

switch enumTest {  case let .Test(future):  future.value = "Foo"}

And later on, you can access the value:

switch enumTest {  case let .Test(future):  // it will print Optional("Foo")  print("\(future.value)")}

This is not something you should abuse, but it can be helpful in some cases.

Hope it helps.