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
}
x * rawValue1 / rawValue2 // rawValue2 = rawValue of desired unit.
protocol Convertable{
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
extension TimeUnit:Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: TimeUnit) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;
}
}
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second)
// returns 80101440
extension Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: Self) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;// Compile error...
}
}
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!