OpenWeatherMap API SWIFT - swift

So I am currently working on a personal project to help understand API's and how they work..so I am still a little new to iOS Development. I have already connected to the URL and gotten the data however now I am trying to make the results a little bit more clear cut.
Below is the code for the class (when the button is clicked it prints all this information)
First part of code
Second part of code
The error I get is Type 'Any' has no subscript members. Any idea as to why? Or how this can be fixed?

you can set their types like this then you can print values.
if let main = json["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
let temp_max = main["temp_max"] as? Double
print("temp\(temp_max!)")
let temp_min = main["temp_min"] as? Double
print("temp\(temp_min!)")
}
let items = json["weather"] as! [AnyObject]
let main = items[0]["main"] as! String
print(main)
let description = items[0]["description"] as! String
print(description)
let icon = items[0]["icon"] as! String
print(icon)
let name = json["name"] as! String
print("name\(name)")

Related

Pull string value from firebase snapshot

I am a new Swift developer. I'm using Swift 4.2 and Xcode 10.1.
I'm trying to create an array of strings from a firebase snapshot. I am able to do this by setting up a struct model and using an initializer, but when I want to do it directly in the code without a separate struct it does not work. Here is my code:
// Get the likers
dbRef.child("likers").observeSingleEvent(of: .value) { snapshot in
// Declare an array to hold the Likers
var retrievedLikers = [String]()
// Loop through the retrieved likers and put into the array
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String:Any]
let likerId = dict["userId"] as? String ?? ""
print (likerId)
retrievedLikers.insert(likerId, at: 0)
}
likers = retrievedLikers
}
My firebase data model looks like this:
I have tried several methods. The code above is the most recent based on another SO suggestion.
The problem is that I can't create a dictionary with snap.value. Snap has the correct data. I am getting an error saying "Could not case value of type '__NSCFString' to 'NSDictionary'. But the snap is showing as a dictionary. Here's a screenshot:
Any help would be appreciated. I've tried for 3 hours to solve this.
You can try
let res = snapshot.value as! [String:String]
retrievedLikers = Array(res.values)
Here the crash snap.value is a String that can't be casted to [String:Any]
let dict = snap.value as! [String:Any]

Swift Create Categories for Items (Classes + Attributes) at runtime

I have a problem I am not able to figure out myself or find any answer to:
I am creating an inventar app for my company and I want that the people are able to create categories for new items like in this example:
Create category at runtime (German Inventar Program)
My problem here is that I use firebase as the backend and - of course - upload all the attributes for the stored items whereas every item belongs to a category with predefined attributes. When I want to work with these items, I download the item from firebase and create an object with these specific attributes from a class I hard-coded at compile time.
I don't want to hard code the class at compile time and change all the functions and if-else blocks where i am checking for the actual category for an item. Now - when adding a new category - I have to change at least 50 places in code to make it work...
For better understanding, here is a function for one category which downloads all the items from firebase:
func downloadMiscsFromFirebase(handler: #escaping (_ miscs: [Miscellaneous]) -> ()) {
var miscs = [Miscellaneous]()
DataService.instance.hardwareRef.child(category).observeSingleEvent(of: .value) { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
let description = snap.childSnapshot(forPath: FirebaseIDs.Item.description).value as! String
let category = snap.childSnapshot(forPath: FirebaseIDs.Item.category).value as! String
let lfdNr = snap.childSnapshot(forPath: FirebaseIDs.Item.lfdNr).value as! Int
let fromTeam = snap.childSnapshot(forPath: FirebaseIDs.Item.fromTeam).value as! String
let hw = snap.childSnapshot(forPath: FirebaseIDs.Miscellaneous.hw).value as! String
let sw = snap.childSnapshot(forPath: FirebaseIDs.Miscellaneous.sw).value as! String
let other = snap.childSnapshot(forPath: FirebaseIDs.Miscellaneous.other).value as! String
let givenOut = snap.childSnapshot(forPath: FirebaseIDs.Item.givenOut).value as? Bool
let givenTo = snap.childSnapshot(forPath: FirebaseIDs.Item.givenTo).value as? String
let givenAt = snap.childSnapshot(forPath: FirebaseIDs.Item.givenAt).value as? String
let givenUntil = snap.childSnapshot(forPath: FirebaseIDs.Item.givenUntil).value as? String
let storageLocation = snap.childSnapshot(forPath: FirebaseIDs.Item.storageLocation).value as? String
let misc = Miscellaneous(UID: snap.key,
lfdNr: lfdNr,
description: description,
category: category,
fromTeam: fromTeam,
hw: hw,
sw: sw,
other: other,
givenOut: givenOut,
givenTo: givenTo,
givenAt: givenAt,
givenUntil: givenUntil,
storageLocation: storageLocation)
// Only add item if from the same team.
if fromTeam == self.fromTeam {
miscs.append(misc)
}
}
handler(miscs)
}
}
}
Thank you in advance!
I would create a general CategoryDefinition class/struct and a Category class/struct that looked something like
class CategoryDefinition {
var categoryName: String
var fields: [String: FieldType] //Dictionary with field definitions
}
class Category {
var values: [String: Any?] //Dictionary with field values
}
then two functions to convert from and to Firebase format.
To make it more flexible to work with this I would have a protocol with the properties and functions I mentioned. It would make sense to have a Factory class to generate Category instances from CategoryDefinition.
FieldType above is my made up enum since I assume you have pre-defined set of data types-

Firebase Add New Key/Value to Pre-existing database without crashing xcode

` let query = ref?.child("Reviews").queryOrdered(byChild: "UserID").queryEqual(toValue: myUser.userId)
query?.observeSingleEvent(of: .value) { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let uid = dict["UserID"] as! String
let review = dict["Body"] as! String
let rating = dict["Rating"] as! String
let titleID = dict["TitleID"] as! String
let reviewID = dict["ReviewID"] as! String
let ratingID = dict["RatingID"] as! String
`
THE ERROR OCCURS AT THE ratingID call to the database. It unwraps nil.
I am trying to adapt a pre existing Firebase database with a new key/value.
I then try to display entries in my tableview and I get a crash with unwrap returning nil. I know why this is happening and it's because the previous data does not have the new key/value I want to include in the node going forward. I have tried many different things such as if let and guard let without much fortune. How do I add new key/Values and still have the tableview read entries that don't have the new value?
I include an image of the current node and I want to add a 'RatingsID' to the node. When I do, I get the unwrap nil error.
Database node prior to new key/value
Your code is super close, just need to protect the code in case the ratingID key doesn't exist.
So change
let ratingID = dict["RatingID"] as! String
to
let ratingID = dict["RatingID"] as! String ?? ""
So if the RatingID node does not exist, you'll set ratingID to an empty string (or whatever string you want)
You could also code it to only operate on the value of that key if the node exists (not nil)
if let someVal = dict["xxx"] {
//do something with someVal
} else {
print("xxx node wasn't found")
}
Here's a complete example: We are reading some messages from the messages node and some of them have a test_key node and some dont. For those that don't, default string is assigned to test
let postsRef = self.ref.child("messages")
postsRef.observe(.childAdded) { snapshot in
let dict = snapshot.value as! [String: Any]
let msg = dict["msg"] as! String
let test = dict["test_key"] ?? "default string"
print(msg, test)
}

Using Guard in an Init?

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).

Using NSURLConnection With Swift

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