Custom Types As Raw Value for Enum in Swift

From Apple language guide :

Raw values can be strings, characters, or any of the integer or floating-point number types. Each raw value must be unique within its enumeration declaration.

After reading these lines from Apple many people remain under impression that custom types cannot be used as Raw values for enums in Swift.

Actually you might not require doing so in all your programming life. But it is technically possible.
If you go deeper and read language reference from Apple, you will come across following lines

In particular, the raw-value type must conform to the Equatable protocol and one of the following protocols: ExpressibleByIntegerLiteral for integer literals, ExpressibleByFloatLiteral for floating-point literals, ExpressibleByStringLiteral for string literals that contain any number of characters, and ExpressibleByUnicodeScalarLiteral or ExpressibleByExtendedGraphemeClusterLiteral for string literals that contain only a single character. Each case must have a unique name and be assigned a unique raw value.

So for your custom type (Class/Struct) to be used as raw value of enums you will need to conform to Equatable and one of the above mentioned protocols.

Lets take an example.
Suppose you have a product Phone. We want to categorise its color. The color of the phone can be a combination of two colors, front color and back color. We can define our custom Struct for this as follows.

public struct PhoneColor {
    var backColor = "Black"
    var frontColor = "Silver"
}

As mentioned earlier to qualify to be the raw value of enum our Struct PhoneColor must implement Equatable protocol methods. Lets do that.

public struct PhoneColor : Equatable {
    var backColor = "Black"
    var frontColor = "Silver"
    
    // MARK:- Equatable Methods
     public static func == (lhs: PhoneColor, rhs: PhoneColor) -> Bool {
        return (lhs.backColor == rhs.backColor && lhs.frontColor == rhs.frontColor)
    }
}

Can we use PhoneColor as raw value for enum? Lets try that

enum PhoneColorType : PhoneColor { // ERROR: raw type 'PhoneColor' is not expressible by any literal
    case blackSilver
    case blackBlack
    case sliverBlack
    case sliverSilver
}

Our custom type PhoneColor is not yet ready to be used as raw value. We will need to implement one of the protocols mentioned in Apple language reference guide. (See above section). In our example we will conform to ExpressibleByStringLiteral. Lets do that. Change the definition of PhoneColor to

public struct PhoneColor : Equatable, ExpressibleByStringLiteral {
    var backColor = "Black"
    var frontColor = "Silver"
    
    //MARK:- Equatable Methods
     public static func == (lhs: PhoneColor, rhs: PhoneColor) -> Bool {
        return (lhs.backColor == rhs.backColor && lhs.frontColor == rhs.frontColor)
    }
    
    //MARK:- ExpressibleByStringLiteral Methods
    public init(stringLiteral value: String) {
        let components = value.components(separatedBy: ",")
        if components.count == 2 {
            self.backColor = components[0]
            self.frontColor = components[1]
        }
    }
    
    public init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }
    public init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }
    
}

Now we have conformed to all the required protocols, our PhoneColor is ready to be used as raw value.
You will notice in the code that in init method, we have assumed that there would be two comma separated values for colors, first values would define back color and the second value would define front color.
Lets use PhoneColor as raw value for enum now.

enum PhoneColorType : PhoneColor {
    case blackSilver = "Black,Silver"
    case blackBlack = "Black,Black"
    case sliverBlack = "Silver,Black"
    case sliverSilver = "Silver,Silver"
}

We can now use this enum for our purpose.
Try using it like following

let myColor = PhoneColorType.blackSilver
print(myColor.rawValue.backColor) // prints : Black
print(myColor.rawValue.frontColor) // prints : Silver

Apart from custom type you can make any type provided by Apple, compatible to be used as raw value to enum. By now you should know what you need to do to achiever that. You will need to write extension for those inbuilt types and conform to above mentioned protocols.

So it works.
All said and done. Did you ever think, why is it required for a type to conform to these protocols to be fit for being the raw value for enum?
I will leave it for you to think.
HAPPY LEARNING!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s