Parsing JSON in Swift with Alamofire - ios

I'm having trouble trying to figure out how to return only one part of my JSON data using Swift 4.
This is the JSON I need to parse:
{
"code": 0,
"responseTS": 1571969400172,
"message": "TagPosition",
"version": "2.1",
"command": "http://123.456.7.89:8080/tag=a4da22e02925",
"tags": [
{
"smoothedPosition": [
-0.58,
-3.57,
0.2
],
"color": "#FF0000",
"positionAccuracy": 0.07,
"smoothedPositionAccuracy": 0.07,
"zones": [],
"coordinateSystemId": "687eba45-7af4-4b7d-96ed-df709ec1ced1",
"areaId": "987537ae-42f3-4bb5-8d0c-79fba8752ef4",
"coordinateSystemName": "CoordSys001",
"covarianceMatrix": [
0.04,
0.01,
0.01,
0.05
],
"areaName": "area",
"name": null,
"positionTS": 1571969399065,
"id": "a4da22e02925",
"position": [
-0.58,
-3.57,
0.2
]
}
],
"status": "Ok"
}
So far I am able to return all of the "tags" array, shown below. However, I just need to return only the "smoothedPosition" data.
func newTest() {
Alamofire.request(url).responseJSON { (response) in
if let newjson = response.result.value as! [String: Any]? {
print(newjson["tags"] as! NSArray)
}
}
}
Would alamofire be a good way to get the result I want? I previously tried with the Codable method but because there are many different parts to my JSON, I found it confusing to just get the part I need. If anyone could give me some advice on the best way to go about this, I would appreciate it.

Better not to use NS classes with Swift wherever possible. So instead of NSArray use Array.
To get smoothedPosition you need to parse more. tags gives you an array of dictionaries, so you need to loop array and get each dictionary inside tags array. Then finally you can get your smoothedPosition array.
func newTest() {
Alamofire.request(url).responseJSON { (response) in
if let newjson = response.result.value as? [String: Any], if let tagArray = newjson["tags"] as? [[String: Any]] {
for object in tagArray {
if let smoothedPosition = object["smoothedPosition"] as? [Double] {
print(smoothedPosition)
}
}
}
}
}
Also you should read more about codables to parse nested data and learn about optional binding and chaining to prevent crashes when you force (!) something which could be nil at some point.
You can use this site to check the possible Codable structure as per your response: https://app.quicktype.io/

It is a good option to create a custom Codable struct or class for the JSON response. You may only implement as member variable which you want to reach.
struct CustomResponse: Codable {
let tags: [Tag]
}
struct Tag: Codable {
let smoothedPosition: Position
}
struct Position: Codable {
let x: Float
let y: Float
let z: Float
}
Then
Alamofire.request(url).responseJSON { (response as? CustomResponse) in
guard let response = response else { return }
print(response.tags.smoothedPosition)
}
Also you can parse deeper manually your JSON response as stated in the other answers.

Related

Unable to get value from JSON array of dictionaries in swift

