Switft Dictionary problem 'String?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? - swift

I'm new to Swift and ObjC and can't get this simple bit of code to work. I get the error 'String?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
guard let data = context as? Dictionary<String,String> else {return}
var str : String
str = data["Score"] as String //<<<I get the error here
When I change it to as! I know get this warning: Forced cast from 'String?' to 'String' only unwraps optionals; did you mean to use '!'?
Any ideas how to extract the string from the dictionary so I can use it?

It might be possible, that the key "Score" is not set in your dictionary and may return nil. Use this code to unwrap the optional:
if let score = data["Score"] {
str = score
}
Since you already unwrapped context to Dictionary<String, String>, Swift will automatically infer the type of score as String.

You don't need to cast because data dictionary would always return optional String type and you can use guard statement
guard let str = data[Score] else { return }
// Your code here

The type returned by subscripting a Dictionary is an optional, since it is valid to subscript it with non-existinent keys. So, as per the error message, in a dictionary with String values, the return type is String?.
If you know for sure that the key always exist in the dictionary, you can force-unwrap it as data["Score"]! to make the type String (without casting, but crashing if it doesn't exist). Otherwise handle the nil in some way, e.g., if let str = data["Score"] or str = data["Score"] ?? "0".

Use like this:
if let dataDictionary = context as? [String: Any] {
if let someString = dataDictionary["score"] as? String {
print(someString)
}
}

We don't know what kind of data you're working with but you're likely either getting back a dictionary actually of type [String: String], in which case you would:
func getData(context: [String: String]) {
guard let str = context["Score"] else {
return
}
print(str)
}
...or you're really getting back a dictionary of type [String: Any] (more common with database dictionaries), in which case you would:
func getData(context: [String: Any]) {
guard let str = context["Score"] as? String else {
return
}
print(str)
}

Related

How to check if a field type Any? is nil o NSNull

I'm actually trying to parse a Json object with Swift3 on Xcode8.1.
This is my code:
if let objData = objJson["DATA"] as! NSDictionary? {
var msg: String = ""
if let tmp = objData.object(forKey: "Message") {
msg = tmp as! String
} else {
print("NIIILLLLL")
}
}
I'm getting this error message: Could not cast value of type 'NSNull' (0x4587b68) to 'NSString' (0x366d5f4) at this line msg = tmp as! String.
I'm not understanding why I'm getting this error because the type of tmp is Any and it should display the print instead of convert tmp as! String
Thank you for the help,
You can add casting in let.
if let tmp = objData.object(forKey: "Message") as? String {
msg = tmp
}
With Swift 3, for example:
fileprivate var rawNull: NSNull = NSNull()
public var object: Any {
get {
return self.rawNull
}
}
You can check field object as:
if self.object is NSNull {
// nil
}
So to answer your question in why you are getting that error, in your code "tmp" is not nil its something of type NSNull (if you want to know more about NSNull check the docs) but its basically "A singleton object used to represent null values in collection objects that don’t allow nil values."
The rest is just you are force casting which I recommend avoiding this is a safer way to do what you are doing.
guard let objData = objJson["DATA"] as? [String: Any], let msg = objData["Message"] else { return }
// now you can use msg only if exists and also important keeping its unmutable state

Swift Optional Dictionary [String: String?] unwrapping error

So here I have a basic setup
var preferenceSpecification = [String : String?]()
preferenceSpecification["Key"] = "Some Key"
preferenceSpecification["Some Key"] = nil
preferenceSpecification["DefaultValue"] = "Some DefaultValue"
print(preferenceSpecification)
var defaultsToRegister = [String : String]()
if let key = preferenceSpecification["Key"], let defaultValueKey = preferenceSpecification["DefaultValue"] {
defaultsToRegister[key] = preferenceSpecification[defaultValueKey]!
}
But the error points out where it demands that I force unwrap this, to be like this:
defaultsToRegister[key!] = preferenceSpecification[defaultValueKey!]!
Which doesn't make sense, because keyValue and defaultValue already are unwrapped
When you extract a value from a dictionary like this using subscript
[String: String?]
you need to manage 2 levels of optional. The first one because the subscript returns an optional. The second one because the value of you dictionary is an optional String.
So when you write
if let value = preferenceSpecification["someKey"] {
}
you get value defined as an optional String.
Here's the code to fix that
if let
optionalKey = preferenceSpecification["Key"],
key = optionalKey,
optionalDefaultValueKey = preferenceSpecification["DefaultValue"],
defaultValueKey = optionalDefaultValueKey,
value = preferenceSpecification[defaultValueKey] {
defaultsToRegister[key] = value
}
Suggestions
You should avoid force unwrapping as much as possible. Instead you managed to put 3 ! on a single line!
You should also try to use better name for your constants and variables.
You could also define an extension which helps get rid of the double optional situation.
extension Dictionary where Value == Optional<String> {
func flattened(_ key: Key) -> Value {
if let value = self[key] {
return value
}
return nil
}
}
Usage: preferenceSpecification.flattened("someKey")

Swift AnyObject Conversion

I created some test code to show the problem I am having.
This compiles fine in a playground, however, when I try to put it into a project, Xcode gives the following warning: Treating a forced downcast to 'String' as optional will never produce 'nil' on line 30. I am given two suggestions to fix the problem:
Use 'as?' to perform a conditional downcast to 'String', which makes absolutely no sense. However, it does compile without warnings/errors, which seems strange because it is assigning an optional value to a non-optional type of String.
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
From the Swift language guide.
Unless it thinks I might want to assign nil if the conversion fails (therefore removing the dictionary entry), this makes no sense. Especially because I am sure it will succeed because I literally just checked to see if it was a String.
Add parentheses around the cast to silence this warning, which seems pointless, but does silence the warning. This seems like a strange thing to do, but then again, it may just be a poor way of confirming that you really want to do what you are trying to do.
Which option is right, or neither? What is causing this warning?
The correct solution is to use optional binding instead of the
forced unwrap operator !. Actually you can incorporate the check
value != nil into the switch statement:
for (key, value) in dict {
switch value {
case let s as String:
newDict[key] = s
case let i as Int:
newDict[key] = String(i)
case let b as Bool:
newDict[key] = b ? "1" : "0"
case let v?: // value is not `nil`
newDict[key] = String(v)
default: // value is `nil`
break
}
}
Here is your code, modified to show how you can cast a function result to match AnyObject? and avoid explicitly unwrapped optionals:
func returnsAString() -> AnyObject? {
return "I am a String." as? AnyObject
}
func returnsAnInt() -> AnyObject? {
return Int(123) as? AnyObject
}
func returnsABool() -> AnyObject? {
return true as? AnyObject
}
func returnsNilBool() -> AnyObject? {
return nil as Bool? as? AnyObject
}
var dict : [String : AnyObject?] = [String : AnyObject?]()
var newDict : [String : String ] = [String : String ]()
dict["string"] = returnsAString()
dict["int"] = returnsAnInt()
dict["bool"] = returnsABool()
dict["nil"] = returnsNilBool()
for (key, value) in dict {
switch value {
case let value as String:
newDict[key] = value
case let value as Int:
newDict[key] = String(value)
case let value as Bool:
newDict[key] = (value ? "1" : "0")
default:
newDict[key] = "nil"
}
}
print("Dict: \(dict)")
print("newDict: \(newDict)")
// Dict: ["nil": nil, "int": Optional(123), "bool": Optional(1), "string": Optional(I am a String.)]
// newDict: ["nil": "nil", "int": "123", "bool": "1", "string": "I am a String."]

Convert dictionary containing `Any?` to `AnyObject`?

I'm looking for a straightforward way of converting a dictionary of type [String : Any?] to a dictionary of [String: AnyObject]. I could loop through the elements individually, but that just seems 'wrong' to me.
If I just try to cast the original dictionary, I get a crash.
let dict:[String : Any?] = ["something" : "else"]
// crashes with fatal error: 'can't unsafeBitCast
// between types of different sizes'
let newDict = dict as? [String: AnyObject]
Looping is exactly correct, and Swift encourages this. The most efficient and Swift-like (*) approach is:
var result = [String: AnyObject]()
for (key, value) in dict {
if let v = value as? AnyObject {
result[key] = v
}
}
(*) This isn't really "Swift-like" because it includes AnyObject, which should almost never be part of a non-temporary data structure. A proper Swift implementation would convert AnyObject to a real type.
Note that the crash you get is definitely not appropriate and you should open a radar (bugreport.apple.com). An as? should never crash like this. You should either get nil or a compiler failure.
Warning: as #rob-napier mentioned in the comments, its O(n^2) so this approach is only valid for small dictionaries (less than 100 elements).
You can use reduce:
let dict:[String : Any?] = ["something" : "else"]
let newDict = dict.reduce([String: AnyObject](), combine: { accumulator, pair in
var temp = accumulator
temp[pair.0] = pair.1 as? AnyObject
return temp
})
Here newDict is of type [String: AnyObject].

Swift: Cannot subscript a value of type 'Dictionary<String, NSObject>?'

EDIT: Not a DUPLICATE:
That solution gives 'Could not find an overload for 'subscript' that accepts the supplied arguments' error. So, no this is NOT a duplicate.
Here is the function declaration.
func auth(user: String, pass: String, completion: (returned: Bool, error: Bool, response: Dictionary<String, NSObject>?) -> ()){
response can be nil
}
Now I'm trying to access value passed back in another file and getting an error:
if let labelString = response["error_description"] as! String?{
self.labelPrompt.text = labelString
}
Error: Cannot subscript a value of type 'Dictionary?' with an index of type 'String'
It is a duplicate of the linked question: what you need is to unwrap the dictionary before using it with a subscript.
There's many ways ("if let", etc) and the linked answer gives the solution of using "optional binding" by adding a ? between the variable holding the dictionary and the subscript.
Example in a Playground:
var response: Dictionary<String, NSObject>? = nil
// NOTICE THE "?" BETWEEN THE VARIABLE AND THE SUBSCRIPT
if let labelString = response?["error_description"] as? String {
println(labelString) // not executed because value for key is nil
}
response = ["test":"yep"]
if let labelString = response?["test"] as? String {
println(labelString) // "yep"
}
Another way of unwrapping the dictionary:
if let responseOK = response, let test = responseOK["test"] as? String {
println(test) // "yep"
}