I'm hoping to write a Swift dictionary extension that will append the reliably same data that eventually gets passed into a web request as URL params. I've tried various tricks, none seem to work.
The closest i've gotten is:
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
mutating func auth() -> Dictionary {
self.updateValue("2.0", forKey: "api")
return self
}
}
But this is still throwing the error:
Cannot invoke 'updateValue' with an argument list of type '(String,
forKey:String)'
on the line that reads, "self.updateValue(..."
Any advice? Thanks in advance!
You just need to cast your string as! Value or as! Key. Also you have declared your method as mutating so you don't need to return anything.
extension Dictionary {
mutating func auth() {
updateValue("2.0" as! Value, forKey: "api" as! Key)
}
}
var dic: [String:AnyObject] = [:]
print(dic) // "[:]\n"
dic.auth()
print(dic) // "["api": 2.0]\n"
Related
import Foundation
enum Errors: Error {
case badParse
}
public typealias JSONDictionary = [String: Any]
public func decode(_ dictionary: JSONDictionary, key: String) throws -> URL {
guard let string = dictionary[key] as? String else {
throw Errors.badParse
}
if let url = URL(string: string) {
return url
}
throw Errors.badParse
}
public func decode<T>(_ dictionary: JSONDictionary, key: String) throws -> T {
guard let value = dictionary[key] else {
throw Errors.badParse
}
guard let attribute = value as? T else {
throw Errors.badParse
}
return attribute
}
let url: URL = try decode(["url":"test/url"], key: "url") // url: test/url
let urlOptional: URL? = try? decode(["url":"test/url"], key: "url") // nil
The above code works as long as you're decoding a NON-optional. The specific non-generic decode function is called and the URL is constructed.
However, if you have an optionally type variable and decode it, it will not use the correct function. In swift 4.2, both optionals and non optionals would use the correct non-generic function, but since I updated to Xcode 11 swift 5.1, this behavior has been seen.
Any help would be greatly appreciated!
I would prefer not to make function signatures with optional returns such as
public func decode(_ dictionary: JSONDictionary, key: String) throws -> URL? {
because it is not scalable...and it used to work without it.
Evidently, how type inference works changed a little bit in Swift 5. If you just want it to use the correct overload, you just need to give it a little push:
let urlOptional: URL? = try? decode(["url":"test/url"], key: "url") as URL
by adding as URL at the end.
func resetUserDefaults() {
let userDefaults = UserDefaults.standard
let dict = userDefaults.dictionaryRepresentation()
for (key,_) in dict {
if let key = key as? String {
userDefaults.removeObject(forKey: key)
} else {
#if DEBUG
NSLog("\(key)")
#endif
}
}
}
I'm getting this warning. can anyone suggest me how to avoid this warnoing
All keys in UserDefaults must be of type String. So key is declared as a String. So attempting to cast it to a String is pointless. Hence the warning.
All you need is:
func resetUserDefaults() {
let userDefaults = UserDefaults.standard
let dict = userDefaults.dictionaryRepresentation()
for (key,_) in dict {
userDefaults.removeObject(forKey: key)
}
}
There is no need to cast something to the type that it is already known (to the compiler) to have.
Just remove the whole condition and use your key directly.
Since the keys in the UserDefault should of type String, casting the key to string is of no use, and hence you are getting this warning.
func resetUserDefaults() {
let userDefaults = UserDefaults.standard
let dict = userDefaults.dictionaryRepresentation()
for (key, _) in dict {
userDefaults.removeObject(forKey: key)
}
}
It will always show waring because dictionaryRepresentation() return [String : Any].
So when you cast from string to string it will definitely show warning.
for more see this -> https://developer.apple.com/documentation/foundation/userdefaults/1415919-dictionaryrepresentation
I had the same issue with a private function in Swift 5 and I found a solution working for me.
The solution was to change the value to optional.
I added a question mark after the type I was looking for. (as String"?")
You can see an example here :
private func doSomeThing(completion: #escaping (String) -> ()) {
let Something = somethingElse;
if let anoterThing = something as String?{
completion(anoterThing)
}else{
completion("Error at private func doSomeThing")
}
}
You can find more pieces of information here:
https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html
Swift: difference as String? vs. as? String
Downcasting in Swift with as and as?
Best Regards
I'm making an extension on Dictionary, just a convenience method to traverse a deep json structure to find a given dictionary that might be present. In the general extension of a Dictionary, i'm not able to subscript because i give a String instead of a Key
extension Dictionary {
func openingHoursDictionary() -> Dictionary<String,AnyObject>? {
if let openingHours = self["openingHours"] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
return nil
}
}
Error: String is not convertible to DictionaryIndex<Key, Value>
on self["openingHours"]
How can i make a Key from the String "openingHours" or check the dictionary for ths string?
You can check at runtime if the string is a valid key for the dictionary:
extension Dictionary {
func openingHoursDictionary() -> [String : AnyObject]? {
if let key = "openingHours" as? Key {
if let openingHours = self[key] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
}
return nil
}
}
But this will "silently" return nil if called for other dictionaries
like [Int, AnyObject].
If you want the compiler to check if it is safe to subscript the
dictionary with a string then you have to use a (generic) function:
func openingHoursDictionary<T>(dict : [String : T]) -> [String : AnyObject]? {
if let openingHours = dict["openingHours"] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
return nil
}
It is (currently) not possible to write Dictionary (or Array)
extension methods that apply only to a restricted type of the
generic parameters.
Just cast the String as Key using "openingHours" as Key
if let pickupPoints = self["openingHours" as Key] as? Array<AnyObject> {
}
Downside is that if doing this i will get a crash if i have a Dictionary<Int,AnyObject> and use the method there.
0x10e8c037d: leaq 0x362aa(%rip), %rax ; "Swift dynamic cast failure"
I am working inside a Swift Extension. I am trying to append data to an array of the type [[String: AnyObject]]. The reason that this is in an extension is because I have to do this lot's of times to lot's of arrays. The problem is, when I append an object of type: [String: AnyObject], I get the error: Dictionary'<'String, AnyObject'>' Not Convertible to T (the quotes are there because within the carrots nothing showed up).
mutating func appendData(data: [String: [String: AnyObject]]?) {
if data != nil {
for (id, object) in data! {
var mutatingObject = object
mutatingObject["id"] = id
append(mutatingObject)
}
}
}
I am not certain what exactly are you trying to achieve. but take a note - Arrays are generic collections that store specific type. Extension for Array might not know what type will be used in each case, so it cannot simply allow you to store Dictionary<String, AnyObject>.
Here is an example on how to make your code more generic:
extension Array {
mutating func appendData(data: [String: T]?) {
if data != nil {
for (id, object) in data! {
if var mutatingObject = object as? [String : AnyObject] {
mutatingObject["id"] = id
}
append(object)
}
}
}
}
I created a swift class to test Dictionaries. So, I wrote the code below:
import Foundation
class MyClass {
var myFirstDictionary:[String :String]
var myThirdDictionary:[String :String]?
init(){
var mySecondDictionary:[String :String] = [String :String]()
mySecondDictionary["animal"] = "Monkey"
mySecondDictionary.updateValue("something", forKey: "SomeKey")
self.myFirstDictionary = [String :String]()
addOneThingToSecondDictionary()
addAnotherThingToSecondDictionary()
self.myThirdDictionary! = [String :String]()
addOneThingToThirdDictionary()
addAnotherThingToThirdDictionary()
}
func addOneThingToSecondDictionary(){
self.myFirstDictionary["animal"] = "Monkey"
}
func addAnotherThingToSecondDictionary(){
self.myFirstDictionary.updateValue("Superman", forKey: "hero")
}
func addOneThingToThirdDictionary(){
self.myThirdDictionary["animal"]! = "Monkey"
}
func addAnotherThingToThirdDictionary(){
self.myThirdDictionary!.updateValue("Superman", forKey: "hero")
}
}
So, I got 3 errors referring to "myThirdDictionary" :
In the Dictionary initialization compiler said: Could not find an overload for 'init' that accepts the supplied arguments
When I tried to add a Key/value pair in addOneThingToThirdDictionary() : '[String : String]?' does not have a member named 'subscript'
When I tried to add a Key/value pair in addAnotherThingToThirdDictionary() : Immutable value of type '[String : String]' only has mutating members named 'updateValue'
Any thoughts ?
Some of these issues are conceptual errors, and some of them have to do with behaviors that changed in today's Xcode 6 beta 5 release. Running through them all:
This line compiles, but has a superfluous !:
self.myThirdDictionary! = [String :String]()
You don't need to unwrap an optional to assign to it -- it doesn't matter if its current contents are nil if you're providing new contents. Instead, just assign:
self.myThirdDictionary = [String :String]()
Similarly, this line fails because you're subscripting before unwrapping:
self.myThirdDictionary["animal"]! = "Monkey"
This is a problem because you could be subscripting nil if myThirdDictionary has not been initialized. Instead, subscript after checking/unwrapping the optional. As of beta 5, you can use mutating operators or methods through an optional check/unwrap, so the shortest and safest way to do this is:
self.myThirdDictionary?["animal"] = "Monkey"
If myThirdDictionary is nil, this line has no effect. If myThirdDictionary has been initialized, the subscript-set operation succeeds.
This line failed on previous betas because force-unwrapping produced an immutable value:
self.myThirdDictionary!.updateValue("Superman", forKey: "hero")
Now, it works -- sort of -- because you can mutate the result of a force-unwrap. However, force unwrapping will crash if the optional is nil. Instead, it's better to use the optional-chaining operator (which, again, you can now mutate through):
self.myThirdDictionary?.updateValue("Superman", forKey: "hero")
Finally, you have a lot of things in this code that can be slimmed down due to type and scope inference. Here it is with all the issues fixed and superfluous bits removed:
class MyClass {
var myFirstDictionary: [String: String]
var myThirdDictionary: [String: String]?
init(){
var mySecondDictionary: [String: String] = [:]
mySecondDictionary["animal"] = "Monkey"
mySecondDictionary.updateValue("something", forKey: "SomeKey")
myFirstDictionary = [:]
addOneThingToSecondDictionary()
addAnotherThingToSecondDictionary()
// uncomment to see what happens when nil
myThirdDictionary = [:]
addOneThingToThirdDictionary()
addAnotherThingToThirdDictionary()
}
func addOneThingToSecondDictionary(){
myFirstDictionary["animal"] = "Monkey"
}
func addAnotherThingToSecondDictionary(){
myFirstDictionary.updateValue("Superman", forKey: "hero")
}
func addOneThingToThirdDictionary(){
myThirdDictionary?["animal"] = "Monkey"
}
func addAnotherThingToThirdDictionary(){
myThirdDictionary?.updateValue("Superman", forKey: "hero")
}
}
(Changes: Foundation import unused, empty dictionary literal instead of repeated type info)
Simple but effective
let like = (dic.value(forKey: "likes") as? NSDictionary)
print(like?.count)
Or if you want get complete Dictionary count then use:
if let mainDic = dic as? NSDictionary{
print(mainDic.count)
//add your code
}