How to get an NSNumber from a String of decimal? - swift

I'm not sure what I'm doing wrong, but NumberFormatter fails to get a number from a simple decimal String when using the number(from:) method.
Here's an example:
let decimalString: String = "12.5"
let formatter = NumberFormatter()
let number = formatter.number(from: decimalString)
print(number) // nil
Please keep in mind that I'm using NumberFormatter to handle decimals from any locale, so passing "١٢٫٥" should also lead to a non-nil return value from number(from:).
What is the correct and locale-aware way of converting any string from a UITextField that is entered using keyboard type .decimalPad to a valid NSNumber and get the doubleValue from it? (shortcuts like using the Double initializer or the .asciiCapableNumberPad keyboard type are not ideal)

Related

Convert string number with exponent to double value in swift

How would I convert a string value with a exponent to a double value that I can use in a calculation
Example:
var newString = "2.9747E+03"
I want to turn that string into a number I can use in formulas.
One approach: Use a NumberFormatter object.
Those objects have a method number(from:) that let you convert a String to an NSNumber.
The existing style .scientific might meet your needs, or you might need to create a custom format string.
This code works:
var numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .scientific
if let value = numberFormatter.number(from:"2.9747E+03")
{
let dVal = Double(truncating: value)
print(dVal)
}
And outputs
2974.7
As pointed out in a now-deleted answer by Dwendel, you could also use the NSDecimalNumber initializer that takes a string, and you could then convert that to a Double.
That code would look like this:
let theNumber = "-6.11104586446241e-01"
let decimalValue = NSDecimalNumber(string: theNumber)
let double = Double(truncating:decimalValue)
print(double)
The string-based initializer for NSDecimalNumber would let you convert number strings in scientific notation with less overhead, both in terms of code and memory (supposedly creating a number-formatter is fairly costly.) A StringFormatter is more flexible, though, and you could adjust the format it uses if your number strings won't convert directly to a DecimalNumber.

Swift 4: Could not cast value of type '__NSCFNumber' to 'NSString'

