Andrew
Andrew

Reputation: 551

Using MeasurementFormatter with Derived unit

When I run the following code in a Playground the formatted string comes back as nil. What am I missing in the derived custom Measurement class?

open class UnitFlowRate : Dimension {
    open override static func baseUnit() -> UnitFlowRate {  return self.metricTonsPerHour }

    static let shortTonsPerHour = UnitFlowRate(symbol: NSLocalizedString("stph", comment: "short tons per hour"), converter: UnitConverterLinear(coefficient: 1))
    static let metricTonsPerHour = UnitFlowRate(symbol: NSLocalizedString("mtph", comment: "metric tons per hour"), converter: UnitConverterLinear(coefficient: 2))
}

var measureCustom = Measurement<UnitFlowRate>(value: 12.31, unit: .shortTonsPerHour)
var measureSystem = Measurement<UnitLength>(value: 12.31, unit: .inches)

var formatter = MeasurementFormatter()
var measureStringCustom = formatter.string(for: measureCustom)
var measureStringSystem = formatter.string(for: measureSystem)
print( measureCustom ) // This works
print( measureSystem ) // This works
print( measureStringCustom ) // This is nil - Why?
print( measureStringSystem ) // This works

Output:

12.31 stph
12.31 in
nil
Optional("0 mi")

Upvotes: 1

Views: 1063

Answers (1)

ganzogo
ganzogo

Reputation: 2614

You need to update a few things in your code.

Firstly, you are using the string method on Formatter which takes Any? and returns an optional String. If you change the parameter name to from, you will use the method defined on MeasurementFormatter which returns a non-optional:

var measureStringCustom = formatter.string(from: measureCustom)

Secondly, you are using a MeasurementFormatter which has the unitOptions property set to .naturalScale (the default). If you change this to, .providedUnit, you'll see that you now get some output. The problem is that .naturalScale will use the appropriate unit for the given locale and there is currently no way to set what that is for custom Dimension subclasses.

So, the way to achieve what you what is to use the converted method along with a .providedUnit formatter, like so:

let converted = measureCustom.converted(to: .metricTonsPerHour)
var formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
print(formatter.string(from: converted))

Finally, you are probably still not getting the output you expect. This is because the coefficient for the UnitConverterLinear which is returned by baseUnit should be 1. I expect you intended to define your dimension as follows (notice the scaled-down coefficients):

open class UnitFlowRate : Dimension {
  open override static func baseUnit() -> UnitFlowRate {  return self.metricTonsPerHour }
  static let shortTonsPerHour = UnitFlowRate(symbol: NSLocalizedString("stph", comment: "short tons per hour"), converter: UnitConverterLinear(coefficient: 0.5))
  static let metricTonsPerHour = UnitFlowRate(symbol: NSLocalizedString("mtph", comment: "metric tons per hour"), converter: UnitConverterLinear(coefficient: 1))
}

Upvotes: 2

Related Questions