So I have several enums representing various unit systems:
enum MassUnit:Double{
case Pound = 453.59237, Ounce = 28.349523125, Gram = 1.0, Kilogram = 1000.0;
}
enum VolumeUnit:Double{
case Teaspoon = 1, Tablespoon = 3, Cup = 48, Pint = 96, Quart = 192, Gallon = 768, Liter = 202.884136211, Milliliter = 0.202884136211
}
enum TimeUnit:Double{
case Second = 1, Minute = 60, Hour = 3600, Day = 86400, Week = 604800, Year = 31536000
}
What I would like to do is be able to convert from one unit to another, eg from year to seconds. To do this I've ensured that the raw values for my enums correspond to the converting multipliers, eg 1 Min = 60 seconds. Thus, given x amount of some unit, the conversion is simply
x * rawValue1 / rawValue2 // rawValue2 = rawValue of desired unit.
While that conversion is simple enough, I would love to be efficient and use a protocol:
protocol Convertable{
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
Then, I could extend the enum:
extension TimeUnit:Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: TimeUnit) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;
}
}
Then I could simply convert like this:
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second)
// returns 80101440
This is great, however, depending on how many units I want to convert, there would be a lot of duplication of the same code.
So, what I would like to do is somehow use a protocol extension.
extension Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: Self) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;// Compile error...
}
}
This is where I get into trouble, the output unit is declared as self, which knows nothing about rawValue.
Any ideas?
We can extend the enum in two orthogonal directions: we can add new methods (or computed properties), or we can add new cases. Adding new methods won't break existing code. Adding a new case, however, will break any switch statement that doesn't have a default case.
Yes, enums can conform protocols. You can use Swift's own protocols or custom protocols. By using protocols with Enums you can add more capabilities.
An enum cannot have both raw values and associated values at the same time. The raw values of an enum must be of the same data type. But associated values can be of any type.
Right when I was asking this question, the answer came to me: Have the Convertible protocol also require a rawValue variable:
protocol Convertable{
var rawValue:Double{get}
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
That way, referencing self.rawValue
is known, and since enums already have a rawValue, no extra work is required:
enum MassUnit:Double, Convertable{
case Pound = 453.59237, Ounce = 28.349523125, Gram = 1.0, Kilogram = 1000.0;
}
enum VolumeUnit:Double, Convertable{
case Teaspoon = 1, Tablespoon = 3, Cup = 48, Pint = 96, Quart = 192, Gallon = 768, Liter = 202.884136211, Milliliter = 0.202884136211
}
enum TimeUnit:Double, Convertable{
case Second = 1, Minute = 60, Hour = 3600, Day = 86400, Week = 604800, Year = 31536000
}
And then using the converter:
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second) // 80101440
MassUnit.Gram.convert(inputAmount: 20.0, outputUnit: .Ounce) //0.70547
VolumeUnit.Pint.convert(inputAmount: 0.2, outputUnit: .Tablespoon)// 6.4000
Even beter, in the odd case where a conversion doesn't work like this, I can override the convert function with my own implementation, such as in Temperature:
enum TemperatureUnit:Double, Convertable{
case Celsius, Fahrenheit
func convert(inputAmount inputAmount: Double, outputUnit: TemperatureUnit) -> Double {
if self == outputUnit {
return inputAmount;
} else if self == .Celsius {
return inputAmount * 9.0/5.0 + 32.0
} else {
return (inputAmount - 32.0) * 5.0 / 9.0;
}
}
}
TemperatureUnit.Celsius.convert(inputAmount: 3, outputUnit: .Fahrenheit) // 37.4
TemperatureUnit.Fahrenheit.convert(inputAmount: 0, outputUnit: .Celsius) // -17.7778
Beautiful!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With