How to use numberFromString when UITextField has currency value in Swift - ios

I have an 'amount' input field that takes a number value that I wish to display as a currency within the input field itself. This I seem to be able to do with no issues.
I then use this value in a calculation and output as a currency value. Again this works fine the first time as it initially sees it as a double and not currency.
My problem comes when I try and reuse the value in the 'amount' input field, as the value is being seen and no longer a double because of the currency symbol etc.
Any suggestions?
Edited below based on suggestions:
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
var numberOfPlaces = 2.0
var multiplier = pow(10, numberOfPlaces)
var enteredAmount = formatter.numberFromString(finalAmount.text)?.doubleValue ?? 0.0
enteredAmountLabel.text = formatter.stringFromNumber(enteredAmount)
finalAmount.text = formatter.stringFromNumber(enteredAmount)
Using these amends the calculations work the first time anything is entered into the field. If you try a calculation when the field is populated it resets the field to $0.00
If I amend to:
formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
Everything works as expected but the amount input field no longer displays as currency.
I am trying to provide screenshots but I do not have enough reputation points.

Is this the line that gives you trouble?
var enteredAmount = NSNumberFormatter().numberFromString(finalAmount.text)!.doubleValue
It looks like you've got the right idea. You can use a number formatter to convert a value to a formatted string for display, or to convert a formatted string to a value.
I would expect that code to work.
Is that line giving you a compile error? An error at runtime? Not giving the value that it should?
EDIT:
#LeonardoSavioDabus pointed out the problem. That line should use the number formatter you created above:
var enteredAmount = formatter.numberFromString(finalAmount.text)!.doubleValue
And you really shouldn't use force unwrapping like that. You should use the nil coalescing operator:
var enteredAmount = formatter.numberFromString(finalAmount.text)?.doubleValue ?? 0.0
EDIT #2:
Here in the US, this code works perfectly in a playground:
import Foundation
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
let moneyString = "$12.93"
var enteredAmount = formatter.numberFromString(moneyString)?.doubleValue ?? 0.0
enteredAmount += 0.07
var newString = formatter.stringFromNumber(enteredAmount)
(I stripped out all the locale stuff to make it simpler.)
EDIT #3:
Your locale code doesn't make sense to me.
for identifier in ["en_US" , "en_UK" , "en_US","fr_FR"] {
formatter.locale = NSLocale(localeIdentifier: identifier)
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
}//end of locale detection for currency and decimal formatting
That code looks like it loops through 3 different values of identifier and assigns 3 different locales to your formatter, and the same max/min digit counts (both 2). The way I read that code, it's going to assign first the US locale, then the UK local, then US English again, and then finally French from France. So at the end of the loop the locale will be set to "fr_FR". That's probably not what you want.

Related

substring of the first 4 characters from a textField in Swift 4

I'm trying to create a substring of the first 4 characters entered in a textField in Swift 4 on my iOS app.
Since the change to Swift 4 I'm struggling with basic String parsing.
So based on Apple documentation I'm assuming I need to use the substring.index function and I understand the second parameter (offsetBy) is the number of characters to create a substring with. I'm just unsure how I tell Swift to start at the beginning of the string.
This is the code so far:
let postcode = textFieldPostcode.text
let newPostcode = postcode?.index(STARTATTHEBEGININGOFTHESTRING, offsetBy: 4)
I hope my explanation makes sense, happy to answer any questions on this.
Thanks,
In Swift 4 you can use
let string = "Hello World"
let first4 = string.prefix(4) // Hell
The type of the result is a new type Substring which behaves very similar to String. However if first4 is supposed to leave the current scope – for example as a return value of a function – it's recommended to create a String explicitly:
let first4 = String(string.prefix(4)) // Hell
See also SE 0163 String Revision 1
In Swift 4:
let postcode = textFieldPostcode.text!
let index = postcode.index(postcode.startIndex, offsetBy: 4)
let newPostCode = String(postcode[..<index])

iOS Charts custom labels: integer values on Bubble Chart

I want to show the values displayed inside the bubbles as whole number Ints. For example: instead of "22.0" I just want "22".
The answer here doesn't work with the new iOS Charts because it requires an IValueFormatter instead of a NumberFormatter:
let numberFormatter = NumberFormatter()
numberFormatter.generatesDecimalNumbers = false
chartData.setValueFormatter(numberFormatter)
Error Message:
Cannot convert value of type 'NumberFormatter' to expected argument type 'IValueFormatter?'
Is there any way to solve this problem?
With the new charts, you still use numberformatter and then just convert at the end. Reference the code below.
let format = NumberFormatter()
format.generatesDecimalNumbers = false
let formatter = DefaultValueFormatter(formatter: format)
chartData.leftAxis.valueFormatter = (formatter as? IAxisValueFormatter)
Hope this helps!

Swift convert object that is NSNumber to Double

