10

This is similar to a question I asked yesterday but the answer I got doesn't seem to work in this case.

I'm getting altitude values in meters from Core Location. I want to display these in a localized form. As an example, the altitude where I am right now is 1839m above sea level. This should be displayed as 6033 feet. The best I can do with MeasurementFormatter is "1.143 mi".

let meters : Double = 1839
let metersMeasurement = Measurement(value: meters, unit: UnitLength.meters)

let measurementFormatter = MeasurementFormatter()
measurementFormatter.locale = Locale(identifier: "en_US")

let localizedString = measurementFormatter.string(from: metersMeasurement)

The .naturalScale option that answered my previous question doesn't help here. I think this is a limitation of the framework, but I wonder if anyone has a workaround for now.

Community
  • 1
  • 1
Tom Harrington
  • 69,312
  • 10
  • 146
  • 170

2 Answers2

13

You just need to convert your UnitLength from meters to feet. You can also create a custom US measurement formatter to display it as needed:

extension Measurement where UnitType == UnitLength {
    private static let usFormatted: MeasurementFormatter = {
       let formatter = MeasurementFormatter()
        formatter.locale = Locale(identifier: "en_US")
        formatter.unitOptions = .providedUnit
        formatter.numberFormatter.maximumFractionDigits = 0
        formatter.unitStyle = .long
        return formatter
    }()
    var usFormatted: String { Measurement.usFormatted.string(from: self) }
}

Playground

let value: Double = 1839
let meters: Measurement<UnitLength> = .init(value: value, unit: .meters)
let feet = meters.converted(to: .feet)
let formatted = feet.usFormatted
print(formatted)    // "6,033 feet"\n
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • I believe you could also do this without the extension with just: `let localizedString = measurementFormatter.string(from: Measurement(value: meters, unit: .meters).converted(to: .feet))` – Benjamin Hall Mar 14 '19 at 19:25
3

I think you are correct there's no way to specify this kind of context. You could do something like:

extension MeasurementFormatter
{
    func altitudeString(from measurement: Measurement<UnitLength>) -> String
    {
        var measurement = measurement
        let unitOptions = self.unitOptions
        let unitStyle = self.unitStyle
        self.unitOptions = .naturalScale
        self.unitStyle = .long
        var string = self.string(from: measurement)
        if string.contains(self.string(from: UnitLength.miles))
        {
            self.unitStyle = unitStyle
            measurement.convert(to: UnitLength.feet)
            self.unitOptions = .providedUnit
            string = self.string(from: measurement)
        }
        else if string.contains(self.string(from: UnitLength.kilometers))
        {
            self.unitStyle = unitStyle
            measurement.convert(to: UnitLength.meters)
            self.unitOptions = .providedUnit
            string = self.string(from: measurement)
        }
        else
        {
            self.unitStyle = unitStyle
            string = self.string(from: measurement)
        }
        self.unitOptions = unitOptions
        return string
    }
}

Maybe there are other culturally specific ways of measuring elevation, but this would seem better than miles and kilometers.

beyowulf
  • 15,101
  • 2
  • 34
  • 40