Format speed in Swift - ios

I want to be able to format and print speed in localised form. I found a few pods that can do that but I'm looking for embedded solution (if possible).
Currently I do next:
let unit = HKUnit.meterUnit(with: .kilo).unitDivided(by: .hour())
let output = HKQuantity(unit: unit, doubleValue: 12.5).description
But in this case I cannot tune anything like use h instead of hr.

As said #Allan we have to use MeasurementFormatter. Check out the ready to use solution below.
import UIKit
import HealthKit
let value = NSMeasurement(doubleValue: 12.5, unit: UnitSpeed.milesPerHour)
let formatter = MeasurementFormatter()
formatter.numberFormatter.maximumFractionDigits = 2
formatter.string(from: value as Measurement<Unit>) // prints 12.5 mph

The description method of HKQuantity does not return a localized value and it is meant for debugging, not for display. Convert the HKQuantity instance to an NSMeasurement and use NSMeasurementFormatter (documentation here) to localize the value for display.

Related

Formatting localized numbers with unit symbols

Situation
I want to format a Double 23.54435678 into a String like 23.54 fps respecting the user's locale.
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
let formatted = formatter.string(from: fps as NSNumber)! + " fps"
For the localized number formatting I use DateFormatter.
Question
How should I handle the unit part? Is it valid to just append the unit to the formatted number? Is the placement of the symbol not locale dependent? How do I handle that?
Cocoa has no built-in support for the unit "frames per second", so you will have to provide the suffix yourself, e.g. using Xcode's localization system.
You still need to format the numeric value with NumberFormatter for the current locale and then insert the resulting number string into the localized format string:
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
let numberString = formatter.string(from: fps)
let formatString = NSLocalizedString("%# fps", comment: "") // provide localizations via .strings files
let fpsString = String(format: formatString, arguments: numberString)
If unit placement is locale-dependent (you will have to this find out yourself for the target locales of your app), you have to deal with this manually as well. You can leverage the localization system here by providing localizations with an adequately positioned placeholder for the numeric value, e.g. %# fps for English and x %# yz for... well, Fantasy Language.

NumberFormatter turn percent value to double get nil

I want to turn String precent value into Double by:
let formatter = NumberFormatter()
formatter.numberStyle = .percent
let a = formatter.number(from: "12.5%")
print(a)
It works well in playground. But when test in project under "romana"(Română) language, it prints nil. If i change 12.5% to int type percentage value like 56% it works no problem.
Any idea?
Attached the language setting page.
If i change 12.5% to int type percentage value like 56% it works no problem.
This usually means that the locale settings use a different character for decimal separator, i.e. comma , instead of dot .
Switching to 12,5% should fix this problem.
I got all the source number with . separator
If your numbers are hard-coded in the source, you should not rely on user-selected locale. Use system locale for dealing with hard-coded inputs:
formatter.locale = Locale.system
[Locale.system is] The generic locale that contains fixed “backstop” settings that provide values for otherwise undefined keys. Use the system locale when you don’t want any localizations.
Try this
let formatter = NumberFormatter()
formatter.numberStyle = .percent
formatter.locale = Locale(identifier: "EN")
let a = formatter.number(from: "12.5%")

Migrating ObjC NSNumber code to Swift and having problems with displaying numbers.

I am migrating my ObjC app to Swift and have run into a peculiar issue.
My app reads a JSON file and builds a model from it. The numbers read from JSON used to be assigned to NSNumber. Some numbers were whole (eg.60) other had a decimal component (e.g. 60.23).
When I displayed these numbers in my app, 60 would be 60 (not 60.0) and decimals would display appropriately.
Now I am writing Swift code and assigning the numbers read from JSON to Double. Even if I use let x=String(myDouble) the number 60 will always come out 60.0
I don't want this...but I can't just format it %f because I never know if the number will be whole or have a decimal component.
Do I really need to check if it is a whole and then format it using it %f?
Am I missing something in Swift? The ObjC code behaved as I wanted by now Swift seems to be giving me decimals for whole numbers.
You could use NSNumberFormatter.
let number1 : Double = 60
let number2 = 60.23
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .DecimalStyle
let number1String = numberFormatter.stringFromNumber(number1) // "60"
let number2String = numberFormatter.stringFromNumber(number2) // "60.23"
FYI, here is the code in PlayGround:
let number = 60.23
String(number)
let double:Double = 60
//First way
String(NSNumberFormatter().stringFromNumber(double)!)
//Second way
let doubleString = String(double)
if doubleString.containsString(".0") {
let length = doubleString.characters.count
let index = doubleString.characters.indexOf(".")
doubleString.substringToIndex(index!)
}

What am I doing wrong with allowsFractionalUnits on NSDateComponentsFormatter?

