Accessing Nested Dictionary from API in Swift

accessing nested dictionary json in swift 5

I like using QuickType.io (https://app.quicktype.io) for this kind of thing. It gives this for the JSON structure:

struct WelcomeElement: Codable {
let id, title: String
let countryID: CountryID
let v: Int

enum CodingKeys: String, CodingKey {
case id = "_id"
case title
case countryID = "countryId"
case v = "__v"
}
}

// MARK: - CountryID
struct CountryID: Codable {
let id, name: String
let v: Int

enum CodingKeys: String, CodingKey {
case id = "_id"
case name
case v = "__v"
}
}

And then decoding and accessing the internal elements is easy:

do {
let root = try JSONDecoder().decode([WelcomeElement].self, from: jsonData)
root.forEach { (element) in
print(element.countryID)
print(element.title)
}
} catch {
print(error)
}

You could continue to just use the untyped [String:Any] dictionaries, but it's not friendly and quite error prone:

do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)

guard let array = json as? [[String:Any]] else {
assertionFailure("Bad cast")
return
}

array.forEach { (element) in
print(element["title"] ?? "unknown")
if let countryId = element["countryId"] as? [String:String] {
print(countryId["name"] ?? "unknown")
}
}
} catch {
print(error)
}

Accessing Nested Dictionaries in JSON with Swift

If you are using Swift 4, there's Codable. Using Codable involves defining custom structs / classes for the JSON but apps like quicktype.io can make that a piece of cake: you just paste the JSON and it generates the structs for you.

First, the struct to hold the response from Wikipedia:

struct Response: Codable {
struct Query: Codable {
let random: [Random]
}

struct Random: Codable {
let id, ns: Int
let title: String
}

struct Continue: Codable {
let rncontinue, continueContinue: String

enum CodingKeys: String, CodingKey {
case rncontinue
case continueContinue = "continue"
}
}

let batchcomplete: String
let `continue`: Continue
let query: Query

enum CodingKeys: String, CodingKey {
case batchcomplete, `continue`, query
}
}

And decoding the JSON:

let url = URL(string: "https://en.wikipedia.org/w/api.php?action=query&list=random&rnlimit=1&format=json")
URLSession.shared.dataTask(with: url!) { (data, response, err) in
guard let data = data else { return }
guard err == nil else { print(err!); return }

do {
let response = try JSONDecoder().decode(Response.self, from: data)
if let firstArticle = response.query.random.first {
print(firstArticle)
} else {
print("No Article")
}
} catch {
print(error)
}
}.resume()

accessing nested dictionary from api in swift

Assuming data is NSDictionary, or [String:AnyObject]. You can:

let beds = data["floorplan_summary"]?["bedrooms"]??["formatted"] as? String // -> as String?
^

You need extra ? because data["floorplan_summary"]?["bedrooms"] returns AnyObject??. You have to unwrap it twice.

Why it returns AnyObject??? Because data["floorplan_summary"]? is AnyObject, and AnyObject may or may not have subscript. So, the first ? means, "If it has subscript", and the second means "If subscript returns non nil".

How to access deeply nested dictionaries in Swift

When working with dictionaries you have to remember that a key might not exist in the dictionary. For this reason, dictionaries always return optionals. So each time you access the dictionary by key you have to unwrap at each level as follows:

bugsDict["ladybug"]!["spotted"]!["red"]!++

I presume you know about optionals, but just to be clear, use the exclamation mark if you are 100% sure the key exists in the dictionary, otherwise it's better to use the question mark:

bugsDict["ladybug"]?["spotted"]?["red"]?++

Addendum: This is the code I used for testing in playground:

var colorsDict = [String : Int]()
var patternsDict = [String : [String : Int]] ()
var bugsDict = [String : [String : [String : Int]]] ()

colorsDict["red"] = 1
patternsDict["spotted"] = colorsDict
bugsDict["ladybug"] = patternsDict

bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 1
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 2
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 3
bugsDict["ladybug"]!["spotted"]!["red"]! // Prints 4

How to access a nested dictionaries from API

Your JSON response is Array not Dictionary and active_upcoming_bookings array and project dictionary is within the first dictionary of your response Array.

if let response = responseJSON as? [[String:Any]], let dictionary = response.first, 
let projectDic = dictionary["project"] as? [String:Any] {

//Now subscript with projectDic to access id and name
let projectId = projectDic["id"] as? Int
let projectName = projectDic["name"] as? String
}

struct for nested dictionary API in swift

You response in data parameter should be an array rather than a dictionary. You will not be able to iterate a dictionary over undefined keys. It would be good to get your response of API updated first.

But, If you wish to continue with the existing API response, first you need to convert your response in an array and use your Decodable structs as:

struct Coin: Decodable {
var id: String
var symbol : String
var name : String
var priceAUD : QuoteStruct

private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
}

struct QuoteStruct: Decodable {
let aud : PriceStruct
}

struct PriceStruct: Decodable {
let price : String
}

Update your data parsing in API block as:

    guard let responseData = data else { return }
