I have a segment of code that gets info from an API, and I need to add it to a Dictionary. The code is below:
typealias JSONdic = [String: AnyObject]
var weatherData: AnyObject = StorageManager.getValue(StorageManager.StorageKeys.WeatherData)!
let json: AnyObject = ["Any": "Object"]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, tempi = history["tempi"] as? Int, hum = history["hum"] as? String, precip = history["precipi"] as? String{
println("Temperature:\(tempi) Humidity:\(hum) Precipitation:\(precip)")
weatherData = [NSDate: AnyObject]()
let temp = tempi as NSNumber
weatherData[(The Current Date)] = temp
}
I want to first add "temp" to the weatherData Dictionary, but even after casting it to NSNumber, I am told that an NSNumber value cannot be assigned to the AnyObject?! type. Can anyone help me fix this?
Your weatherData variable is of type AnyObject. Despite the fact that you later assign it a value of type [NSDate: AnyObject], the variable itself is still considered by the compiler to be AnyObject. You then hit problems because you try to subscript it, assigning an NSNumber, which is obviously not possible on AnyObject.
Your declaration of weatherData should ensure it is the type you intend. If you are sure that your StorageManager will return you the appropriate dictionary type for the weather data key, you can force downcast it to the correct type:
var weatherData = StorageManager.getValue(StorageManager.StorageKeys.WeatherData) as! [NSDate: NSObject]
Related
I am accessing user default value as:
let data = UserDefaults.standard.object(forKey: identifier)
when I see the value in data it is visible as:
https://i.stack.imgur.com/UOjI8.png
type of data is
po type(of: data)
Swift.Optional<Any> . //Output
How can I access pageNumber?
since data is a dictionary in order to have access to pageNumber you need to cast data as a Dictionary
guard let data = UserDefaults.standard.value(forKey: identifier) as? [String: Any] else { //fallback here if you need return }
let pageNumber = data["pageNumber"] as? Int ?? 0
Simply use dictionary(forKey:) method on UserDefaults instance to directly fetch a dictionary from UserDefaults.
The dictionary returned is of type [String:Any]?
if let dict = UserDefaults.standard.dictionary(forKey: identifier) {
let pageNumber = dict["pageNumber"] as? Int
}
Note: pageNumber is of type Int?. Unwrap it to use further.
Everything works swimmingly except for when I do a random string like "fds", how would I correctly and efficiently use a guard to protect from this sort of error?
init(weatherData: [String: AnyObject]) {
city = weatherData["name"] as! String
let weatherDict = weatherData["weather"]![0] as! [String: AnyObject]
description = weatherDict["description"] as! String
icon = weatherDict["icon"] as! String
let mainDict = weatherData["main"] as! [String: AnyObject]
currentTemp = mainDict["temp"] as! Double
humidity = mainDict["humidity"] as! Int
let windDict = weatherData["wind"] as! [String: AnyObject]
windSpeed = windDict["speed"] as! Double
}
how would I correctly and efficiently use a guard to protect from this sort of error?
Why would you want to? If the caller does not hand you a dictionary whose "name" key is present and is a string, you are dead in the water because you cannot initialize city. You want to crash.
If you would like to escape from this situation without actually crashing, then make this a failable initializer and fail (return nil) if the dictionary doesn't contain the needed data. This effectively pushes the danger of crashing onto the caller, because the result will be an Optional that might be nil, and the caller must check for that.
init?(weatherData: [String: AnyObject]) {
guard let city = weatherData["name"] as? String else {return nil}
self.city = city
// ... and so on ...
}
But what I would do is none of those things. I would rewrite the initializer as init(city:description:icon:currentTemp:humidity:windSpeed:) and force the caller to parse the dictionary into the needed data. That way, if the data is not there, we don't even try to initialize this class in the first place. My argument would be that it is the caller's job to parse the dictionary; this class should have no knowledge of the structure of some complex dictionary pulled off the Internet (or whatever the source is).
I have a Gfycat struct that represents the data I want to store after making a network call to the Gfycat API.
typealias JSONDictionary = [String: Any]
struct Gfycat {
let id: String
let number: Int
}
In an extension to the Gfycat struct, I wrote a failable initializer that takes a dictionary of type [String: Any] as its argument. This dictionary is then used to assign values to the struct's properties. This is the original init method I wrote:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let number = dictionary["gfyNumber"] as? Int { return nil }
self.id = id
self.number = number
}
}
The problem is that when accessing a value from the dictionary, I cannot downcast the value from Any to Int. I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
This was my solution:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let uncastedNumber = dictionary["gfyNumber"] as? String,
let number = Int(uncastedNumber) else { return nil }
self.id = id
self.number = number
}
}
I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
It's neither a bug nor a feature of Swift. It's a fact about the dictionary you're working with. This thing is a String, not an Int. So you cannot cast it to an Int.
This code works fine in Swift 2:
guard let userData = responseData["UserProfile"] as? [String : AnyObject] else { return }
var userProfileFieldsDict = [String: String]()
if let profileUsername = userData["Username"] as? NSString {
userProfileFieldsDict["username"] = String(profileUsername)
}
if let profileReputationpoints = userData["ReputationPoints"] as? NSNumber {
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}
But, in Swift 3 it throws an error on userProfileFieldsDict["reputation"] saying
init has been renamed to init(describing:)
My question is why does it trigger on that line and not on the userProfileFieldsDict["username"] assignment line, and how to go about fixing it? I'm assuming it's because I'm casting a NSNumber to a String, but I can't really understand why that matters.
NSNumber is a very generic class. It can be anything from a bool to a long to even a char. So the compiler is really not sure of the exact data type hence it's not able to call the right String constructor.
Instead use the String(describing: ) constructor as shown below
userProfileFieldsDict["reputation"] = String(describing: profileReputationpoints)
Here's more info about it.
You need to drop your use of Objective-C types. This was always a bad habit, and now the chickens have come home to roost. Don't cast to NSString and NSNumber. Cast to String and to the actual numeric type. Example:
if let profileUsername = userData["Username"] as? String {
userProfileFieldsDict["username"] = profileUsername
}
if let profileReputationpoints = userData["ReputationPoints"] as? Int { // or whatever
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}
I am using the following code to get data from an API:
typealias JSONdic = [String: AnyObject]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, hour = history["hour"] as? String {
println(hour)
}
However, Xcode tells me that "json" is not a recognized identifier. I believe this can be solved with NSURLConnection, but I have no idea how to use that. Can anyone provide any examples of this protocol in use?
You're declaring a variable by setting it to itself, which doesn't make any sense. In order to use a variable on the right hand side of an assignment, it needs to have already been declared. So let's give json a value outside of the casting statements and it works fine.
typealias JSONdic = [String: AnyObject]
let json: AnyObject = ["greeting": "Hello"]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, hour = history["hour"] as? String {
println(hour)
}