Unable to get value from JSON array of dictionaries in swift - ios

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)

Related

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.

Change JSON value for a key which can be nested in Swift

I don't know if there is a quicker way or a function that's already implemented for my issue. I have a nested dynamic JSON with unique keys.
{
"Key1": true,
"Key2": false,
"Key3": "just a string",
"Key4": {
"Key5": "just a string",
"Key6": 51,
"Key7": {
"Key8": "this value has to be changed"
}
}
}
Is there any Swift function or a package that could do this easily like that:
let updatedJSON = originalJSON.update(key: "Key8", with: "---")
So the updatedJSON will be the same only "Key8" will be updated. Take into consideration that I don't know the hierarchy. The JSON key positions may wary. Sometimes Key8 can be at the root of the object.
Not knowing the structure of your JSON complicates this problem a bit, but it can still be solved. I've come up with an idea to recursively search through a Dictionary
extension Dictionary
{
mutating func updateValueRecursively(forKey key : Key, newValue: Value) -> Bool
{
if keys.contains(key)
{
self[key] = newValue
return true
}
for (_, value) in enumerated()
{
if var dict = value.value as? Dictionary<Key, Value> {
if dict.updateValueRecursively(forKey: value.key, newValue: newValue), let newDict = dict as? Value {
self[value.key] = newDict
return true
}
}
}
return false
}
}
Of course you will first have to turn your JSON string to a Dictionary
var dict : Dictionary<String : Any>
if let data = jsonString.data(using: .utf8) {
do {
dict= try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}

how to add Json value into model Array to display into tableview in swift

I'm using the tableview to display the Two Json value but the problem is I cant add value into model struct to displaying into tableview using two Api's. i want to show percentage value in one of the cell label and
here is my json
[
{
"Percentage": 99.792098999,
}
]
my second json value
{
"Categories": [
"Developer",
"ios "
],
"Tags": [
{
"Value": "kishore",
"Key": "Name"
},
{
"Value": "2",
"Key": "office"
},
]
}
and i need show the Categories value in Categories label in tableview
value and key on tableview
here is my Struct
struct info: Decodable {
let Categories: String?
let Tags: String?
let Value: String?
let Key: String?
var Name: String?
let percentage: Double?
}
here its my code
var List = [info]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
print(json as Any)
guard let jsonArray = json as? [[String: Any]] else {
return
}
print(jsonArray)
for dic in jsonArray{
guard let per = dic["percentage"] as? Double else { return }
print(per)
}
and second json
if let array = json["Tags"] as? [[String: String]] {
for dict in array {
let key = dict["Key"]
let value = dict["Value"]
switch key {
case "office":
case "Name":
default:
break;
}
}
here is my cell for row indexpath
cell.Categories.text = list[indexpath.row].percentage
cell.Name.text = list[indexpath.row].name
cell.office.text = list[indexpath.row].office
Please use Swift 4 Codable protocol to decode the value from JSON.
//1.0 Create your structures and make it conform to Codable Protocol
struct Tags: Codable{
var Key: String
var Value: String
}
struct Sample: Codable{
var Categories: [String]
var Tags: [Tags]
}
In your method, perform below steps:
//2.0 Get your json data from your API. In example below, i am reading from a JSON file named "Sample.json"
if let path = Bundle.main.path(forResource: "Sample", ofType: "json") {
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
do {
//3.0 use JSONDecoder's decode method to decode JSON to your model.
let sample = try JSONDecoder().decode(Sample.self, from: jsonData)
//4.0 User the "sample" model to do your stuff. Example, printing some values
print("Sample.Category = \(sample.Categories)")
print("Sample.Name = \(sample.Tags[0].Value)")
print("Sample.Office = \(sample.Tags[1].Value)")
} catch let error {
print("Error = \(error)")
}
} catch {
// handle error
}
}
I prefer to use Codable all the time with JSON even for simpler types so for percentage I would do
struct ItemElement: Decodable {
let percentage: Double
enum CodingKeys: String, CodingKey {
case percentage = "Percentage"
}
}
and we need to keep these values in a separate array, declared as a class property
let percentageList: [Double]()
and json encoding would then be
let decoder = JSONDecoder()
do {
let result = try decoder.decode([ItemElement].self, from: data)
percentageList = result.map { item.percentage }
} catch {
print(error)
}
Similar for the second part
struct Item: Decodable {
let categories: [String]
let tags: [Tag]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
case tags = "Tags"
}
}
struct Tag: Decodable {
let value, key: String
enum CodingKeys: String, CodingKey {
case value = "Value"
case key = "Key"
}
}
use a dictionary for the result, again as a class property
var values = [String: String]()
and the decoding
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Item.self, from: data)
for item in result.tags {
values[item.key] = values.item.value
}
} catch {
print(error)
}
and then in the cell for row code
cell.Categories.text = percentageList[indexpath.row].percentage
cell.Name.text = values["name"]
cell.office.text = values["office"]
Note that this last code looks very strange since you don't have an array of name/office values judging by your json. Maybe you have simplified it some way but the code above is the best I can do with the information given even if it possibly wrong

If a value in a json array returns null, how to return the next non-null json value?

