How to store json response into variable? - ios

I am getting json response from api. I need to store into variable using model,how can i store?
struct StructLogin {
var status:String?
var contentarr = [content]()
}
struct content {
var AuthoToken:String?
var user_name:String?
var user_type:String?
}
let jsonResponse = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
StructLoginObj.status = jsonResponse!["status"] as? String
StructLoginObj.contentarr = contentArray
contentObj.AuthoToken = jsonResponse!["auth_token"] as? String
contentObj.user_name = jsonResponse!["user_name"] as? String
contentObj.user_type = jsonResponse!["user_type"] as? String
{"status":"200","content":{"user_type":"1","user_name":"Super Admin","auth_token":"7500b440c0f8035e864e1541c650b888"}}

Use Codable to parse your JSON response into an object.
struct Login: Codable {
var status: String?
var content:Content?
}
struct Content: Codable {
var auth_token:String?
var user_name:String?
var user_type:String?
}
Let's take the example of JSON response provided by you.
let str = """
{"status":"200","content":{"user_type":"1","user_name":"Super Admin","auth_token":"7500b440c0f8035e864e1541c650b888"}}
"""
Since we don't have the actual Data from API, we'll be converting str to data for parsing.
if let data = str.data(using: .utf8) {
let login = try? JSONDecoder().decode(Login.self, from: data)
print(login)
}

You need to use Codable for easy convert the object to data , also your content key is a dictionary not an array
Save:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try! decoder.decode(Root.self, from: data)
print(res.content)
let data = try! JSONEncoder().encode(res.content)
// save data anywhere e.x userDefaults if it's a settings
UserDefaults.shared.set(data,forKey:"User")
Read:
if let data = UserDefaults.shared.data(forKey:"User") {
print(data)
}
struct Root: Codable {
let status: String
let content: Content
}
struct Content: Codable {
let userType, userName, authToken: String
}

You can use JSON.parse() method to convert API return value to JavaScript object :
let result = JSON.parse(data);

Related

How to map [[String:AnyObject]] to a class from a string

