Search code examples
swiftpolymorphism

Function that accepts any type of Measurement in swift


Swift implements the Measurement type which can be used to hold values such as speeds, altitudes, etc. in their desired metric.

For example:

// Going to explicitly type here so the question is clear

var speed: Measurement<UnitSpeed> = Measurement(value: 10, unit: UnitSpeed.metersPerSecond)
var altitude: Measurement<UnitLength> = Measurement(value: 500, unit: UnitLength.meters)

altitude.convert(to: UnitLength.feet)

print("500 meters is roughly \(Int(altitude.value)) feet")
// Prints: 500 meters is roughly 1640 feet

However, even though UnitLength and UnitSpeed are both subclasses of Unit, measurements using these types can not be passed in polymorphically into a function in swift.

For example, the following function does not work if you pass in an object of type Measurement<UnitLength> or Measurement<UnitSpeed>.

func formatMeasurementAsString(measurement: Measurement<Unit>) -> String {
    return "\(Int(measurement.value)) \(measurement.unit.symbol)"
}

Passing in either speed or altitude defined above into this function yields the following error: Cannot convert value of type 'Measurement<UnitSpeed>' to expected argument type 'Measurement<Unit>'

EDIT:

So you can explicitly type both speed and altitude in this example as type Measurement<Unit>. However, if you do this, they lose the incredibly useful .convert(to:) function unless you typecast them back into Measurement<UnitLength> before converting, which is not very helpful.


Solution

  • You can make the function generic

    func formatMeasurementAsString<T: Unit>(measurement: Measurement<T>) -> String {
        return "\(Int(measurement.value)) \(measurement.unit.symbol)"
    }
    

    Example

    let length = Measurement(value: 180.0, unit: UnitLength.meters)
    let speed = Measurement(value: 60, unit: UnitSpeed.kilometersPerHour)
    
    print(formatMeasurementAsString(measurement: length))
    print(formatMeasurementAsString(measurement: speed))
    

    180 m
    60 km/h