json response:
"result": {
"user_images": [
{
"id": 113,
"user_id": "160",
"image": "1617349564.jpg",
"image_title": "33"
},
{
"id": 112,
"user_id": "160",
"image": "1617349541.jpg",
"image_title": "22"
},
{
"id": 111,
"user_id": "160",
"image": "1617349528.jpg",
"image_title": "11"
},
........
code: with this code i am getting response like above means all user_images array coming... but here i need image_title how to get that.. if i run for loop getting error.. pls do help
if let code = ((response.dict?["result"] as? [String : Any])){
let userImages = code["user_images"] as? [String : Any]
}
how to get image_title value from above array of dictionaries
Sol 1
if let code = response.dict?["result"] as? [String : Any] {
if let userImages = code["user_images"] as? [[String : Any]] {
for item in userImages {
print(item["image_title"])
}
}
}
Sol 2
if let code = response.dict?["result"] as? [String : Any] {
do {
let data = try JSONSerialization.data(withJSONObject: code)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode(Result.self, from: data)
let titles = res.userImages.map { $0.imageTitle }
print(titles)
}
catch {
print(error)
}
}
// MARK: - Result
struct Result: Codable {
let userImages: [UserImage]
}
// MARK: - UserImage
struct UserImage: Codable {
let id: Int
let userId, image, imageTitle: String
}
Code from #Sh_Khan
if let code = response.dict?["result"] as? [String : Any] {
//You're using "as?" which means you're casting as an optional type.
//One simple solution to this is here where it's unwrapped using "if let"
if let userImages = code["user_images"] as? [[String : Any]] {
// [String:Any] is a Dictionary
// [[String:Any]] is an Array of Dictionary
for item in userImages {
print(item["image_title"])
}
} else {
// We didn't unwrap this safely at all, do something else.
}
Let's dive into this a little bit. This structure is a JSON Object
{
"id": 113,
"user_id": "160",
"image": "1617349564.jpg",
"image_title": "33"
}
But it's only a JSON object when it stands alone. Adding a key, or in this example user_images makes it a dictionary. Notice that the [ is not wrapped around it. Meaning it's a standalone dictionary. If this was your object, and this alone, your original code would work, but you're dealing with an Array of Dictionaries.
"user_images":
{
"id": 113,
"user_id": "160",
"image": "1617349564.jpg",
"image_title": "33"
}
This line of code essentially means that you're expecting to get back that Array of Dictionary. Bear in mind, each value for the dictionary is not an array value, which is why you don't see something like this [[[String: Any]]] because the data isn't nested like that.
if let userImages = code["user_images"] as? [[String : Any]]
What's this about optionals?
An optional is basically a nil possible value that can be returned. Typically when working with JSON you cannot guarantee that you'll always receive a value for a given key. It's even possible for a key value pair to be completely missing. If that were to happen you'd end up with a crash, because it's not handled. Here are the most common ways to handle Optionals
var someString: String? //This is the optional one
var someOtherString = "Hello, World!" //Non-optional
if let unwrappedString1 = someString {
//This code will never be reached
} else {
//This code will, because it can't be unwrapped.
}
guard let unwrappedString2 = someString else {
//This code block will run
return //Could also be continue, break, or return someValue
}
//The code will never make it here.
print(someOtherString)
Furthermore, you can work with optionals by chain unwrapping them which is a nifty feature.
var someString: String?
var someInt: Int?
var someBool: Bool?
someString = "Hello, World!"
//someString is not nil, but an important distinction to make, if any
//fail, ALL fail.
if let safeString = someString,
let safeInt = someInt,
let safeBool = someBool {
//If the values are unwrapped safely, they will be accessible here.
//In this case, they are nil, so this block will never be hit.
//I make this point because of scope, the guard statement saves you from the
//scoping issue present in if let unwrapping.
print(safeString)
print(safeInt)
print(safeBool)
}
guard let safeString = someString,
let safeInt = someInt,
let safeBool = someBool {
//This will be hit if a value is null
return
}
//However notice the scope is available outside of the guard statement,
//meaning you can safely use the values now without them being contained
//to an if statement. Despite this example, they would never be hit.
print(safeString)
print(safeInt)
print(safeBool)

Values of Array of struct are nil

I am pretty new to Swift and as an exercise I try to create an App with Swift 5, which should show you the Weather at a Location you searched for.
Right now I am trying to implement a function that can turn a local JSON file in a struct I am using.
The JSON file is located at the same directory as all the other files.
The JSON for testing purposes looks like this:
[
{
"id": 833,
"name": "Ḩeşār-e Sefīd",
"state": "",
"country": "IR",
"coord": {
"lon": 47.159401,
"lat": 34.330502
}
},
{
"id": 2960,
"name": "‘Ayn Ḩalāqīm",
"state": "",
"country": "SY",
"coord": {
"lon": 36.321911,
"lat": 34.940079
}
}
]
The struct:
struct Root: Codable {
let jsonWeather: [JsonWeather]?
}
struct JsonWeather: Codable {
let id: Int
let name: String
let state: String
let country: String
let coord: Coord
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case state = "state"
case country = "country"
case coord = "coord"
}
}
The function that i am working on:
func loadJson(fileName: String) -> Void{
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let jsonData = try JSONDecoder().decode(Array<Root>.self, from: data)
print(type(of: jsonData)) // Array<Root>
print(jsonData) // [WeatherApp.Root(jsonWeather: nil), WeatherApp.Root(jsonWeather: nil)]
} catch {
print("error:\(error)")
}
}
}
After all I wanted to see how the result looks like and I noticed that each jsonWeather in Root is nil
So in the end I understand that the values are nil because I allow them to be nil by setting an optional in the Root struct, but I dont quite understand why they are turning nil because there is data given. Also I don't know how I would implement it without being an optional.
This is my first time using JSONDecoder and Optionals in Swift.
Could anyone point out what I did wrong (or understood wrong) and how to fix this issue
Since your JSON is just of array of objects, you don't need a top-level struct like Root.
Get rid of your Root struct, and just decode an array of JsonWeathers:
let jsonData = try JSONDecoder().decode([JsonWeather].self, from: data)
(Note that there is no difference between [JsonWeather].self and Array<JsonWeather>.self - I just chose to do it this way since it's shorter.)

Swift syntax 'case let' in for loop is confusing

If the goal is to have 'result' represent an object in the 'results' array, why not drop case let in the following and just have 'for result in' instead ? I don't understand why case let is required here.
if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
for case let result in json["results"] {
if let restaurant = Restaurant(json: result) {
restaurants.append(restaurant)
}
}
}
JSON
{
"query": "sandwich",
"results_count": 12,
"page": 1,
"results": [
{
"name": "Caffè Macs",
"coordinates": {
"lat": 37.330576,
"lng": -122.029739
},
"meals": ["breakfast", "lunch", "dinner"]
},
...
]
}
REF: https://developer.apple.com/swift/blog/
In your case there is no need to use case let with for loop, because you want all objects of results response, If you want to understand how case let is use check this.
let albumDic = [
("Red", 2014),
("1989", 2014),
("Fearless", 2008),
("Speak Now", 2008)
]
for case let (album, 2014) in albumDic {
print("Album \(album) was released in 2014")
}
Output
Album Red was released in 2014
Album 1989 was released in 2014
Note: You can directly use for in loop no need to use case let since you want all the objects, so you need to write like this.
if let results = json["results"] as? [[String: Any]]{
for result in results {
}
}
For more details you can check this tutorial of for in.