I'm trying to add an Array of objects from a string. I am getting an encrypted string from an API call. After decrypting the data, getting as a string of JSON structure as below:
{
"request_id”:”abcds123”,
"status”:”18”,
"message":"SUCCESS",
"customer_name”:”XXXX X XX”,
"mobile_no”:”123456789”,
"email_id":"xxx#xxx.com",
“function”:
[
{“funcCode”:”OPN”,,”funcVal”:0,”funcType":"M"},
{“funcCode”:”CLO”,”funcVal”:1,”funcType":"M"}
],
“dataID”:”ASD1234”,
”valid”:”Y”
}
This is generic API response which will very based on response.
I can map "function" element to [[String:AnyObject]]. But not able to map it to class directly. Do we have an easier approach to extract "function" array to a class in swift without iterating the data and add to an array variable:
var mainObject : [String:Any] = [:]
if let data = result.data(using: .utf8) {
do {
mainObject = (try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any])!
if let status = mainObject[“status”] as? String {
switch(status) {
case 200:
if mainObject.keys.contains(“customer_name”) {
//do something
}
if mainObject.keys.contains(“function”) {
if let functionList = mainObject[“function”] as? [[String:AnyObject]] {
//map functionList to class [Function]
for function in functionList {
print(“Function ====> ", function)
//create a class and add to an array of Function class
}
}
}
}
} catch {
print(error.localizedDescription)
}
}
}
The result string coming from response.
Objective is to extract "function" data alone and map it to class without creating container class/struct.
You can use the Codable protocol that will help you to map your JSON data into objects.
First create your structs:
struct MyFunction: Codable {
let funcCode: String
let funcVal: Int
let funcType: String
}
struct MyContainer: Codable {
let status: string,
let message: string,
// Another properties
let function: [MyFunction]
}
Then you just have to map your JSON string to the object using the decode function:
if let jsonData = result.data(using: .utf8) {
do {
let decoder = JSONDecoder()
let mainObject = try decoder.decode(MyContainer.self, for: jsonData)
} catch {
print(error.localizedDescription)
}
}
For more information you can check out this blog post.
State of the art is the (De)Codable protocol, you can decode JSON dictionaries directly into structs
struct Main: Decodable {
let status, customerName: String
let function : [Function]
}
struct Function: Codable {
let funcCode, funcType: String
let funcVal : Int
}
let data = Data(result.utf8)
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let mainObject = try decoder.decode(Main.self, from: data)
print(mainObject.customerName)
print(mainObject.function)
} catch {
print(error)
}
Note: A JSON dictionary is never [String:AnyObject] in Swift 3+, it's [String:Any]
Edit: If you want only the Function struct keep the JSONSerialization code, omit the Main struct, declare mainObject as
var functions = [Function]()
and map the JSON array
if let functionList = mainObject["function"] as? [[String:Any]] {
functions = functionList.map {Function(funcCode: $0["funcCode"] as! String,
funcVal: $0["funcVal"] as! Int,
funcType: $0["funcType"] as! String) }
}
To write the array to UserDefaults encode it with JSONEncoder to Data.

Array of Codable structs possibly decoded from JSON data object

I have this piece of code:
struct NoteRecord: Codable {
let id: String
let title: String
let detail: String?
let dueDate: String?
private enum CodingKeys: String, CodingKey {
case id, title, detail, dueDate
}}
and parsing part:
do {
let decoder = JSONDecoder()
let note = try decoder.decode(NoteRecord.self, from: data)
} catch let err {
print("Error occured:", err)
}
Is there any way to use this when REST API returns an array of objects to decode the data correctly as array of structs?
Yes, just use this:
do {
let decoder = JSONDecoder()
let notes = try decoder.decode([NoteRecord].self, from: data)
} catch let err {
print("Error occured:", err)
}
If you use [YourCodableStruct].self you are parsing the array. If you use YourCodableStruct.self you are parsing the struct.
You can implement another struct to hold the array.
struct NoteRecords: Codable {
var list: [NoteRecord] // You should change the var name and coding keys
}
And parse it like
let note = try decoder.decode(NoteRecords.self, from: data)
I hope this helps.

struct for nested dictionary API in swift

i'm trying to import JSON data from the v2 of coinmarketcap API. I had it working with v1 as it was an array, however the new version is a dictionary and i cant quite get my struct correct.
The API im using is : https://api.coinmarketcap.com/v2/ticker/?convert=AUD
My struct is set up as below:
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
var id: String
var symbol : String
var name : String
var priceAUD : quoteStruct
}
struct quoteStruct{
let aud : priceStruct
}
struct priceStruct{
let price : String
}
My code for fetching the data is:
var coins = [Coin]()
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
My code for fetching the data i have used the same as previously which worked with v1 of the API, however i don't think i made my struct correctly.
Thanks in advance!
Your response Changed i try to configure it by converting it to array of dictionary you will need to change quotes to be [String:priceStruct]
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id,rank,symbol, name, priceAUD = "quotes"
}
var id: Int
var rank: Int
var symbol : String
var name : String
var priceAUD : [String: priceStruct]
}
struct priceStruct : Decodable{
let price : Double
}
func getCoinData() {
var coins = [Coin]()
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let resultData = json["data"] as? [String:Any] {
let dataObject = try JSONSerialization.data(withJSONObject: resultData.values.map({$0}) , options: .prettyPrinted)
coins = try JSONDecoder().decode([Coin].self, from: dataObject)
print(coins.count)
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
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)")
}

Creation problem with appropriate struct for URL data in JSON format. Source URL in example

