Difference Between Associated and Raw Values in Swift Enumerations

Difference between associated and raw values in swift enumerations

Raw values are for when every case in the enumeration is represented by a compile-time-set value. The are akin to constants, i.e.

let A = 0
let B = 1

is similar to:

enum E: Int {
case A // if you don't specify, IntegerLiteralConvertible-based enums start at 0
case B
}

So, A has a fixed raw value of 0, B of 1 etc set at compile time. They all have to be the same type (the type of the raw value is for the whole enum, not each individual case). They can only be literal-convertible strings, characters or numbers. And they all have to be distinct (no two enums can have the same raw value).

Associated values are more like variables, associated with one of the enumeration cases:

enum E {
case A(Int)
case B
case C(String)
}

Here, A now has an associated Int that can hold any integer value. B on the other hand, has no associated value. And C has an associated String. Associated types can be of any type, not just strings or numbers.

Any given value of type E will only ever hold one of the associated types, i.e. either an Int if the enum is an A, or a String if the enum is a C. It only needs enough space for the bigger of the two. Types like this are sometimes referred to as "discriminated unions" – a union being a variable that can hold multiple different types, but you know (from the enum case) which one it is holding.

They can even be generic. The most common example of which is Optional, which is defined like this:

enum Optional<T> {
case .Some(T)
case .None
}

in swift enum, when to use raw value and when to use associated value

You pick one that logically matches your business case/requirement/feature/model BETTER.

You should look at the anatomy of swift Enum and try to find cases/situations/options in real world which match one of the two Enum variants. I'll give you an example for both.

RAW VALUE

enum CountryAcronyms: String {

case UnitedKingdom = "UK"
case Germany = "DE"
case Australia = "AU"
}

Here you are dealing with cases are all the same category of things which is Country and each country can be represented by a single one acronym, that is of type String. The important fact here is that the underlying type for acronyms all across is String.
So "RawValue"..in other words is when you have ONE AND THE SAME underlying type chosen to represent EACH case. The when you want to extract the underlying value of the underlying type, you use the rawValue accessor.

ASSOCIATED VALUE

enum Trip {

case Abroad(Airplane, Taxi, Foot)
case Grandma(Tube, Foot)
case McDonalds(Car)
case MountEverest(Ski, Foot)
}

Here we have a set of cases and each represent also one thing - a Trip, but the associated types in this enum represent THE MEANS (this is what we chose..that's it! Perhaps there is a business case or a design ..or simply customer wants it..) and Since the means DIFFER for each case, we associate a unique type (in this case tuples with 1 or more types) that is able to represent the means. Since we wanted to represent something like this we couldn't chose the previous Enum approach because we would have no way to express various means.

Raw Value of Enumeration, Default value of a class/structure, What's the different?

From Apple docs:

Raw Values

The barcode example in Associated Values shows how cases of an
enumeration can declare that they store associated values of different
types. As an alternative to associated values, enumeration cases can
come prepopulated with default values (called raw values), which are
all of the same type.

So I guess it is the same.

On the other hand, with "default value", you may be referring to the default value of an enum case where no values have been set, for example:

enum TestEnum: Int  {    
case A
case B
}

Here, TestEnum.A has a default value of 0, and TestEnum.B has a default value of 1.

Raw value refers to the actual value of an enum case (in the enum's type, in this example it would be Int):

enum TestEnum: Int  {    
case A
case B = 3
}

Here, TestEnum.A has the default value (which is also the raw value) of 0, and TestEnum.B has a raw value of 3 (which is no longer the default value).

Can a Swift enum have a function/closure as a raw value?

It's not as straight forward, but you could use OptionSet, see this page:

Unlike enumerations, option sets provide a nonfailable init(rawValue:) initializer to convert from a raw value, because option sets don’t have an enumerated list of all possible cases. Option set values have a one-to-one correspondence with their associated raw values.

Could be something like this:

func doSomething() {}
func doSomethingElse() {}

struct MyClosures: OptionSet {

static let closureOne = MyClosures(rawValue: doSomething)
static let closureTwo = MyClosures(rawValue: doSomethingElse)

let rawValue: () -> Void

init(rawValue: @escaping () -> Void) {
self.rawValue = rawValue
}

init() {
rawValue = {}
}

mutating func formUnion(_ other: __owned MyClosures) {
// whatever makes sense for your case
}

mutating func formIntersection(_ other: MyClosures) {
// whatever makes sense for your case
}

mutating func formSymmetricDifference(_ other: __owned MyClosures) {
// whatever makes sense for your case
}

static func == (lhs: MyClosures, rhs: MyClosures) -> Bool {
// whatever makes sense for your case
return false
}
}

And so you can use it as:

let myClosures: MyClosures = [ .closureOne, .closureTwo ]

HOWEVER looking at your explanation in the comment:

So I'm trying to find the most efficient way to run a function given the state of a variable.

I think what you actually want is some sort of state machine. Some examples are available here and here

Comparing two enum variables regardless of their associated values

Updated approach:

I think there's no native support for this. But you can achieve it by defining a custom operator (preferrably by using a protocol, but you can do it directly as well). Something like this:

protocol EnumTypeEquatable {
static func ~=(lhs: Self, rhs: Self) -> Bool
}

extension DataType: EnumTypeEquatable {
static func ~=(lhs: DataType, rhs: DataType) -> Bool {
switch (lhs, rhs) {
case (.one, .one),
(.two, .two):
return true
default:
return false
}
}
}

And then use it like:

let isTypeEqual = DataType.One(value: 1) ~= DataType.One(value: 2)
print (isTypeEqual) // true




Old approach:

protocol EnumTypeEquatable {
var enumCaseIdentifier: String { get }
}

extension DataType: EnumTypeEquatable {
var enumCaseIdentifier: String {
switch self {
case .one: return "ONE"
case .two: return "TWO"
}
}
}

func ~=<T>(lhs: T, rhs: T) -> Bool where T: EnumTypeEquatable {
return lhs.enumCaseIdentifier == rhs.enumCaseIdentifier
}

The older version depends on Runtime and might be provided with default enumCaseIdentifier implementation depending on String(describing: self) which is not recommended. (since String(describing: self) is working with CustromStringConvertible protocol and can be altered)

Can Swift enums have multiple raw values?

No, an enum cannot have multiple raw values - it has to be a single value, implementing the Equatable protocol, and be literal-convertible as described in the documentation.

I think the best approach in your case is to use the error code as raw value, and a property backed by a prepopulated static dictionary with the error code as key and the text as value.



Related Topics



Leave a reply



Submit