Swift 3 retrieve value from nested JSON Array - ios

Hi I am currently learning Swift 3, and I have an app I am "developing" to provide weather for a specific location.
I have got my MetOffice API and retrieved the JSON required. I am able to retrieve the location without issue, but when I try to get the temp data I get no result. The temp data is listed as DM in the JSON and is an array within an array within a dictionary within dictionary.
my current code looks like
if let SiteRep = dict["SiteRep"] as? Dictionary<String, Any> {
if let DV = SiteRep["DV"] as? Dictionary<String,Any> {
if let location = DV["Location"] as? Dictionary<String, Any> {
if let period = location["Period"] as? Dictionary<String, Any> {
if let Rep = period["Rep"] as? [Dictionary<String, Any>] {
if let DM = Rep[0]["DM"] as? Double {
self._currentTemp = DM
} } } } } }
relevant vars are declared above this.
I'm not sure how to correct this code to pull this required value back from the JSON
Json as follows, the value i am trying to retrieve is highlighted in bold
{
"SiteRep":{
"Wx":{ },
"DV":{
"dataDate":"2017-02-10T12:00:00Z",
"type":"Forecast",
"Location":{
"i":"350497",
"lat":"99.6115",
"lon":"-21.0017",
"name":"TOWN",
"country":"ENGLAND",
"continent":"EUROPE",
"elevation":"160.0",
"Period":[
{
"type":"Day",
"value":"2017-02-10Z",
"Rep":[
{
"D":"NE",
"Gn":"16",
"Hn":"77",
"PPd":"51",
"S":"9",
"V":"GO",
**"Dm":"2",**
"FDm":"-2",
"W":"24",
"U":"1",
"$":"Day"
},
{
"D":"NNW",
"Gm":"20",
"Hm":"89",
"PPn":"58",
"S":"11",
"V":"GO",
"Nm":"-1",
"FNm":"-6",
"W":"24",
"$":"Night"
}

Related

iOS Swift iterate over an Array of Dictionary Items from Firestore

I am trying to iterate over the following dictionary:
Dictionary in Firebase
This is my code:
Global.sharedInstance.db.collection("usuarios").getDocuments { (snapshot, error) in
if error != nil {
print("error de lectura usuarios...")
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let txtIdentificador = data["identificador"] as? String ?? ""
let txtBio = data["bio"] as? String ?? ""
let txtNombre = data["nombre_usuario"] as? String ?? ""
let txtFotoPerfil = data["foto_perfil"] as? String ?? ""
var arrFotos = data["fotos"] as? [String: [String:String]]
}
}
}
}
I am able to retrieve the first few lines, like the id, the biography, name, etc.
But when I try to access the array of dictionary I have no idea.
This is the main idea:
I have a set of users, which I iterate over with the first loop 'for document in documents...", then each user has a set of photos. I want to iterate over the 3 photos, and in each iteration I want to retrieve the fields, so I can create a object called Image and associate the user with the 'hasUpload(Image)'.
I would like to know how to iterate over X photos an in each iteration retrieve the fields.
Something like this:
var arrFotos = data["fotos"] as? [String: [String:String]]
for foto in arrFotos {
for (key,value) in foto {
}
}
I get the error: For-in loop requires '[String : [String : String]]?' to conform to 'Sequence'; did you mean to unwrap optional?
A similar StackOverflow case can be found here and this is how they resolved it:
You can either do this, where x is the index and token is the element:
for (x, token) in transcriptCandidate.enumerated() {
}
Or this if you don't need the index:
for token in transcriptCandidate {
}

Parsing JSON with dynamic keys in Swift [duplicate]

This question already has an answer here:
How to obtain values without key of dictionary?
(1 answer)
Closed 6 years ago.
I am trying to parse JSON files from an url. This JSON has a key that is dynamic, in that it changes per file. Right now my code looks like this:
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
// Parsing
if let stations = json["observations"]!!["-10019482"] as? [[String: AnyObject]] {
for observation in stations {
if let name = observation["stationname"] as? String {
if let temperatuuri = observation["Temperature"] as? String {
if let windSpeed = observation["WindSpeedMS"] as? String {
print(name, temperatuuri, windSpeed,"m/s")
self.temperature.text = String(temperatuuri)
}
}
}
}
}
"-10019482" is the part that changed dynamically and the key name cannot be predicted. However, I know that it is always the first key inside "observations".
How would I do the same, but without searching for a specific key name?
Since json["observations"] is a dictionary you could do something like this to obtain it's first key
guard let dict = json["observations"] as? [String: AnyObject],
let firstKey = dict.keys.first else { return }
print(firstKey) // should print the first key found in the observations dictionary

Swift--Reading in JSON file

Using Xcode 6.4 and programming in swift.
I am typing a program that should read in JSON from a URL. A sample of the JSON can be found at this URL (https://itunes.apple.com/us/rss/topmovies/limit=2/json) and Ive been using this site to parse the JSON in order to read it better (http://json.parser.online.fr/).
Now I need to work through the levels of the JSON in order to get to
the actual movie names and image URL's but I am lost at what kind of variable entryDictionary should be. I was thinking it should be an array of dictionaries, and this compiles, but the output of entryDictionary in the console is sloppy looking, starting with Optionl( and not entry{ as it should. And when I go to loop through entryDictionary, I get an error saying entry does not have a subscript of type AnyObject.
So I am asking how I retrieve the im:name fields and im:image from the JSON.
func downloadDataFromURLString(urlString: String) {
//Downloaded data and is now stored in data. Took code out because
//irrelevant to my problem at this point. Data variable has correct
//JSON, now I am trying to parse it.
} else { //download suceeded, time to parse
var error: NSError? = nil
var names = [String]()
if let rootDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? [String: AnyObject] {
let feedDictionary = rootDictionary["feed"] as! [String: AnyObject]
let entryDictionary: AnyObject? = feedDictionary["entry"]
println(entryDictionary) //For debugging
//for entry in entryDictionary as! NSArray {
// let name = entryDictionary["name"]
// let image = entryDictionary["image"]
// let movie = Movie(name: name!, image: image!)
// weakSelf!.movies.append(movie)
//}
here is a blueprint of the JSON
"feed":{
"author":{},
"entry":[
{
"im:name":{
"label":"Deadpool"
},
"im:image":[],
"summary":{},
"im:price":{},
"im:contentType":{},
"rights":{},
"title":{},
"link":[],
"id":{},
"im:artist":{},
"category":{},
"im:releaseDate":{}
AnyObject is indeed not subscriptable (you're trying to subscript a variable whose type is AnyObject? with ["feed"]). You should also avoid casting to Cocoa container types like NSArray and NSDictionary whenever you can. Here's an example of how you might get the labels out of the entries array's names array:
import Foundation
func labels(feedDictionary:[String:AnyObject]) -> [String] {
guard let entries = feedDictionary["entry"] as? [String:AnyObject] else {
return []
}
return entries.flatMap { (key:String, value:AnyObject) -> String? in
guard key == "im:name" else {
return nil
}
guard let name = value as? [String:String] else {
return nil
}
return name["label"]
}
}
I'd however advise against using NSJSONSerialization on its own in Swift for anything but the simplest case, as you end up casting and wrapping optionals until the cows come home.
There are good 3rd party libraries such as Freddy and SwiftyJSON which apply Swift language features to accomplish a very convenient JSON (de)serialization experience.
For instance with Freddy you could express your problem in the following style:
let json = try JSON(data: data)
json.decode("feed", type:Feed.self)
struct Feed: JSONDecodable {
let entries:[Entry]
init(json: JSON) throws {
self.entries = try json.arrayOf("entry", type:Entry.self)
}
}
struct Entry:JSONDecodable {
let name:IMName
init(json: JSON) throws {
self.name = try json.decode("im:name", type:IMName.self)
}
}
struct IMName:JSONDecodable {
let label:String
init(json: JSON) throws {
self.label = try json.string("label")
}
}

Extract value from dictionary of annoying format

I apologise for the title of this question. I have no idea what else to call it.
So... When calling the following:
let testData: [NSObject : AnyObject] = getTestData()
print(testData)
I get this output:
[data: {"TypeId":7,"DataList":null,"TypeName":"This is a test"}]
How would I be able to access the value 7 for the key "TypeId"?
EDIT:
Please note that it's holding { } brackets, not only [ ], thus a cast to NSDictionary is not possible as far as I have tried.
Kind regards,
Anders
You can achieve plist-like nested structures using Any type for dictionary values which is Swift's somewhat counterpart to Objective-C's id type but can also hold value types.
var response = Dictionary()
response["user"] = ["Login": "Power Ranger", "Password": "Mighty Morfin'"]
response["status"] = 200
Any seems to be better than AnyObject because in the above code response["status"] is of type Swift.Int, while using value type of AnyObject it is __NSCFNumber.
The way most people do it is to parse annoying JSON data as custom objects. That should be done as soon as you get the JSON. Ideally, data as JSON should not be used outside your communication code, example:
First, let's define a class to hold your server data:
class MyServerObject {
let typeId: Int
let typeName: String
let dataList: [AnyObject]?
init(dictionary: Dictionary<String, AnyObject>) {
let dataDictionary = dictionary["data"] as! Dictionary<String, AnyObject>
self.typeId = dataDictionary["TypeId"] as! Int
self.typeName = dataDictionary["TypeName"] as! String
self.dataList = dataDictionary["DataList"] as? [AnyObject]
}
}
Note that init method is already parsing the JSON. This doesn't have to be done in init, you could also create a static parse method that will return a new instance.
Usage:
// demo data
let jsonString = "{\"data\": {\"TypeId\":7,\"DataList\":null,\"TypeName\":\"This is a test\"}}"
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
let json = try! NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// parsing
let myServerObject = MyServerObject(dictionary: json as! Dictionary<String, AnyObject>)
// now we can simply read data as properties
print(myServerObject.typeId)
print(myServerObject.typeName)
One of the good thing about this solution is that we can check the JSON format and all the properties are parsed with the correct types.
Parsing can be hierarchical, for example, if your dataList contains complex objects, let's call them DataListItem, your parsing method can parse each item separately and put them into a [DataListItem], e.g.
if let dataListJSON = dataDictionary["DataList"] as? [Dictionary<String, AnyObject>] {
self.dataList = dataListJSON.map({ DataListItem($0) })
}
Also note that when parsing as! will crash the app when the format is invalid. as? will return nil if the types don't match. as? is very useful for types that can be nil because they are parsed as NSNull instances.
taking in account your data ...
print(testData)
/*
[data: {
DataList = null;
TypeId = 7;
TypeName = "This is a test";
}]
*/
// DataList type should be declared somewhere
class DataList {}
// parse data or set default value, if 'key' doesn't exist
if let data = testData["data"] as? [String:AnyObject] {
let dataList = data["DataList"] as? DataList // nil
let typeId = data["TypeId"] as? Int ?? 0 // 7
let typeName = data["TypeName"] as? String ?? "" // This is test
}

Accessing devices data from Facebook graph Api request in swift

I have made a graph request using the FBSDK in my swift application for iphone and I am having some difficulty accessing some of the information it has returned in the result. Specifically I want to get the list of device platforms the user uses. Playing around with the Graph api explorer I get this data for a query on a user's devices.
{
"devices": [
{
"os": "iOS"
}
],
"id": "12345678912345"
}
but in swift the data returned is in this format when I print the graph results value to console:
{
devices = (
{
os = iOS;
}
);
}
so my question is, how do I get the value of 'os' in swift? all my attempts casting the data to NSDictionaries and NSArrays etc have all failed.
let listOfDevices: String = result.valueForKey("devices") as? String
outputs
devices = (
{
os = iOS;
}
);
which just goes to show it doesn't contain any key/pair values for an NSDictionary when I search for ["os"] because of those"()" parenthesis. All help appreciated. Probably something really simple...
I don't want to use string regex.
listOfDevices should be a dictionary.
Swift 2
do {
// Replicating your data for the example
let response = "{\"devices\": [{\"os\": \"iOS\"}],\"id\": \"12345678912345\"}"
// Cast the response as a Dictionary with String as key and AnyObject as value
if let data = response.dataUsingEncoding(NSUTF8StringEncoding),
listOfDevices = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject] {
// Value of `devices` is an array of dictionaries
if let devices = listOfDevices["devices"] as? [[String:AnyObject]] {
for device in devices {
if let os = device["os"] as? String {
print(os)
}
}
}
// Value of `id` is a String
if let id = listOfDevices["id"] as? String {
// use `id`
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
Swift 1
// Replicating your data for the example
let response = "{\"devices\": [{\"os\": \"iOS\"}],\"id\": \"12345678912345\"}"
let data = response.dataUsingEncoding(NSUTF8StringEncoding)
// Cast the response as a Dictionary with String as key and AnyObject as value
let listOfDevices = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as! [String:AnyObject]
// Value of `devices` is an array of dictionaries
if let devices = listOfDevices["devices"] as? [[String:AnyObject]] {
for device in devices {
if let os = device["os"] as? String {
println(os)
}
}
}
// Value of `id` is a String
if let id = listOfDevices["id"] as? String {
// use `id`
}

Resources