How to move inside collection of nested dictionar Array JSON response using swift 2.0

I am totally new to swift and getting trouble access the array content which is inside two dictionary level
{
"responsecode": "200",
"responsemsg": "Product List",
"total_pages_count": "35",
"data": [
{
"pk_productid": 403,
"fk_userid": 59,
"productname": "Intencity",
"sku": "man-intencity403",
"description": "",
"shortdescription": "",
"prodsaletype": 1,
"prodprice": 325000,
"is_approve": 1,
"issold": false,
"issoldprice": 0,
"isbid": 0,
"lastbidprice": 325000,
"isdiscount": false,
"isfixeddiscount": false,
"discountamt": 0,
"ispromocode": false,
"isonline": false,
"iscash": true,
"images": [
{
"imagepath": "http://www.artively.com/Upload/ProdColorThumbImage/270120161509_Intensity_Fotor.jpg",
"imgvideotype": 1
}
],
...
..
.
I am able to access the response code as well as the second level ie SKU, description etc but how to access images array here i have to access the imagepath and show the image in uitableview
Here is my code
func parseJSONData(data: NSData) -> [ProductDetails] {
var product_Detail = [ProductDetails]()
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
let jsonProductDetails = jsonResult?["data"] as! [AnyObject]
print("the json response is",jsonProductDetails)
for jsonproductDetail in jsonProductDetails{
let productDetail = ProductDetails()
productDetail.productAuthor = jsonproductDetail["first_name"]as! String
productDetail.productPrice = jsonproductDetail["prodprice"]as! Int
product_Detail.append(productDetail)
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSArray
let jsonProductImageDetails = (jsonResult?["images"] as! [AnyObject]
print("the json response is",jsonProductImageDetails)
//How to access array
}
}
catch {
print (error)
}
return product_Detail
}
Thank you for your help
I think Caleb nailed the original problem, but wanted to add - I was trying to deal with some JSON in Swift the other day and realized that it's kind of a nightmare to unpack it. I found a Cocoapod called "SwiftyJSON" that makes it a lot easier....
https://github.com/SwiftyJSON/SwiftyJSON

Swift - Find the index of an array of dictionaries based on the value in the dictionaries using swiftyJSON

I am very new to programming, let alone Swift. I understand some concepts from previous attempts at programming so I have gotten further than ever before. I apologize if I am not clear in what I need.
I am pulling in JSON data of a list of alerts and I am trying to parse the data with swiftyJSON which I think is working out ok but I have run into a snag of trying to grab some data from some dictionaries within an array, and unfortunately inside a dictionary inside this arrary is where the email address is, and the different dictionaries use similar keys within each other.
I am going to show you my struct, fucntion and JSON data. Please help me grab the email address, service - its ID and its label. Also, there may be more than one Service as in the data shown and I need to capture all of them.
HERE IS THE JSON DATA:
{
"hasNext": false,
"data": [
{
"status": [
1,
"READ"
],
"resolutionStatus": [
0,
"OPEN"
],
"description": "There is some description here",
"title": "Some Activity",
"entities": [
{
"view_name": "audits",
"type": "link",
"parameters": {
"orgUnit": "/"
},
"label": "/"
},
{
"type": "user",
"id": "hidden#hidden.com",
"label": "hidden#hidden.com"
},
{
"type": "service",
"id": 6666,
"label": "someService"
},
{
"type": "service",
"id": 7777,
"label": "anotherService"
}
],
"stories": [
5
],
"date": "2014-12-10T23:46:28.067000Z",
"audits": [
"ljBhqKQVOF9w",
"pISQyT9iy9w",
"oynGf2_CIw"
],
"_id": "54fdad0dfd",
"id": [
14683,
"ALERT_SOME_ACTIVITY"
],
"severity": [
5,
"HIGH"
]
}
Here is my Struct:
struct AlertModel: Printable {
let alertUser: String?
let alertService: String?
let alertTitle: String?
let alertReadStatus: String?
let alertResolutionStatus: String?
let alertDescription: String?
let alertEntities: Array <String> = []
let alertDate: String?
let alertAudits: Array <String> = []
let alertId: String?
let
alertSeverity: String?
// change description to print to console
var description: String {
return "User: \(alertUser)\nService: \(alertService)\nTitle: \(alertTitle!)\nRead Status: \(alertReadStatus!)\nResolution Status: \(alertResolutionStatus!)\nDescription: \(alertDescription!)\nDate: \(alertDate!)\nAlert ID: \(alertId!)\nSeverity: \(alertSeverity!)\n******************************************\n"
}
init(alertUser: String?, alertService: String?, alertTitle: String?, alertReadStat
us: String?, alertResolutionStatus: String?, alertDescription: String?/*, alertEntities: Array<String>*/, alertDate: String?/*, alertAudits: Array<String>*/, alertId: String?, alertSeverity: String?) {
self.alertUser = alertUser
self.alertService = alertService
self.alertTitle = alertTitle
self.alertReadStatus = alertReadStatus
self.alertResolutionStatus = alertResolutionStatus
self.alertDescription = alertDescription
//self.alertEntities = alertEntities
self.alertDate = alertDate
//self.alertAudits = alertAudits
self.alertId = alertId
self.alertSeverity = alertSeverity
}
AND HERE IS THE FUNCTION:
let jsonAlert = JSON(data: jsonAlertObject)
if let alertArray = jsonAlert["data"].array {
var alerts = [AlertModel]()
for alertDict in alertArray {
let alertTitle: String? = alertDict["title"].stringValue
let alertReadStatus: String? = alertDict["status"][1].stringValue
let alertResolutionStatus: String? = alertDict["resolutionStatus"][1].stringValue
let alertDescription: String? = alertDict["description"].stringValue
let alertDate: String? = alertDict["date"].stringValue
let alertId: String? = alertDict["_id"].stringValue
// Need to grab the type and label from each dictionary in the array of entities
let alertEntitiesArray: Array? = alertDict["entities"].arrayObject
var arrayIndex = 0
var entitiesDict = ["" : ""]
while arrayIndex < alertEntitiesArray?.count {
entitiesDict[alertDict["entities"][arrayIndex]["type"].stringValue] = alertDict["entities"][arrayIndex]["label"].stringValue
arrayIndex++
}
let alertService: String? = entitiesDict["service"]
let alertUser: String? = entitiesDict["user"]
let alertSeverity: String? = alertDict["severity"][1].stringValue
let alert = AlertModel(alertUser: alertUser, alertService: alertService, alertTitle: alertTitle, alertReadStatus: alertReadStatus, alertResolutionStatus: alertResolutionStatus, alertDescription: alertDescription, alertDate: alertDate, alertId: alertId, alertSeverity: alertSeverity)
alerts.append(alert)
var alertsDictionaryByID = [alertId!: alert]
}
println(alerts)
}
As you can see the JSON data is a few levels deep. I have no problem getting to the data and pulling it out. The problem is the "Entities" array may not always have the same data in it. It my have multiple services, it may have no email address, it may have a completely different set of data for the first value of the array.
I am trying to get the email address out. If I could find a way to search for the "user" as with the dictionary data and then when found it would return the array index value to be able to reference it directly because I will never know with index number the user value is part of.
I hope I came across clear enough and someone can help me. --- my next step will be populating a listView with each individual alert.
I was also using SwiftyJSON, but using NSURLConnection was easy. Like this method.
//MARK: - NSURLConnection Delegate methods
var responseData : NSMutableData = NSMutableData()
func connection(connection: NSURLConnection!, didReceiveResponse response: NSURLResponse!)
{
self.responseData.length = 0
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!)
{
self.responseData.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!)
{
if let responseDatas = responseData as NSData? {
if let jsonResult : NSDictionary = NSJSONSerialization.JSONObjectWithData(responseDatas, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary? {
if let dataArray = jsonResult.valueForKeyPath("data") as? NSArray
{
if let entitiesArray = dataArray.valueForKeyPath("entities") as? NSArray
{
if let firstArray = dataArray[0]
{
// access your viewName and types here
}
// Like this method, convert all your parameters and access it.
}
}
else
{
// Do something here
}
}
else
{
// Do something here
}
}
else
{
// Do something here
}
}
func connection(connection: NSURLConnection!, didFailWithError error: NSError!)
{
// Do something here
}
I ended up finding SwiftyJSON and Alamofire to significantly assist with the networking and JSON Serialization.
As far as my issue with the dictionaries within the array I ended up creating a function that iterated through the entities array looking at each dictionary seperately and then performed a Switch statement based on the key="type" to determine if it was the "user" dictionary or the "service" dictionary and then combined the 'type' value, which said if it was a user or a service, with the value of "label", which is the the username or service name, and created a new dictionary out of that which I could then reference to put back into my data model.
class func retrieveDataFromEntitiesArray (alertDict: JSON) -> (entitesDict: Dictionary<String, String>, servicesArray: [String]) {
// Need to create an array object, instead of a JSON object, of the entities array to be able to get a count to run the while against.
var arrayIndex = 0
var entitiesDict: Dictionary<String, String> = [:]
var alertEntitiesArray = alertDict.arrayObject
var servicesArray = [String]()
while arrayIndex < alertEntitiesArray?.count {
var dictKey = alertDict[arrayIndex]["type"].string
switch (dictKey!) {
case "user":
entitiesDict[alertDict[arrayIndex]["type"].stringValue] = alertDict[arrayIndex]["label"].stringValue
case "service":
servicesArray.append(alertDict[arrayIndex]["label"].stringValue)
case "policyRule":
entitiesDict[alertDict[arrayIndex]["type"].stringValue] = alertDict[arrayIndex]["label"].stringValue
default:
println("Nothing Here")
}
arrayIndex++
}
return (entitiesDict, servicesArray)
}

Resources