I have the following dictionary of type [String: Any] (this is what the log looks like):
["name": Cesare de Cal, "last_name": de Cal, "email": hi#cesare.io, "id": 1012058902268810, "first_name": Cesare]
I want to get the profile ID "id":
if let fbID = fbValues["id"] as? Int {
print("here should be the fb Id", fbID)
} else {
print("cast failed") // cast failed
}
but this cast fails. Why? I'm assuming "id" (1012058902268810) is a number, right?
rob already provided you with a possible solution. Just to answer your question about why the cast fails, it fails because of how type casting works in Swift:
From the documentation:
Type casting is a way to check the type of an instance, or to treat
that instance as a different superclass or subclass from somewhere
else in its own class hierarchy. (...)
A constant or variable of a certain class type may actually
refer to an instance of a subclass behind the scenes. Where you
believe this is the case, you can try to downcast to the subclass type
with a type cast operator (as? or as!). (...)
Casting does not actually modify the instance or change its values. The underlying instance remains the same; it is simply treated and accessed as an instance of the type to which it has been cast.
This will work:
let something: Any = 1
let number = something as! Int
This won't work:
let something: Any = "1"
let number = something as! Int
This won't work either because Int has no initializer for type Any:
let something: Any = "1"
let number = Int(something)
But this will work - first you cast to String and then you coerce to Int (and Int has an initializer that accepts String)
let something: Any = "1"
let string = something as! String
let number = Int(string)!
Edit to answer Cesare: You're right. I edited my answer to just provide more info about type casting since you already had your problem solved ;)
And these were just some examples for getting the cast/coercion point across. In a real scenario you shouldn't be forcing unwrapping any of this as John Montgomery pointed out.
If you don't know whether the id value is coming in as a String or an Int, you could try handling both:
switch fbValues["id"] {
case nil:
print("no id given")
case let string as String:
if let id = Int(string) { print("id = \(id)") }
else { print("non-numeric id \(string)") }
case let id as Int:
print("id = \(id)")
default:
print("what is this I don't even")
}
Related
I like using guard and came across the situation where I want to use where for a typecheck as well:
guard let status = dictionary.objectForKey("status") as! String! where status is String else { ...}
xCode complains correctly that it's always true.
My goal is to have an unwrapped String after the guard in one line.
How can I do this?
Probably you want this?
guard let status = dictionary["status"] as? String else {
// status does not exist or is not a String
}
// status is a non-optional String
When you use as! String! you tell Swift that you know that the object inside your dictionary must be a String. If at runtime the object is not a String, you let Swift throw a cast exception. That is why the where part of your check is not going to fail: either the status is going to be a String, or you would hit an exception prior to the where clause.
You can do an optional cast instead by using as? operator instead of as!. Coupled with guard let, this approach produces
guard let status = dictionary.objectForKey("status") as? String else { ... }
... // If you reached this point, status is of type String, not String?
I would like to check the type of a dictionary value, and I use the following test code:
let swiftDict = [1: "one", 2: "two"]
if swiftDict[1] is NSString {
println("it's an nsstring")
}
I got Compiler warning: "Cast from 'String?' to unrelated NSString always fails.
Modified the code to the following:
if let str = swiftDict[1] as? NSString? {
println("it's an nsstring")
}
Now I get Compiler warning: Conditional cast from String? to NSString? always succeeds.
How do I fix the above warning? What's the right way to check for a variable type?
The reason for the check is to find out whether String or NSString is stored.
If you have a dictionary with mixed types (i.e. value:AnyObject) you could iterate through them and use is to check what type a value in the dictionary is, e.g.
let swiftDict:[Int:AnyObject] = [1: "one", 2: "two", 3: 15, 4: true]
for (key, value) in swiftDict
{
if (value is String)
{
println("\(key): \(value) is String")
}
}
Why do you want to test your value type at all, as Swift is already type-safe?
Contrary to Objective-C, arrays and dictionaries in Swift are typed. So your swiftDict variable is of type Dictionary<Int, String> (or [Int:String] for short) and thus the values of this dictionary will always be Strings, by definition.
For example, if you had tried to write this:
let swiftDict = [1: "one", 2: "two", 3: 7]
// error: '_' is not convertible to 'StringLiteralConvertible'
Then the compiler would error, telling you that 7 is not of type String nor convertible to a String, so you can't add it to swiftDict, which is inferred to be of type [Int:String]
This means that, by definition, swiftDict[1] will always be a String (because swiftDict is a [Int:String])… or nil if there is no key 1 in that dict.
--> swiftDict[1] is guaranteed to be of type String?. So you don't even need to test the type cast. You just need to test if it's nil or not.
if let value = swiftDict[1] {
println("swiftDict has a string value of \(value) for key 1")
} else {
println("swiftDict does not have the key 1")
}
[EDIT] Also note that String and NSString are toll-free bridged, which means that they are totally interchangeable. So you don't even need to test or cast if your String is an NSString, as it will always be possible to cast from String to NSString and vice-versa (it will always be possible to interpret your string either as String or NSString).
guard could be used
guard let key = swiftDict[1] else {
println("swiftDict does not have the key 1")
return;
}
It wont matter if you cast it to string or NSString as its just a character sequence in the dictionary. Both will work. If it's a string in the dictionary, it's a String or an NSString, depending what you set it as.
if let str = swiftDict[1] as? String
{
println("it's a string")
}
how can i convert any object type to a string?
let single_result = results[i]
var result = ""
result = single_result.valueForKey("Level")
now i get the error: could not assign a value of type any object to a value of type string.
and if i cast it:
result = single_result.valueForKey("Level") as! String
i get the error:
Could not cast value of type '__NSCFNumber' (0x103215cf0) to 'NSString' (0x1036a68e0).
How can i solve this issue?
You can't cast any random value to a string. A force cast (as!) will fail if the object can't be cast to a string.
If you know it will always contain an NSNumber then you need to add code that converts the NSNumber to a string. This code should work:
if let result_number = single_result.valueForKey("Level") as? NSNumber
{
let result_string = "\(result_number)"
}
If the object returned for the "Level" key can be different object types then you'll need to write more flexible code to deal with those other possible types.
Swift arrays and dictionaries are normally typed, which makes this kind of thing cleaner.
I'd say that #AirSpeedVelocity's answer (European or African?) is the best. Use the built-in toString function. It sounds like it works on ANY Swift type.
EDIT:
In Swift 3, the answer appears to have changed. Now, you want to use the String initializer
init(describing:)
Or, to use the code from the question:
result = single_result.valueForKey("Level")
let resultString = String(describing: result)
Note that usually you don't want valueForKey. That is a KVO method that will only work on NSObjects. Assuming single_result is a Dictionary, you probably want this syntax instead:
result = single_result["Level"]
This is the documentation for the String initializer provided here.
let s = String(describing: <AnyObject>)
Nothing else is needed. This works for a diverse range of objects.
The toString function accepts any type and will always produce a string.
If it’s a Swift type that implements the Printable protocol, or has overridden NSObject’s description property, you’ll get whatever the .description property returns. In the case of NSNumber, you’ll get a string representation of the number.
If it hasn’t, you’ll get a fairly unhelpful string of the class name plus the memory address. But most standard classes, including NSNumber, will produce something sensible.
import Foundation
class X: NSObject {
override var description: String {
return "Blah"
}
}
let x: AnyObject = X()
toString(x) // return "Blah"
"\(x)" // does the same thing but IMO is less clear
struct S: Printable {
var description: String {
return "asdf"
}
}
// doesn't matter if it's an Any or AnyObject
let s: Any = S()
toString(s) // reuturns "asdf"
let n = NSNumber(double: 123.45)
toString(n) // returns "123.45"
n.stringValue // also works, but is specific to NSNumber
(p.s. always use toString rather than testing for Printable. For one thing, String doesn’t conform to Printable...)
toString() doesn't seem to exist in Swift 3 anymore.
Looks like there's a failable initializer that will return the passed in value's description.
init?(_ description: String)
Docs here https://developer.apple.com/reference/swift/string/1540435-init
for example
if let name = jsonDict["name"] as AnyObject? as? String {
println("name is \(name)")
} else {
println("property was nil")
}
I have the follow question:
jsonDict["name"] as AnyObject? as? String is the same as jsonDict["name"] as? AnyObject as? String yes or no?
jsonDict["name"] as AnyObject? as? String is the same as jsonDict["name"] as AnyObject? as String? yes or no?
I don't know the difference between as? String and as String?
jsonDict["name"] as AnyObject? as? String is the same as jsonDict["name"] as? AnyObject as? String yes or no? - No, the latter makes no sense, you are trying to making a double cast from AnyObject to String. Furthermore, jsonDict["name"] would be enough for the compiler to recognize what type the return is, you shouldn't need any casting to a string.
jsonDict["name"] as AnyObject? as? String is the same as jsonDict["name"] as AnyObject? as String? yes or no?. Same as the first case, making a double cast makes little sense. Furthermore, the difference between as? and as is that as? will only execute in the case that the object can be successfully converted to the desired type, if this is not the case, the object will not be casted, therefore avoiding crashes.
there is huge difference between as and as?.
as
the as downcasts the object and force-unwrap the result in every time, and it does not really care about the real type of the object or any fail-safe procedure. the responsibility is yours, therefore that can cause the following code crashes in runtime:
let jsonDict: Dictionary<String, AnyObject> = ["name": 32]
if let downcasted: String = jsonDict["name"] as String? { ... } else { ... }
the reason: it forcibly downcasts the Int32 value to an optional String (=String?), and that cause a simple crash – if force-unwrapping fails, it will cause crash in every occasion in runtime.
you can use this solution only when you are 100% certain the downcast won't fail.
therefore, if your dictionary looks like this:
let jsonDict: Dictionary<String, String?> = ["name": nil]
if let downcasted: String = jsonDict["name"] as String? { ... } else { ... }
the else-branch will be executed, because the String? is nil, when you replace the nil with any string (e.g. "My Name") in your dictionary, the if-branch will be executed in runtime as expected – if the value is any other type than optional String, a crash will happen as I highlighted above in every case when the value is not nil.
as?
the as? optionally downcast the object, and if the downcasting procedure fails then it returns nil. that solution helps you to check the success of the donwcasting in runtime without suffering any crash:
let jsonDict: Dictionary<String, AnyObject> = ["name": 32]
if let downcasted: String = jsonDict["name"] as? String { ... } else { ... }
in that situation the else-branch will executed and your application can carry on, because the actual Int32 value fails to be downcasted to String – if your value is a String (e.g. "My Name") then the if-branch will be executed in runtime.
you have to use this solution when you are not certain about which the actual object's type is in runtime and there is a chance the downcasting procedure could fail.
NOTE: I would recommend to read the official docs about type-casting and optional-chaining for better understanding the entire procedure.
I am trying with these lines of code
class Student {
var name: String
var age: Int?
init(name: String) {
self.name = name
}
func description() -> String {
return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
}
}
var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())
Above code produces as follow
Daniel hides his age.
Daniel is Optional(18) years old.
My question is why there is Optional (18) there, how can I remove the optional and just printing
Daniel is 18 years old.
You have to understand what an Optional really is. Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
Inside your description() function you don't print the Int, but instead you print the Optional. If you want to print the Int you have to unwrap the Optional. You can use "optional binding" to unwrap an Optional:
if let a = age {
// a is an Int
}
If you are sure that the Optional holds an object, you can use "forced unwrapping":
let a = age!
Or in your example, since you already have a test for nil in the description function, you can just change it to:
func description() -> String {
return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
To remove it, there are three methods you could employ.
If you are absolutely sure of the type, you can use an exclamation mark to force unwrap it, like this:
// Here is an optional variable:
var age: Int?
// Here is how you would force unwrap it:
var unwrappedAge = age!
If you do force unwrap an optional and it is equal to nil, you may encounter this crash error:
This is not necessarily safe, so here's a method that might prevent crashing in case you are not certain of the type and value:
Methods 2 and three safeguard against this problem.
The Implicitly Unwrapped Optional
if let unwrappedAge = age {
// continue in here
}
Note that the unwrapped type is now Int, rather than Int?.
The guard statement
guard let unwrappedAge = age else {
// continue in here
}
From here, you can go ahead and use the unwrapped variable. Make sure only to force unwrap (with an !), if you are sure of the type of the variable.
Good luck with your project!
For testing/debugging purposes I often want to output optionals as strings without always having to test for nil values, so I created a custom operator.
I improved things even further after reading this answer in another question.
fileprivate protocol _Optional {
func unwrappedString() -> String
}
extension Optional: _Optional {
fileprivate func unwrappedString() -> String {
switch self {
case .some(let wrapped as _Optional): return wrapped.unwrappedString()
case .some(let wrapped): return String(describing: wrapped)
case .none: return String(describing: self)
}
}
}
postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
return x.unwrappedString
}
Obviously the operator (and its attributes) can be tweaked to your liking, or you could make it a function instead. Anyway, this enables you to write simple code like this:
var d: Double? = 12.34
print(d) // Optional(12.34)
print(d~?) // 12.34
d = nil
print(d~?) // nil
Integrating the other guy's protocol idea made it so this even works with nested optionals, which often occur when using optional chaining. For example:
let i: Int??? = 5
print(i) // Optional(Optional(Optional(5)))
print("i: \(i~?)") // i: 5
Update
Simply use me.age ?? "Unknown age!". It works in 3.0.2.
Old Answer
Without force unwrapping (no mach signal/crash if nil) another nice way of doing this would be:
(result["ip"] ?? "unavailable").description.
result["ip"] ?? "unavailable" should have work too, but it doesn't, not in 2.2 at least
Of course, replace "unavailable" with whatever suits you: "nil", "not found" etc
To unwrap optional use age! instead of age. Currently your are printing optional value that could be nil. Thats why it wrapped with Optional.
In swift Optional is something which can be nil in some cases. If you are 100% sure that a variable will have some value always and will not return nil the add ! with the variable to force unwrap it.
In other case if you are not much sure of value then add an if let block or guard to make sure that value exists otherwise it can result in a crash.
For if let block :
if let abc = any_variable {
// do anything you want with 'abc' variable no need to force unwrap now.
}
For guard statement :
guard is a conditional structure to return control if condition is not met.
I prefer to use guard over if let block in many situations as it allows us to return the function if a particular value does not exist.
Like when there is a function where a variable is integral to exist, we can check for it in guard statement and return of it does not exist.
i-e;
guard let abc = any_variable else { return }
We if variable exists the we can use 'abc' in the function outside guard scope.
age is optional type: Optional<Int> so if you compare it to nil it returns false every time if it has a value or if it hasn't. You need to unwrap the optional to get the value.
In your example you don't know is it contains any value so you can use this instead:
if let myAge = age {
// there is a value and it's currently undraped and is stored in a constant
}
else {
// no value
}
I did this to print the value of string (property) from another view controller.
ViewController.swift
var testString:NSString = "I am iOS Developer"
SecondViewController.swift
var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")
Result :
The Value of String is I am iOS Developer
Check out the guard statement:
for student in class {
guard let age = student.age else {
continue
}
// do something with age
}
When having a default value:
print("\(name) is \(age ?? 0) years old")
or when the name is optional:
print("\(name ?? "unknown") is \(age) years old")
I was getting the Optional("String") in my tableview cells.
The first answer is great. And helped me figure it out. Here is what I did, to help the rookies out there like me.
Since I am creating an array in my custom object, I know that it will always have items in the first position, so I can force unwrap it into another variable. Then use that variable to print, or in my case, set to the tableview cell text.
let description = workout.listOfStrings.first!
cell.textLabel?.text = description
Seems so simple now, but took me a while to figure out.
This is not the exact answer to this question, but one reason for this kind of issue.
In my case,
I was not able to remove Optional from a String with "if let" and "guard let".
So use AnyObject instead of Any to remove optional from a string in swift.
Please refer link for the answer.
https://stackoverflow.com/a/51356716/8334818
If you just want to get rid of strings like Optional(xxx) and instead get xxx or nil when you print some values somewhere (like logs), you can add the following extension to your code:
extension Optional {
var orNil: String {
if self == nil {
return "nil"
}
return "\(self!)"
}
}
Then the following code:
var x: Int?
print("x is \(x.orNil)")
x = 10
print("x is \(x.orNil)")
will give you:
x is nil
x is 10
PS. Property naming (orNil) is obviously not the best, but I can't come up with something more clear.
With the following code you can print it or print some default value. That's what XCode generally recommend I think
var someString: String?
print("Some string is \(someString ?? String("Some default"))")
If you are printing some optional which is not directly printable but has a 'to-printable' type method, such as UUID, you can do something like this:
print("value is: \(myOptionalUUID?.uuidString ?? "nil")")
eg
let uuid1 : UUID? = nil
let uuid2 : UUID? = UUID.init()
print("uuid1: \(uuid1?.uuidString ?? "nil")")
print("uuid2: \(uuid2?.uuidString ?? "nil")")
-->
uuid1: nil
uuid2: 0576137D-C6E6-4804-848E-7B4011B40C11