Currently I'm trying to return values from JSON data but am running into an issue where one of the values is returning null thereby causing my app to crash.
This is what the json looks like:
"results": [
{
"genre_ids": [
99
],
"id": 440108,
"poster_path": null, //The value that's causing incredible headaches!
},
{
"genre_ids": [
99,
10402
],
"id": 391698,
"poster_path": "/uv7syi4vRyjvWoB8qExbqnbuCu5.jpg",//The value trying to get!
},
]
I'm using the json object mapper Gloss which has been good to this point. Here is how I have my objects set up:
public struct ResultsGenrePosters: Decodable {
public let results : [GenrePosters]?
public init?(json: JSON) {
results = "results" <~~ json
}
}
public struct GenrePosters: Decodable, Equatable{
public let poster : String
public init? (json: JSON) {
guard let poster: String = "poster_path" <~~ json
else {return nil}
self.poster = poster
}
public static func ==(lhs: GenrePosters, rhs: GenrePosters) -> Bool {
return lhs.poster == rhs.poster
}
static func updateGenrePoster(genreID: NSNumber, urlExtension: String, completionHandler:#escaping (_ details: [String]) -> Void){
let nm = NetworkManager.sharedManager
nm.getJSONData(type:"genre/\(genreID)", urlExtension: urlExtension, completion: {
data in
if let jsonDictionary = nm.parseJSONData(data)
{
guard let genrePosters = ResultsGenrePosters(json: jsonDictionary)
else {
print("Error initializing object")
return
}
guard let posters = genrePosters.results
else {
print("No poster exists for genre: \(genreID)")// This keeps on triggering when the null object is hit, this is where it would be good to move to the next array to get that value
return
}
let postersArray = posters.map {$0.poster}// converts custom object "GenrePosters" to String(poster value)
completionHandler(postersArray)
}
})
}
}
guard let poster: String = "poster_path" <~~ json
should be
guard let poster = ("poster_path" <~~ json) as? String
(Of course you are using a library that I know nothing about, so the library could be crashing. JSONSerialization is your friend. It works. Everyone knows what it does).
for data in results as! [Dictionary<String,AnyObjects>]{
var value1 = data["key"] as? String
if value1 == nil{value1 = ""}else{}
}
}
That was just a simple logic but completely work for me all the time. :)

How to parse such type array?

I got the next when I'm trying to do:
for item in serverResponse {
print(item)
}
it gives me:
("0", {
"id" : 1,
"name" : "Programming"
})
("1", {
"id" : 2,
"name" : "Music"
})
How can I parse this array to take name there? I've confused with it =/
UPDATE
The full response is:
{
"name": "ABC",
"books": [
{
"id": 1,
"name": "Programming"
}
{
"id": 2,
"name": "Music"
}
]
}
You are looking to convert Json to an object to read name. Crude solution would be to parse for name. Better way of doing it is use SwiftyJson or ObjectMapper.
If you get Json from your server you can use it:
func getJSON(urlToRequest: String) -> NSData{
return NSData(contentsOfURL: NSURL(string: urlToRequest)!)!
}
func parseJSON(inputData: NSData) -> NSArray{
let boardsDictionary = try! NSJSONSerialization.JSONObjectWithData(inputData, options: .AllowFragments) as! NSArray
return boardsDictionary
}
Then use myData = parseJSON(json) and you can access name with
let data = myData[0] as! NSDictionary
let name = data["name"]! as? String
for item in serverResponse {
let itemArray = (item as? NSDictionary)["books"] as? NSArray
for (var i = 0; i < itemArray.count ; i++ ) {
if let dic1 = itemArray[i] as? NSDictionary {
let item: String!
item = dic1["name"] as? String
print(item)
}
}
}
I don't like dealing with dictionaries in Swift. I find that objects behave better and are easier to manage - they're also safer.
So my suggestion is to parse your response into "Book" structs, then you can access the properties of these structs (id, name) easily.
It could be even better to make a class that does all the work.
For testing purposes I've reproduced your response like this:
let response: NSDictionary = ["name": "ABC", "books": [["id": 1, "name": "Programming"], ["id": 2, "name": "Music"]]]
Now let's first make our Book struct.
It will have the two properties, and also an initializer which will decode the content of each dictionary that's located in the array in the "books" key of your response. The initializer is failable because the content might not be decodable.
struct Book {
let id: Int
let name: String
init?(_ object: AnyObject) {
if let dictionary = object as? NSDictionary,
dictId = dictionary["id"] as? Int,
dictName = dictionary["name"] as? String {
self.id = dictId
self.name = dictName
} else {
return nil
}
}
}
Now we can make a class that will hold all the books. We need another initializer for that, which will iterate over the contents and create the objects with the structs:
class BooksManager {
var books = [Book]()
init(response: NSDictionary) {
if let array = response["books"] as? NSArray {
self.books = array.flatMap { Book($0) }
}
}
}
Ok, now we have finished writing the boilerplate code, we can use our toys.
Create an instance of the class:
let manager = BooksManager(response: response)
That's it! Now the class holds the objects and you can easily access their properties.
For example with a loop:
for book in manager.books {
print("Book id: \(book.id), book name: \(book.name)")
}
Result:
Book id: 1, book name: Programming
Book id: 2, book name: Music
And let's say you want the name of the first book:
if let nameOfFirstBook = manager.books.first?.name {
print(nameOfFirstBook)
}
Result:
Programming
Last example, let's say you want to collect all books names in an array:
let booksNames = manager.books.map { $0.name }
Result:
["Programming", "Music"]

Resources