Basically what I want is to get the value of a time interval represented in hours only, without rounding it to full hours (using NSDateComponentsFormatter to get it properly formatted and localized). I don't know if I misunderstand the use of NSDateComponentsFormatter.allowsFractionalUnits, but I can't get the formatter to give me a decimal value. Can anyone help me spot my error or tell me in what way I misunderstand this?
From Apple docs about allowsFractionalUnits property:
Fractional units may be used when a value cannot be exactly
represented using the available units. For example, if minutes are not
allowed, the value “1h 30m” could be formatted as “1.5h”.
Swift example code:
let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = .Abbreviated
formatter.allowedUnits = .Hour
formatter.allowsFractionalUnits = true
let onePointFiveHoursInSeconds = NSTimeInterval(1.5 * 60.0 * 60.0)
print(formatter.stringFromTimeInterval(onePointFiveHoursInSeconds)!)
//"1h" instead of expected "1.5h"
Same example in Objective-C code:
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
formatter.allowedUnits = NSCalendarUnitHour;
formatter.allowsFractionalUnits = YES;
NSTimeInterval onePointFiveHoursInSeconds = 1.5 * 60.0 * 60.0;
NSLog(#"%#", [formatter stringFromTimeInterval:onePointFiveHoursInSeconds]);
//"1h" instead of expected "1.5h"
Update:
I have reported a bug to Apple about this problem (rdar://22660145).
According to Open Radar #32024200:
After doing some digging (disassembling Foundation), it looks like every call to -[_unitFormatter stringFromNumber:] in -[NSDateComponentsFormatter _stringFromDateComponents:] is passed an +[NSNumber numberWithInteger:] which drops floating point data.
You're not doing anything wrong. The flag is simply broken.
Looking at the documentation, it’s using a lot of interesting language (emphasis mine):
Fractional units may be used when a value cannot be exactly represented using the available units. For example, if minutes are not allowed, the value “1h 30m” could be formatted as “1.5h”.
While to me it seems that it would only make sense for the values in the documentation to be values that actually work, it’s certainly possible that there is some combination of time value, formatter options, and calendar/locale settings that would make this work. Definitely worth filing a Radar on both the functionality and the documentation.
enter code hereAs far as i understood , You want to display time in 12-Hour formate Right ?
Below is the code :
Swift->
let dateAsString = "20:30"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm"
let date = dateFormatter.dateFromString(dateAsString)
dateFormatter.dateFormat = "h:mm a"
let date12 = dateFormatter.stringFromDate(date!)
txtText.text = date12

How do I know which is the default measure system (imperial or metric) on iOS?

How do I know which is the default measure system (imperial or metric) on iOS ?
How do I get this preference from the device settings, so I know what to display in my app ?
thanks
The NSLocale can tell you:
NSLocale *locale = [NSLocale currentLocale];
BOOL isMetric = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue];
Only three countries do not use the metric system: the US, Liberia and Myanmar. The later uses its own system, the former two use Imperial Units.
Apples documentation says (emphasis mine):
NSLocaleUsesMetricSystem
The key for the flag that indicates whether the locale uses the metric system.
The corresponding value is a Boolean NSNumber object. If the value is NO, you can typically assume American measurement units (for example, the statute mile).
Available in iOS 2.0 and later.
#DarkDust answer for swift3
//User region setting return
let locale = Locale.current //NSLocale.current
//Returns true if the locale uses the metric system
let isMetric = locale.usesMetricSystem
here's a swift version
var locale = NSLocale.currentLocale()
let isMetric = locale.objectForKey(NSLocaleUsesMetricSystem) as! Bool
For swift 3
let locale = NSLocale.current
let isMetric = locale.usesMetricSystem
As others mentioned before, the UK uses a mix of metric and imperial units.
I would recommend using the new MeassurementFormatter introduced in iOS 10 which handles most of these discrepancies:
import Foundation
let locale = Locale(identifier: "EN_UK")
locale.usesMetricSystem // true!
var formatter = MeasurementFormatter()
formatter.locale = locale
formatter.string(from: Measurement(value: 1000, unit: UnitLength.meters)) // 0.621 mi
To render a distance as a string in local, natural unit, use:
let distanceInMeters: CLLocationDistance = 1000
let formatter = MeasurementFormatter()
formatter.string(from: Measurement(value: distanceInMeters, unit: UnitLength.meters)) // 0.621 mi
Official documentation: https://developer.apple.com/documentation/foundation/measurementformatter
You should probably just have a setting in your app and let your users choose -- this is what Apple does in the Weather app.
If you want to choose a sensible default you could look at the locale. If it's US, pick imperial otherwise choose metric. It is a heuristic, it will be wrong sometimes, but it's just a default that can be changed.

Resources