I have tried
self.adc_role_id = String(res["adc_role_id"])
self.adc_role_id = "\(res["adc_role_id']"
self.adc_role_id = (\(res["adc_role_id"] as? String)!
but still get
Could not cast value of type '__NSCFNumber' to 'NSString'
I added the dump of res[4] below
As new as I am to Swift, I don't know anything else to try
In Swift 4, the String initializer requires the describing: argument label.
I don't know if this will solve your problem, but your first line of code should be written:
self.adc_role_id = String(describing: res["adc_role_id"])
In your screenshot we can see that res["adc_role_id"] is an NSNumber.
To transform an NSNumber to a String you should use its stringValue property.
And since a dictionary gives an Optional, you should use optional binding to safely unwrap it.
Example:
if let val = res["adc_role_id"] {
self.adc_role_id = val.stringValue
}
You could also, if you want, use string interpolation instead of the property:
if let val = res["adc_role_id"] {
self.adc_role_id = "\(val)"
}
but I think using the property is more relevant.
If for some reason the compiler complains about the type of the content, cast it:
if let val = res["adc_role_id"] as? NSNumber {
self.adc_role_id = val.stringValue
}
Note that you should not use String(describing:) because this initializer will try to represent the string in many ways, and some of them will give inaccurate and unexpected results (for example, if String(describing:) resolves to use the debugDescription property, as explained in the documentation, you may get a totally different string than the one you want).
It's also worth noting that using String(describing:) with an optional value such as your dictionary will resolve to a wrong string: String(describing: res["adc_role_id"]) will give Optional(yourNumber)! This is why Mike's answer is wrong. Be careful about this. My advice is to avoid using String(describing:) altogether unless for debugging purposes.
The error message is clear and the dump is clear, too.
The value is not a String, it's an Int(64) wrapped in NSNumber
Optional bind the value directly to Int (NSNumber is implicit bridged to Int) and use the String initializer.
if let roleID = res["adc_role_id"] as? Int {
self.adc_role_id = String(roleID)
}
Please conform to the naming convention that variable names are camelCased rather than snake_cased

How to fix the warning of type casting in 'if let' statement in Xcode 8.3?

Consider the following code:
let nsdate: NSDate? = NSDate()
if let date = nsdate as? Date { // a warning occurs here
print(date)
}
The compiler complains like this: Conditional downcast from 'NSDate?' to 'Date' is a bridging conversion; did you mean to use 'as'?
A cast from NSData to Data has the same problem. How to fix the it?
Try to cast to an optional Date:
if let date = nsdate as Date?
You're trying to optional cast of optional NSDate to NON optional Date. As long as NSDate is bridged from obj-c to Date, so this cast always success, so no optional cast required here, just basic cast as is enough. Then you need to cast optional value, so the resulting value has to be optional too, therefore Date? is appropriate here.
Swift 3.1 distinguishes
An optional down cast as? Foo
It casts a more unspecific to a more specific type for example
let dict : [String:Any] = ["Foo" : 12]
let number = dict["Foo"] as? Int
A bridge cast of an optional type as Foo?
It bridges a (Core)Foundation type to a toll free Swift type and vice versa.
It's the optional equivalent of the usual non-optional syntax
let string : NSString = "Foo"
let swiftString = string as String
The difference is subtle for the developer but very useful for the compiler.
Basically don't use the NS... Foundation classes in Swift 3 if there is a native Swift counterpart.
Try this:
let nsdate: NSDate? = NSDate()
if let date = nsdate {
print(date)
}
The compiler knows it´s an NSDate if unwrapped, so what you are doing is actually casting an NSDate to a Date

NSNull into a Struct with a property of type NSDate

I have an object from the server that is recognized by Swift 2.1 as either NSDate or NSNull. I want to put it into a struct with a property of type NSDate.
Is that possible? If not, how should I handle this to be type safe later when I use it?
struct Data {
var completedAt: [NSDate]
var name: [String]
var gender: [Bool]
}
but sometimes completedAt comes from the server as NSNull:
completedAt = "<null>";
Any help is very much appreciated, thank you.
Based on my interpretation of the text in the question you didn't mean to declare the variables as arrays.
This is how I handle my parson and I think it works pretty neatly.
The date formatter should probable not be initiated in every iteration of the constructor. If you won't use the date regularly you might want to keep the detesting until you need to parse the date or you can have a static date formatter utility that you only instantiate once.
struct Data {
var completedAt: NSDate?
var name: String
var gender: Bool
init?(dictionary: [String:AnyObject]) {
//Guessing that you want some of the values non optional...
guard let name = dictionary["name"] as? String,
let gender = dictionary["gender"] as? String
else {
return nil
}
self.name = name
self.gender = gender
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
//safe handle of optional values
if let completedAtString = dictionary["completedAt"] as? String, completedAt = dateFormater.dateFromString(completedAtString) {
self.completedAt = completedAt
}
}
}
Take a step back. For each item that the server might provide, there is no guarantee whatsoever that you receive what you expect, since you cannot control the server. So you need to decide how to react to which input.
In the case of expecting a date for example (if your data comes in JSON, that means you likely expect a string formatted in a certain way), the actual data that you receive might be an array, dictionary, string, number, bool, null, or nothing. You might then for example decide that you want to interpret nothing or null or an empty string as nil, that you want to interpret a string containing a well-formatted date as an NSDate, and anything else a fatal error in a debug version, and as either nothing or a fatal error in a release version. On the other hand, if an NSDate is absolutely required then you might interpret anything that doesn't give an NSDate as an error.
Then you write a function that delivers exactly what you want and use it. That way you can parse complex data, with your code warning you when something isn't as it should be, and with your code either surviving any possible input, or deliberately crashing on wrong input, as you want it.

Function returning Optional("String")

I have this function that accepts an Double value, convert it to a currency format and return a String formatted like R$:1.200,30.
func convert_Value(valor: Double) ->String {
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.locale = NSLocale(localeIdentifier: "pt_BR")
return ("\(formatter.stringFromNumber(valor))")
}
This function doesn't have any Optional variable declared, but when i call it using:
x = convert_Value(1200.30)
it returns:
Optional("R$1.200,30")
I can't figure out what i need to do, as its not an optional i can't use exclamation marks to unwrap the optional.
I tried to turn the Double and String parameter in function as Optional and then unwrap, but the Optional stills showing.
It doesn't return Optional("R$1.200,30"), it returns "Optional("R$1.200,30")". There's a subtle difference there; notice the ". What's happening is formatter.stringFromNumber(valor) returns String?, which you're putting in a String using "\(...)". Instead, you should return formatter.stringFromNumber(valor)!, force unwrapping here is okay because you know the input is a number.