Using the default enum as a dictionary key without explicitly clicking on String

If I declare enum as follows:

 enum Keys { case key_one case key_two } 

I can print it and it will be automatically converted to String :

 print(Keys.key_one) // prints "key_one" 

If I then create a dictionary that maps Strings to anything (but let Strings be selected again for simplicity), I think it should be able to add a key using Keys.key_one as the key, right? Wrong.

 var myDict = [String : String]() /// error: cannot subscript a value of type '[String : String]' with an index /// of type 'Keys' myDict[Keys.key_one] = "firstKey" 

I can do this if I explicitly convert Keys.key_one to String , like this:

 myDict[String(Keys.key_one)] = "firstKey" print(myDict) // ["key_one": "firstKey"] 

So, I want to do this without having to wrap enum String() every time.

I tried several things by doing extension off Dictionary with the where keyword using Key and trying to implement new subscript functions, but I can't get it to work. What a trick?

Update for solution

I found my solution ( below) .

+5
source share
3 answers

I figured out the extension I wanted.

 extension Dictionary { subscript(key: Keys) -> Value? { get { return self[String(key) as! Key] } set(value) { guard let value = value else { self.removeValueForKey(String(key) as! Key) return } self.updateValue(value, forKey: String(key) as! Key) } } } enum Keys { case key_one case key_two } var myStringDict = [String : String]() /// Adding the first key value through the String() way on purpose myStringDict.updateValue("firstValue", forKey: String(Keys.key_one)) // myStringDict: ["key_one": "firstValue"] // myStringDict[Keys.key_one]!: firstValue myStringDict[Keys.key_two] = "secondValue" // myStringDict: ["key_one": "firstValue", "key_two": "secondValue"] myStringDict[Keys.key_one] = nil // myStringDict: ["key_two": "secondValue"] 

Note that the declared dictionary type of the word is String , but I can just use Keys.key_one , and the index in the dictionary extension will take care of the rest.

Maybe I’d better work on transforming as! in Key , but I'm not sure if this is necessary, since I know that my enum can always be converted to a valid Key using String() casting.

Better response

Even better, since I use this for API keys, I made an empty protocol called APIKeys , and each model will implement its own list of Keys , which conforms to the APIKeys protocol. And the dictionary index is updated to accept APIKeys as the value of Key .

 extension Dictionary { subscript(key: APIKeys) -> Value? { get { return self[String(key) as! Key] } set(value) { guard let value = value else { self.removeValueForKey(String(key) as! Key) return } self.updateValue(value, forKey: String(key) as! Key) } } } protocol APIKeys {} enum Keys: APIKeys { case key_one case key_two } enum Model1Keys: APIKeys { case model_1_key_one case model_1_key_two } enum Model2Keys: APIKeys { case model_2_key_one case model_2_key_two } var myStringDict = [String : String]() var model1StringDict = [String : String]() var model2StringDict = [String : String]() myStringDict.updateValue("firstValue", forKey: String(Keys.key_one)) // myStringDict: ["key_one": "firstValue"] myStringDict[Keys.key_two] = "secondValue" // myStringDict: ["key_one": "firstValue", "key_two": "secondValue"] myStringDict[Keys.key_one] = nil // myStringDict: ["key_two": "secondValue"] model1StringDict.updateValue("firstValue", forKey: String(Model1Keys.model_1_key_one)) // model1StringDict: ["model_1_key_one": "firstValue"] model1StringDict[Model1Keys.model_1_key_two] = "secondValue" // model1StringDict: ["model_1_key_one": "firstValue", "model_1_key_two": "secondValue"] model1StringDict[Model1Keys.model_1_key_one] = nil // model1StringDict: ["model_1_key_two": "secondValue"] model2StringDict.updateValue("firstValue", forKey: String(Model2Keys.model_2_key_one)) // model2StringDict: ["model_2_key_one": "firstValue"] model2StringDict[Model2Keys.model_2_key_two] = "secondValue" // model2StringDict: ["model_2_key_one": "firstValue", "model_2_key_two": "secondValue"] model2StringDict[Model2Keys.model_2_key_one] = nil // model2StringDict: ["model_2_key_two": "secondValue"] 
+7
source

Or you can do:

 enum Keys: String { case key_one case key_two } 

And use it as follows:

 var myDict = [String : String]() myDict[Keys.key_one.rawValue] = "firstKey" 

Update

If you really want to make it work, you can do it as shown below. But these are some risks forcing to deploy.

 extension Dictionary where Key: StringLiteralConvertible { subscript (key: Keys) -> Value? { get { return self[key.rawValue as! Key] } set { self[key.rawValue as! Key] = newValue } } } 

And then you use it like this:

 myDict[Keys.key_one] = "firstKey" myDict[Keys.key_one] 
+1
source

How important is your keys to be strings? If this is not important, I suggest doing something like:

 enum Keys { case key_one case key_two } var myDict = [Keys : String]() myDict[Keys.key_one] = "firstKey" print(myDict) // [Keys.key_one: "firstKey"] 
0
source

Source: https://habr.com/ru/post/1244164/


All Articles