I have a simple question, I have this dictionary:
let dict: [String: Any] = ["Area": "100", "YessorNo" : true]
And, for the key area, I want to cast it's value to a double, like so:
let a = dict["Area"] as? Double
When I print a, I get a nil, why? the value 100 is although a string but it is a number isn't? why can't I cast it to a double?
You can't directly cast a String to a Double, you need to use the proper initializer.
guard let numStr = dict["Area"] as? String else {
return
}
let a = Double(numStr)
First, cast from Any to String, and then use an initializer to convert it to Double.
if let a = dict["Area"] as? String, let aDouble = Double(a) {
print(aDouble)
}
Since the other answers provided the code, I will focus on the why of the question. Type casting won't work because, according to the Swift docs, type casting allows you to "treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy." Since a double is not a subclass or a superclass of a string, you can't cast a string to a double even if that string is a string of a number. This is why trying to cast a string to a double returns nil.
Related
So is there another way of conversion without it being explicit?
Lets say, this is the string
var stringValue = "123"
You can convert it into "INT" two ways
first is
let intNumber : Int = Int(stringValue)!
second is
let anotherIntNumber : Int = (stringValue as NSString).integerValue // go with first 1 as this is more of objective-c
If you confirm String to ExpressibleByIntegerLiteral you can just write this:
let myVar: String = 1
print(myVar)
Just note that you need to explicitly provide a type for myVar as compiler doesnt know which type it shouldd choose from.
How do we conform String to ExpressibleByIntegerLiteral? Something like this:
extension String : ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = String(value)
}
}
for the other way round you can look at ExpressibleByStringLiteral
I'd like to implement automatic type conversions between known types in Swift. The C# way of doing it has been overloading type-casting operators. If I want my X type to be cross-assignable with, say, string, I would write:
public class X
{
public static implicit operator string(X value)
{
return value.ToString();
}
public static implicit operator X(string value)
{
return new X(value);
}
}
After that I could write stuff like:
string s = new X();
X myObj = s;
and they would be automatically converted. Is that possible in any way in Swift?
No, the language doesn't provide such functionality for custom types. There is bridging between Objective-C collections and Swift collections but that's baked in and not customizable.
// Swift array of `String` elements
let swiftArray: [String] = ["Bob", "John"]
// Obj-C array of `NSString` elements, but element type information
// is not known to the compiler, so it behaves like an opaque NSArray
let nsArray: NSArray = ["Kate", "Betty"]
// Obj-C array of an `NSString` and an `NSNumber`, element type
// information is not known to the compiler
let heterogeneousNSArray: NSArray = ["World", 3]
// Casting with `as` is enough since we're going from Swift array to NSArray
let castedNSArray: NSArray = swiftArray as NSArray
// Force casting with `as!` is required as element type information
// of Obj-C array can not be known at compile time
let castedSwiftArray: [String] = nsArray as! [String]
// Obj-C arrays can not contain primitive data types and can only
// contain objects, so we can cast with `as` without requiring a
// force-cast with `!` if we want to cast to [AnyObject]
let heterogeneousCastedNSArray: [AnyObject] = heterogeneousNSArray as [AnyObject]
Documentation for type casting is available here.
I think you can achieve what you want to do with initializers.
extension X {
init(string: String) {
self = X(string)
}
}
extension String {
init(x: X) {
// toString is implemented elsewhere
self = x.toString
}
}
let x = X()
let string = "Bobby"
let xFromString: X = X(string: string)
let stringFromX: String = String(x: x)
Not directly related to your question but there is also a family of protocols that start with ExpressibleBy..., enabling you to do things like the following:
Let's say we want to initialize strings from integer literals. We can do that by conforming to and implementing ExpressibleByIntegerLiteral
// Strings can not be initialized directly from integer literals
let s1: String = 3 // Error: Can not convert value of type 'Int' to specified type 'String'
// Conform to `ExpressibleByIntegerLiteral` and implement it
extension String: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
// String has an initializer that takes an Int, we can use that to
// create a string
self = String(value)
}
}
// No error, s2 is the string "4"
let s2: String = 4
A nice use case for ExpressibleByStringLiteral can be found here.
How to cast an Any object to a Double. I was able to cast it to a string but I can't find anywhere to cast to a Double. Either read in zip code as a Double or cast zip code after to a Double.
let zipCode = ((o as AnyObject).fieldValue("Zip__c"))!
//String cast that works
lender.First_Name__c = String(describing: zipCode)
The short answer is that you can use as to cast, though you can do this in a few ways. The safe, Swift way of doing it is with optional binding, like this:
if let doubleValue = o as? Double {
print(doubleValue)
}
If you can guarantee it's a Double you can force unwrap it like so:
let doubleValue = o as! Double
Cannot understand how to treat the types of generics and how to use the arguments with the undefined types with libraries:
func cellWith<T>(value: T) -> String { // I expect Int, Double or String
// type value as argument
let fm = NSNumberFormatter() // Double type argument will be
fm.numberStyle = .CurrencyStyle // processed like "currency"
if value.self is Double { // In case value have type "Double",
// like 20_000.00
return(fm.stringFromNumber(value)) // I expect return $20,000.00
^~~~~
// ERROR: Cannot convert value of type 'T' to expected argument type 'NSNumber'
} else {
return("bla-bla-bla")
}
}
Why do you have to use generics in this case? Use generics only when you have to conserve type information.
Use generics only when :
"you are going to give you a type later and I want you to enforce that
type everywhere I specify."
Use Any only when you want to tell the compiler:
"Don't worry about this variable no need to enforce any type here let
me do whatever I want to."
With that said, your use case fits the second one. You do not want to mess with generics here. Your solution should go:
func cellWith(value: Any) -> String {
let fm = NSNumberFormatter()
fm.numberStyle = .CurrencyStyle
if let doubleValue = value as? Double {
return(fm.stringFromNumber(doubleValue))!
} else {
return("bla-bla-bla")
}
}
There is this function in Alamofire
func escape(string: String) -> String {
let legalURLCharactersToBeEscaped: CFStringRef = ":/?&=;+!##$()',*"
return CFURLCreateStringByAddingPercentEscapes(nil, string, nil, legalURLCharactersToBeEscaped, CFStringBuiltInEncodings.UTF8.rawValue)
}
Complier show "CFString! is not convertible to String" error in this func. I have tried Convert CFString to NSString - Swift to cast this , but have no luck.
From the Xcode 6.3 Release Notes:
The implicit conversions from bridged Objective-C classes (NSString/NSArray/NSDictionary) to their corresponding Swift value types (String/Array/Dictionary) have been removed, making the Swift type system simpler and more predictable.
...
In order to perform such a bridging conversion, make the conversion explicit with the as keyword.
You have to convert CFString/NSString to a Swift String explicitly:
func escape(string: String) -> String {
let legalURLCharactersToBeEscaped: CFStringRef = ":/?&=;+!##$()',*"
return CFURLCreateStringByAddingPercentEscapes(nil, string,
nil, legalURLCharactersToBeEscaped,
CFStringBuiltInEncodings.UTF8.rawValue) as String
// HERE ---^
}
The conversion in the other direction (Swift String to NSString) is still done automatically, that's why the the string parameter of your
function can be passed directly to the
CFURLCreateStringByAddingPercentEscapes() function which expects
a CFString argument.