I have this code in Swift and it works, but I would think there is a better way to get my object from NSNumber and convert it to a Double:
var rating: NSNumber
var ratingDouble: Double
rating = self.prodResult?.prodsInfo.prodList[indexPath.row].avgRating as NSNumber!!
ratingDouble = Double(rating.doubleValue)
Update
Swift's behavior here has changed quite a bit since 1.0. Not that it was that easy before, but Swift has made it harder to convert between number types because it wants you to be explicit about what to do with precision loss. Your new choices now look like this:
var rating: NSNumber
var ratingDouble: Double
ratingDouble = rating as! Double // 1
ratingDouble = Double(exactly: rating)! // 2
ratingDouble = Double(truncating: rating) // 3
ratingDouble = rating.doubleValue // 4
if let x = rating as? Double { // 5
ratingDouble = x
}
if let x = Double(exactly: rating) { // 6
ratingDouble = x
}
This calls Double._forceBridgeFromObjectiveC which calls Double(exactly:) with Double, Int64, or UInt64 based on the stored type in rating. It will fail and crash the app if the number isn't exactly representable as a Double. E.g. UInt64.max has more digits than Double can store, so it'll crash.
This is exactly the same as 1 except that it may also crash on NaN since that check isn't included.
This function always returns a Double but will lose precision in cases where 1 and 2 would crash. This literally just calls doubleValue when passing in an NSNumber.
Same as 3.
This is like 1 except that instead of crashing the app, it'll return nil and the inside of the statement won't be evaluated.
Same as 5, but like 2 will return nil if the value is NaN.
If you know your data source is dealing in doubles, 1-4 will probably all serve you about the same. 3 and 4 would be my first choices though.
Old Answer for Swift 1 and 2
There are several things you can do:
var rating: NSNumber
var ratingDouble: Double
ratingDouble = rating as Double // 1
ratingDouble = Double(rating) // 2
ratingDouble = rating.doubleValue // 3
The first item takes advantage of Objective-Cbridging which allows AnyObject and NSNumber to be cast as Double|Float|Int|UInt|Bool.
The second item presumably goes through a constructor with the signature init(_ number: NSNumber). I couldn't find it in the module or docs but passing AnyObject in generated an error that it cannot be implicitly downcast to NSNumber so it must be there and not just bridging.
The third item doesn't employ language features in the same way. It just takes advantage of the fact that doubleValue returns a Double.
One benefit of 1 is that it also works for AnyObject so your code could be:
let ratingDouble = self.prodResult!.prodsInfo.prodList[indexPath.row].avgRating! as Double
Note that I removed the ? from your function and moved the ! in. Whenever you use ! you are eschewing the safety of ? so there's no reason to do both together.

Getting the First Letter of a String in Hebrew

In a UITableView, I'm listing a bunch of languages to be selected. And to put a section index view to the right like in Contacts app, I'm getting all first letters of languages in the list and then use it to generate the section index view.
It works almost perfect, Just I encountered with a problem in getting first letter of some strings in Hebrew. Here a screenshot from playground, one of the language name that I couldn't get the first letter:
Problem is, the first letter of the name of the language that has "ina" language code, isn't "א", it's an empty character; it's not a space, it's just an empty character. As you can see, it's actually 12 characters in total, but when I get count of it, it says 13 characters because there is an non-space empty character in index 0.
It works perfectly if I use "eng" or "ara" languages with putting these values in value: parameter. So maybe the problem is cause of system that returns a language name with an empty character in some cases, I don't know.
I tried some different methods of getting first letter, but any of it didn't work.
Here "א" isn't the first letter, it's the second letter. So I thought maybe I can find a simple hack with that, but I want to try solving it before trying workarounds.
Here is the code:
let locale = NSLocale(localeIdentifier: "he")
let languageName = locale.displayNameForKey(NSLocaleIdentifier, value: "ina")!
let firstLetter = first(languageName)!
println(countElements(languageName))
for character in languageName {
println(character)
}
You could use an NSCharacterSet.controlCharacterSet() to test each character. I can't figure out how to stay in Swift-native strings, but here's a function that uses NSString to return the first non-control character:
func firstNonControlCharacter(str: NSString) -> String? {
let controlChars = NSCharacterSet.controlCharacterSet()
for i in 0..<str.length {
if !controlChars.characterIsMember(str.characterAtIndex(i)) {
return str.substringWithRange(NSRange(location: i, length: 1))
}
}
return nil
}
let locale = NSLocale(localeIdentifier: "he")
let languageName = locale.displayNameForKey(NSLocaleIdentifier, value: "ina")!
let firstChar = firstNonControlCharacter(languageName) // Optional("א")

Decimal point in calculations as . or ,

If I use decimal pad for input of numbers the decimal changes depending of country and region format.
May be as a point "." or as a comma ","
And I do not have control over at which device the app is used.
If the region format uses a comma the calculation gets wrong. Putting in 5,6 is the the same as putting in only 5 some times and as 56 same times.
And that is even if I programmatically allow both . and , as input in a TextField.
How do I come around this without using the numbers an punctation pad and probably also have to give instructions to avoid input with comma ","
It is only input for numbers and decimal I need and the decimal pad is so much nicer.
You shoudld use a NSNumberFormatter for this, as this can be set to handle different locales.
Create a formatter:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setLocale:[NSLocale currentLocale]];
Use it:
NSNumber *number = [numberFormatter numberFromString: string]; //string is the textfield.text
if the device's locale is set to a locale, where the decimal separator in a ,, the Number Keypad will use is and the formatter as-well. On those the grouping separator will be .
For the other locales it will be vice-versa.
NSNumberFormatter is very sophisticated, you should read its section in Data Formatter Guide, too. It also knows a lot of currency handling (displaying, not conversion), if your app does handle such.
You can use also the class method of NSNumberFormatter
NSString* formattedString = [NSNumberFormatter
localizedStringFromNumber:number
numberStyle:NSNumberFormatterCurrencyStyle];
Identify is local country uses comma for decimal point
var isUsesCommaForDecimal : Bool {
let nf = NumberFormatter()
nf.locale = Locale.current
let numberLocalized = nf.number(from: "23,34")
if numberLocalized != nil {
return true
} else {
return false
}
}
One way could be to check if the textField contains a ",".
If it contains, replace it with "." and do all arithmetic operations.
As anyways you will be reading all textFields and textViews as NSString object, you can manipulate the input value and transform it according to your need.
Also while showing the result replace "." with "," so that user feel comfortable according to there regional formats.

Resources