Error when getting value from constant : Ambiguous reference to member 'subscript' - swift

I am using enum and tuple with value of enum case. I've trouble with getting value from [String: String] constant.
I don't know how to fix it, it has to be a trap, but I don't know where, as the key is surely string:
enum DictTypes : String {
case settings
case options
case locations
}
enum FileTypes : String {
case json
case pList
}
func getCodebookUrlComponent() -> String
{
var FileSpecs: (
dictType: DictTypes,
fileType: FileTypes,
redownload: Bool
) = (.settings, .json, true)
let codebooks = [
"settings" : "settings",
"options" : "options"
]
let key = self.FileSpecs.dictType // settings or options
if let urlComponent = codebooks[key] {
return urlComponent
}
return ""
}
This line if let urlComponent = codebooks[key] comes with an error:
Ambiguous reference to member 'subscript'

You should use .rawValue for this case :
if let urlComponent = codebooks[key.rawValue]{
return urlComponent
}
This problem occurs because of the let key = self.FileSpecs.dictType in this line you receive key that is FileSpecs type. And subscript that is implemented in the Array will not conform for that value type.
rawValue in you're case return String value that you assign in you're enum.

Since value from enum case is surely string, I would type it like this:
let key = FileSpecs.dictType.rawValue // "settings" or "options"
or
let key = String(describing: FileSpecs.dictType)
return codebooks[key]!

Related

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."]

Swift: Getting dictionary value if I know the key

This should be super basic but I'm getting an error anyway.
Cannot subscript a value of type 'Dictionary<String, AnyObject>' with an index of type 'String'
Here's my code:
func createComments(attributes: [[String: AnyObject]], votes: [String: AnyObject], sid: Int) -> [Comment] {
var comments: [Comment] = [Comment]()
for commentAttributes in attributes {
let comment = Comment()
comment.commentId = commentAttributes["id"]
comments.append(comment)
}
return comments
}
I'm getting the error on this line:
comment.commentId = commentAttributes["id"]
As far as I can tell, commentAttributes should be a Dictionary with keys as Strings and values of AnyObject. I'm not sure how else to access the values of a Dictionary with a String key other than by using a String to subscript. What am I missing here?
Try to use if let and cast it to proper type.
for commentAttributes in attributes {
let comment = Comment()
if let id = commentAttributes["id"] as? Int {
comment.commentId = id
}
comments.append(comment)
}
Of course as soon as I ask the question I find the answer:
Cannot subscript a value of type '[String : AnyObject]' with an index of type 'String'
I needed to typecast the value of commentAttributes["id"] so that it matched the type of comment.commentId
comment.commentId = commentAttributes["id"] as! Int

Swift - matching class dynamically

I am trying to write an extension method on Dictionary with the following signature:
func firstNonNilObjectForKey(keys: [Key], ofClass aClass: Any.Type = String.self) -> Value?
This is meant to help me deal with JSON dictionaries, where I often need the first non-null value, but sometimes the JSON includes null itself as value, which is converted to NSNull in Cocoa.
The usage would be something like:
let dict: [String:AnyObject] = [...]
let myValue = dict.firstNonNilObjectForKey([ "key1", "key2" ]) as? String
The issue is implementational - how to match the class:
if let value = self[key] {
if value is aClass { ... } <--- error: aClass is not a type
if let castedValue = value as? aClass { ... } <--- error: ditto
let type = value.dynamicType
if type == aClass { ... } <--- no error, but doesn't handle subclasses!
// Cannot use value.isKindOfClass() since value may be Any
}
I have thought of an alternative:
func firstNonNilObjectForKey<ReturnValueType>(keys: [Key]) -> ReturnValueType?
which allows to be implemented as
if let value = self[key] as? ReturnValueType { ... }
But:
- Here I cannot set the default type (I mostly need String.Type).
- I'm not really sure how to invoke this:
let value = dict.firstNonNilObjectForKey([ "key1", "key2" ]) as? String <--- error: Cannot invoke 'firstNonNilObjectForKey' with an argument list of type '([String])'
let value = dict.firstNonNilObjectForKey<String>([ ... ]) <--- error: Cannot explicitly specialize a generic function
I hope this isn't a duplicate, but most similar questions here are simply handling a situation where you are matching against a known class.
I'm not sure I got the requirements exactly, but can something like this work for you?
extension Dictionary {
func firstNonNilObjectForKey(keys: [Key]) -> String? {
return self.firstNonNilObjectForKey(keys, ofClass: String.self)
}
func firstNonNilObjectForKey<T>(keys: [Key], ofClass aClass: T.Type) -> T? {
for k in keys {
if let v = self[k] as? T {
return v
}
}
return nil
}
}
let dict = ["key1": 2, "key2": "Foo"]
let aString = dict.firstNonNilObjectForKey(["key1", "key2"]) // "Foo"
let anInt = dict.firstNonNilObjectForKey(["key1", "key2"], ofClass: Int.self) // 2
The main gotcha here is that I'm using overloading as opposed to default parameters, which don't seem to get along well with the swift type checker.

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"
}

Grabbing values from a dictionary in a more elegant way

I've been playing with swift and am getting quite tortured! Consider:
var myDict : Dictionary <String, String>
//DO SOME MAGIC TO POPULATE myDict with values
<magic being done>
//Now myDict has values. Let's parse out the values of myDict
//This doesn't work
let title : String = myDict["title"]
//This does
let title : String? myDict["title"]
This is because it isn't known whether the key is in the dictionary. What I want to say, though, is "If the title key is in the dictionary, give me that value, else, just give me an empty string"
I could probably write:
var myTitle : String
if let title : String = myDict["title"] {
myTitle = title
} else {
myTitle = ""
}
I believe that works...BUT...it's quite a lot of code for EACH key of the dictionary. Does anyone have any ideas in the swift world on how this is supposed to be written?
RD
You could write an extension on optional:
extension Optional {
/// Unwrap the value returning 'defaultValue' if the value is currently nil
func or(defaultValue: T) -> T {
switch(self) {
case .None:
return defaultValue
case .Some(let value):
return value
}
}
}
Then you can do:
myDict["title"].or("")
This would also work for all optionals.
Note: I started a module to add common helpers like this or on Optional to swift.
You unwrap the value either explicitly:
let title : String = myDict["title"]!
or implicitly:
let title : String! = myDict["title"]
Note that you still have to check whether title is nil or not unless you are really sure it's there.
Edit:
Here's a sample global operator overload for any optional for type T:
#infix func | <T: Any>(lhs: T?, rhs: T!) -> T! {
if lhs {
return lhs!
}
return rhs
}
var myDict : Dictionary <String, String> = ["a": "b"]
let title1 = (myDict["a"] | "") // "b"
let title2 = (myDict["title"] | "") // ""