I have a few measurements (like UnitLength
, UnitDuration
, UnitSpeed
etc) and for most of them I want to have a formatter that uses the provided unit (formatter.unitOptions = .providedUnit
).
To simplify the call site, I created a custom FormatStyle - for length it looks like this:
struct ProvidedUnitFormatStyle: FormatStyle {
func format(_ measurement: Measurement<UnitLength>) -> String {
let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
return formatter.string(from: measurement)
}
}
extension FormatStyle where Self == ProvidedUnitFormatStyle {
static var providedUnit: ProvidedUnitFormatStyle { .init() }
}
// usage: distance.formatted(.providedUnit)
But if I want to do the same for UnitDuration
, I'll have to repeat this code.
I tried using Measurement<Dimension>
in func format()
, hoping I'll be able to use this for all units, but the compiler complains:
Instance method 'formatted' requires the types
Measurement<Dimension>
andMeasurement<UnitDuration>
be equivalent
Is there a way to achieve this without repeating the code for every Unit
?
U can pass a generic Dimension
type and then use MeasurementFormatter
.
struct ProvidedUnitFormatStyle<D: Dimension>: FormatStyle where D: Unit {
func format(_ measurement: Measurement<D>) -> String {
let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
return formatter.string(from: measurement)
}
}
extension FormatStyle {
static func providedUnit<D: Dimension>(for dimension: D.Type) -> Self where Self == ProvidedUnitFormatStyle<D> {
return ProvidedUnitFormatStyle<D>()
}
}
// Usage:
let distance = Measurement(value: 100, unit: UnitLength.meters)
let duration = Measurement(value: 60, unit: UnitDuration.seconds)
let distanceString = distance.formatted(.providedUnit(for: UnitLength.self))
let durationString = duration.formatted(.providedUnit(for: UnitDuration.self))
print(distanceString) // 100 m
print(durationString) // 60 sec