do {
let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
if let jsonData = json as? [String: Any], let dataObject = jsonData["data"] as? [Int: Any] {
let coinArray = dataObject.map { $0.1 }

if let jsonData = try? JSONSerialization.data(withJSONObject: coinArray, options: .prettyPrinted) {
coins = try JSONDecoder().decode([Coin].self, from: jsonData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
} catch {
print("Error is : \n\(error)")
}

How to access nested dictionary?

print((params["message"] as! [String: Any])["title"] as! String)

You need to cast the Dictionary value as specific type, since the compiler doesn't know what to expect. (Please mind that you mustn't use force unwrap in other way than example code.)

Considering you need to fetch array values when recipients dictionary looks like this:

let recipients = ["custom_ids":["recipe1", "recipe2", "etc"]]

get to the ids like this:

guard let recipients = params["recipients"] as? [String: Any],
let customIDs = recipients["custom_ids"] as? [String]
else { return }

for id in customIDs {
print(id) // Gets your String value
}

Swift map nested dictionary to swap outer and inner keys

A possible way is to use reduce(into:_:):

With sample:

let rates: [String: [String: Double]] = ["2021-11-14": ["CAD": 1.1,
"USD": 1.0,
"EUR": 0.9],
"2021-11-15": ["CAD": 1.11,
"USD": 1.01,
"EUR": 0.91]]

This should do the trick:

let target = rates.reduce(into: [String: [String: Double]]()) { partialResult, current in
let date = current.key
let dayRates = current.value
dayRates.forEach { aDayRate in
var currencyRates = partialResult[aDayRate.key, default: [:]]
currencyRates[date] = aDayRate.value
partialResult[aDayRate.key] = currencyRates
}
}

For the logic, we iterate over each elements of rates.
For each [CurrencyCode: Amount], we iterate on them, and set them to partialResult (which at the end of the inner loop of reduce(into:_:) will be the finalResult, ie the returned value).

Outputs for print(rates) & print(target) (I just formatted them to make them easier to read):

$> ["2021-11-14": ["CAD": 1.1, "EUR": 0.9, "USD": 1.0], 
"2021-11-15": ["USD": 1.01, "EUR": 0.91, "CAD": 1.11]]
$> ["EUR": ["2021-11-14": 0.9, "2021-11-15": 0.91],
"USD": ["2021-11-15": 1.01, "2021-11-14": 1.0],
"CAD": ["2021-11-14": 1.1, "2021-11-15": 1.11]]

SWIFT access nested dictionary

When you subscript, you get an Optional. And you cannot subscript an Optional. Therefore, while you can perhaps avoid casting, you cannot avoid unwrapping:

let dinner = ["name":"Matt"]
let douter = ["owner":dinner]
let name = douter["owner"]!["name"]

But that only works because Swift knows very specifically what douter is. It would be better, therefore, to do this in stages, as Swift expects you to do, e.g. with a nested series of if let bindings:

let dinner : AnyObject = ["name":"Matt"] as AnyObject
let douter : AnyObject = ["owner":dinner] as AnyObject
if let owner = douter["owner"] as? [NSObject:AnyObject],
let name = dinner["name"] as? String {
// do something with name
}

How to send a POST request with nested Dictionary in swift

Thanks to all,
I solve This problem.
Here is the step by step solution to this question.
1)

 Alamofire.request(url, method : .post , parameters : parameter , encoding : JSONEncoding.default, headers : tiket ).responseJSON { (response) in
if response.result.isSuccess{
let responseJoson : JSON = JSON(response.result.value!)
print(responseJoson)
}

encoding is very important here. Don't skip this.
Step 2)

 func apiCall(){
let customerTckt:[String:Any] = ["RequestID" : requestID ,
"TaskID" : taskID ,
"Description" : comments ,
"TicketTypeID" : 3 ,
"CustomerID" : customerID ,
"PriorityID" : 3 ,
"CustomerTPPID" : TTPIDArray ]

let param : [String : Any] = ["ServiceReqID" : 1 ,
"WebUsersID" : customerID,
"FirstName" : userName,
"LastName" : "" ,
"Company":self.profileValues.customerCompanyName ,
"City" : self.profileValues.customerCityName ,
"Email" : self.profileValues.customerEmail ,
"ContactNo" : self.profileValues.customerContactNumber ,
"Country" : "Pakistan" ,
"PackageChange" : 0 ,
"AddressChange" : 0,
"TelInternetVAS" : 0 ,
"Others" : 0 ,
"Comments" : comments ,
"CSAFNO" : self.profileValues.customerCSAFNo,
"SecondaryContactNo" : "" ,
"CustomerTicket" :customerTckt]
let userToken: HTTPHeaders = [
"Authorization": "bearer \(accessToken)",
"Content-Type": "application/json"
]
postserviceRequestFeedback(url: postRequestFeedbackUrl, parameter: param , tiket: userToken)
}

Don't skip "Content-Type" to application/json



Related Topics



Leave a reply



Submit