Swift syntax 'case let' in for loop is confusing - ios

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.

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)

Could not cast value of type 'Swift.__SwiftDeferredNSArray' (0x104cd8ae8) to 'NSMutableArray' (0x7fff87c52960)

Below line of code is producing the error,
let response1 : NSMutableArray = NSMutableArray.init(array: (JSON.object as! NSMutableArray).value(forKey: "media_list") as! NSArray)
As the error says I understand its a cast exception, but I'm not able to modify the code to make it work. I'm kinda new to Swift, so any help would be appreciated.
Below is my JSON.object
So, I checked and this is my JSON.object
[["offset": 30119146, "file_size": 30119146, "filename": video_220120201129271580.mp4, "mediaPath": file:///Users/evuser/Library/Developer/CoreSimulator/Devices/B9B0232F-237D-4413-BB81-BD5FAC727305/data/Containers/Data/Application/401D5D91-4500-434A-98FE-BD416135A1C7/Documents/video_220120201129271580.mp4, "status": completed, "group_id": fKQ2Xd9bE0cXchsw, "createdDate": 2020/01/22 13:59:47, "_id": 5e27e4d3138c8801cd3c26ca, "user_id": 21, "mime_type": video/mp4, "dest_path": /video_220120201129271580.mp4, "resource_id": 3a743d84-eafe-41e5-9f4c-dece67598c32],
["offset": 6435018, "file_size": 6435018, "filename": video_220120201127525480.mp4, "mediaPath": file:///Users/evuser/Library/Developer/CoreSimulator/Devices/B9B0232F-237D-4413-BB81-BD5FAC727305/data/Containers/Data/Application/401D5D91-4500-434A-98FE-BD416135A1C7/Documents/video_220120201127525480.mp4, "status": completed, "group_id": ffoHuGL0Z17vOqY9, "createdDate": 2020/01/22 13:58:10, "_id": 5e27e472138c8801cd3c26c9, "user_id": 21, "mime_type": video/mp4, "dest_path": /video_220120201127525480.mp4, "resource_id": 50e34fd5-b488-4861-aedd-03ea1ed0d91c]]
It seems that JSON.object may not be an array. Or at least not mutable array. It will be hard for us to identify your issue without having a look into JSON.object. A quick fix may actually be
let response1 : NSMutableArray = NSMutableArray.init(array: (JSON.object as! NSArray).value(forKey: "media_list") as! NSArray)
but I would try to dig in a bit more. Try to check what exactly is going on and try to avoid old Objective-C Next Step (NS) objects. Do it step by step:
let response1: [Any]? = {
guard let mainArray = JSON.object as? [Any] else {
print("Outer object is not an array. Check type of \(JSON.object)")
return nil
}
var mutableVersionOfArray = mainArray // This already creates a mutable copy because we used "var" instead of "let"
guard let mediaList = mutableVersionOfArray.value(forKey: "media_list") as? [Any] else {
print("Inner object is not an array. Check type of \(mutableVersionOfArray.value(forKey: "media_list"))")
return nil
}
return mediaList
}()
But this code makes no sense to me. Looking at your code I expect that your JSON object looks similar to:
{
"media_list": [{}, {}]
}
in this case you are looking at dictionaries. Try the following:
let mediaList: [Any]? = {
guard let topDictionary = JSON.object as? [String: Any] else {
print("Outer object is not a dictionary. Check type of \(JSON.object)")
return nil
}
guard let mediaListItem = topDictionary["media_list"] else {
print("There is no media_list in payload")
return nil
}
guard let mediaList = mediaListItem as? [Any] else {
print("mediaList is not an array")
return nil
}
return mediaList
}
I hope you can see the difference between an array and a dictionary. Array has some N ordered elements in it while a dictionary has key-value pairs. So to access a value under key you call it as dictionary[key]. Your whole code if you are correct could simply be:
let response1 = (JSON.object as? [String: Any])?["media_list"] as? [Any]
but if it returns nil it may be a bit hard to debug what went wrong.

Parsing JSON in Swift with Alamofire

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.

Create array of Json Data Items

I have a very long Json array that is full of items that look like this:
[
{
"id": "sm10-1",
"name": "Pheromosa & Buzzwole-GX",
"imageUrl": "https://images.pokemontcg.io/sm10/1.png",
"subtype": "TAG TEAM",
"supertype": "Pokémon",
"hp": "260",
"retreatCost": [
"Colorless",
"Colorless"
],
"convertedRetreatCost": 2,
"number": "1",
"artist": "Mitsuhiro Arita",
"rarity": "Rare Holo GX",
"series": "Sun & Moon",
"set": "Unbroken Bonds",
"setCode": "sm10",
"text": [
"When your TAG TEAM is knocked out, your opponent takes 3 Prize Cards."
],
"types": [
"Grass"
],
"attacks": [
{
"name": "Jet Punch",
"cost": [
"Grass"
],
"convertedEnergyCost": 1,
"damage": "30",
"text": "This attack does 30 damage to 1 of your opponent's Benched Pokémon. (Don't apply Weakness and Resistance for Benched Pokémon.)"
},
{
"name": "Elegant Sole",
"cost": [
"Grass",
"Grass",
"Colorless"
],
"convertedEnergyCost": 3,
"damage": "190",
"text": "During your next turn, this Pokémon's Elegant Sole attack's base damage is 60."
},
{
"name": "Beast Game-GX",
"cost": [
"Grass"
],
"convertedEnergyCost": 1,
"damage": "50",
"text": "If your opponent's Pokémon is Knocked Out by damage from this attack, take 1 more Prize card. If this Pokémon has at least 7 extra Energy attached to it (in addition to this attack's cost), take 3 more Prize cards instead. (You can't use more than 1 GX attack in a game.)"
}
],
"weaknesses": [
{
"type": "Fire",
"value": "×2"
}
],
"imageUrlHiRes": "https://images.pokemontcg.io/sm10/1_hires.png",
"nationalPokedexNumber": 794
}
]
That is just one item of hundreds in the array. What I want to do is grab specific values from each item (i.e. name, imageUrl, supertype, hp, rarity, set) and send them to a struct which will then be added to an array of such structs.
What I currently have prints just prints out all of the json data and I can not figure out how to get individual data and create an array of structs for each individual card.
Here is the code I have currently:
//[TEST] READING JSON FILE LOCALLY
struct card: Decodable {
let name: String
let imageUrl: String
let supertype: String
let artist: String
let rarity: String
let set: String
let types: Array<String>
}
func loadJsonInfo() {
do{
let data = try Data.init(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "Unbroken Bonds", ofType: "json")!))
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print(error)
}
}
Also, the json file is locally stored in my appData. Thanks in advance for your help!
Give a try to https://quicktype.io/
You put json there. And get all necessary data structures to decode json
To decode a JSON with Decodable type, you need to use JSONDecoder's decode(_:from:) method.
Update your loadJsonInfo() method to,
func loadJsonInfo() {
if let file = Bundle.main.url(forResource: "Unbroken Bonds", withExtension: "json") {
do {
let data = try Data(contentsOf: file)
let arr = try JSONDecoder().decode([Card].self, from: data)
print(arr)
} catch {
print(error)
}
}
}
Note: Use first letter capital while creating a type, i.e. use Card instead of card.
Parsing Code when you have DATA from server. I also removed force unwrapped ! so it won't crash in absence of file
func loadJsonInfo() {
if let path = Bundle.main.path(forResource: "Unbroken Bonds", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let result = try JSONDecoder().decode(ResultElement.self, from: data)
} catch let error {
print(error)
}
}
}
Your Model
import Foundation
// MARK: - ResultElement
struct ResultElement: Codable {
let id, name: String?
let imageURL: String?
let subtype, supertype, hp: String?
let retreatCost: [String]?
let convertedRetreatCost: Int?
let number, artist, rarity, series: String?
let resultSet, setCode: String?
let text, types: [String]?
let attacks: [Attack]?
let weaknesses: [Weakness]?
let imageURLHiRes: String?
let nationalPokedexNumber: Int?
enum CodingKeys: String, CodingKey {
case id, name
case imageURL = "imageUrl"
case subtype, supertype, hp, retreatCost, convertedRetreatCost, number, artist, rarity, series
case resultSet = "set"
case setCode, text, types, attacks, weaknesses
case imageURLHiRes = "imageUrlHiRes"
case nationalPokedexNumber
}
}
// MARK: - Attack
struct Attack: Codable {
let name: String?
let cost: [String]?
let convertedEnergyCost: Int?
let damage, text: String?
}
// MARK: - Weakness
struct Weakness: Codable {
let type, value: String?
}
typealias Result = [ResultElement]

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

Resources