Url source code : https://api.openweathermap.org/data/2.5/weather?q=warsaw&appid=5ca98c08c8abd2bf1fdbd601c3cf3d7e
I tried this code but it's not correct, i have problem with writing appropriate struct.
struct Weather: Codable {
let weather : [cos]
let base: String
}
struct cos : Codable {
let main: String
}
let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=warsaw&appid=5ca98c08c8abd2bf1fdbd601c3cf3d7e")
override func viewDidLoad() {
super.viewDidLoad()
json()
}
func json() {
guard let downloadURL = url else {return}
URLSession.shared.dataTask(with: downloadURL) { (data, response, error) in
guard let data = data, error == nil, response != nil
else {
print("zle cos")
return
}
print("downloaded")
do{
let downloaded =try JSONDecoder().decode([Weather].self,from:
data)
print("ok")
print(downloaded[0].)
} catch {
print("error")
}
}.resume()
}
This does not represent the entire data, I left some work for you 😉
Please read the JSON carefully, all dictionaries ({}) can be decoded into a struct, all values in double quotes are String, floating point numeric values are Double, the other Int, the Int dates starting with 1523... can be decoded to Date with the appropriate date strategy:
let jsonString = """
{"coord":{"lon":21.01,"lat":52.23},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"stations","main":{"temp":294.15,"pressure":1012,"humidity":52,"temp_min":294.15,"temp_max":294.15},"visibility":10000,"wind":{"speed":6.7,"deg":90},"clouds":{"all":0},"dt":1523548800,"sys":{"type":1,"id":5374,"message":0.0023,"country":"PL","sunrise":1523504666,"sunset":1523554192},"id":756135,"name":"Warsaw","cod":200}
"""
struct Root : Decodable {
let coord : Coordinate
let weather : [Weather]
let base : String
let main : Main
let dt : Date
}
struct Coordinate : Decodable {
let lat, lon : Double
}
struct Weather : Decodable {
let id : Int
let main, description, icon : String
}
struct Main : Decodable {
let temp, tempMin, tempMax : Double
let pressure, humidity : Int
}
let data = Data(jsonString.utf8)
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .secondsSince1970
let result = try decoder.decode(Root.self, from: data)
print(result)
} catch { print(error) }

Swift 4 Parsing an Object Array

I'm having problems transferring my parsing code over to Swift4/Xcode9. I'm parsing it through my model correctly and I know the problem is because I'm currently parsing through the root JSON object, however my REST API is structured JSON root -> data array -> further objects. I know it's probably simple but I've been struggling for ages. I have attached an image of the layout of my REST API, and just need to know how to step into objects to retrieve values.
let jsonUrlString = "HIDDEN"
guard let url = URL(string: jsonUrlString) else
{return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let show = try
JSONDecoder().decode(TvHeaderModel.self, from: data)
print(show.title)
} catch let jsonErr {
print("Error serializing JSON", jsonErr)
}
}.resume()
struct TvHeaderModel: Decodable {
var id: Int?
var title: String?
var year: Int?
var songCount: Int?
var poster: String?
init(json: [String: Any]) {
id = json["_id"] as? Int ?? -1
title = json["title"] as? String ?? ""
year = json["year"] as? Int ?? -1
songCount = json["song_count"] as? Int ?? -1
poster = json["poster_urL"] as? String ?? ""
}
}
Basically you don't need the init(json initializer when using JSONDecoder and declaring all properties as optional and assigning always a non-optional value is nonsensical anyway.
To decode also the seasons add a struct
struct Season : Decodable {
private enum CodingKeys : String, CodingKey {
case id = "_id", season
case show = "tv_show", createdDate = "created_at"
case numberOfEpisodes = "episodes_count", numberOfSongs = "songs_count"
}
let id, season, show, numberOfEpisodes, numberOfSongs : Int
let createdDate : Date
}
In TvHeaderModel add a property
let seasons : [Season]
And set the dateDecodingStrategy of the decoder to decode ISO8601 dates.
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
decoder.decode(TvHeaderModel.self, from: data)

Resources