swift decode json data - ios

I have following json data returns form php.
{"Response":"OK","Data":[{"id":"1","organization_name":"Organization","description":"Description","address":"Address1, Ny, USA"}]}
In need to decode it using swift
Below is my code.
struct OrgData: Decodable {
let data: [Data]
enum CodingKeys : String, CodingKey {
case data = "Data"
}
}
struct Data: Decodable {
let id: String
let address: String
let description: String
let organization_name: String
}
and I am decoding it using
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
guard let dataObj = try? JSONDecoder().decode(OrgData.self, from: data) else {
print("Error: Couldn't decode data ")
return
}
..................
But no data I am getting in dataObj.
I am referring this article
https://roadfiresoftware.com/2018/02/how-to-parse-json-with-swift-4/

Create a function in a helper class
func decodedObject<T: Decodable>(_ type: T.Type, dictionaryData: JSONDictionary) throws -> T? {
guard let jsonData = try? JSONSerialization.data(withJSONObject: dictionaryData,
options: JSONSerialization.WritingOptions.prettyPrinted) else {
return nil
}
let decodedData = try self.decode(type, from: jsonData)
return decodedData
}
Now call it like follows:
guard let dataObj = try? JSONDecoder().decodedObject(OrgData.self, dictionaryData: data) else {
print("Error: Couldn't decode data ")
return
}

There were some issues with how your structs were configured:
There is no field for the "Response" value
Data is an array, not a String
The following appears to properly output the json you give:
import Foundation
let json =
"""
{"Response":"OK","Data":[{"id":"1","organization_name":"Organization","description":"Description","address":"Address1, Ny, USA"}]}
"""
// MARK: - OrgData
struct OrgData: Codable {
let response: String
let data: [Datum]
enum CodingKeys: String, CodingKey {
case response = "Response"
case data = "Data"
}
}
// MARK: - Datum
struct Datum: Codable {
let id, organizationName, datumDescription, address: String
enum CodingKeys: String, CodingKey {
case id
case organizationName = "organization_name"
case datumDescription = "description"
case address
}
}
guard let data = json.data(using: .utf8) else {
return
}
do {
let dataObj = try JSONDecoder().decode(OrgData.self,
from: data)
print(dataObj)
// Optional(__lldb_expr_1.OrgData(response: "OK", data: [__lldb_expr_1.Datum(id: "1", organizationName: "Organization", datumDescription: "Description", address: "Address1, Ny, USA")]))
} catch {
print(error)
}

Related

Parsing JSON File From iTunes API

The code below attempts parse through a json file that contains details about an artist and their music genre and was collected from the iTunes API. I am trying to extract the collectionName and artistName keys from the file with the use of the guard statement but it isn't working. It prints out parsing error.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
musicData()
}
let urlString = "https://itunes.apple.com/search?term=Alex&media=music&entity=album"
func musicData(){
let url = URL(string: urlString)
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let myData = data else {
return
}
guard let rawJSON = try? JSONSerialization.jsonObject(with: myData, options: []),
let json = rawJSON as? [String:Any] else {
print("error serializing JSON")
return
}
guard let musicDictionary = json["collectionName"] as? [String:String] else {
print("Parsing error")
return
}
}
task.resume()
}
let collectionName = musicDictionary["collectionName"]
let artistName = musicDictionary["artistName"]
}
The property you're trying to access is a level deeper. Better approach is to use JSONDecoder.
Model:
struct Response: Codable {
let resultCount: Int
let results: [Result]
}
struct Result: Codable {
let wrapperType: WrapperType
let collectionType: CollectionType
let artistId, collectionId: Int
let amgArtistId: Int?
let artistName, collectionName, collectionCensoredName: String
let artistViewUrl: String?
let collectionViewUrl, artworkUrl60, artworkUrl100: String
let collectionPrice: Double?
let collectionExplicitness: CollectionExplicitness
let trackCount: Int
let copyright: String
let country: Country
let currency: Currency
let releaseDate, primaryGenreName: String
let contentAdvisoryRating: String?
}
enum CollectionExplicitness: String, Codable {
case explicit = "explicit"
case notExplicit = "notExplicit"
}
enum CollectionType: String, Codable {
case album = "Album"
}
enum Country: String, Codable {
case usa = "USA"
}
enum Currency: String, Codable {
case usd = "USD"
}
enum WrapperType: String, Codable {
case collection = "collection"
}
Decoding:
guard let myData = data else {
return
}
do {
let response = try JSONDecoder().decode(Response.self, from: myData)
print(response.results[0].collectionName)
} catch { print(error) }

how to get value from json array in swift4

I can't getting json value into variable.I'm printing value but the problem is I can't getting json value without array
here is my json
{
"Categories": [
"city",
"delhi"
]
}
I want to categories value with array im printing value with array
here is my code
do{
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject]
print(json as AnyObject)
if let Categories = json["Categories"] {
print(Categories)
}
You need
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:[String]]
let arr1 = json["Categories"]!
let str1 = arr1.joined(separator: ":")
print(str1)
// or
let decoded = try JSONDecoder().decode(Root.self, from: data)
let str = decoded.categories.joined(separator: ":")
print(str)
} catch {
print(error)
}
or use
struct Root: Codable {
let categories: [String]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
}
Make your life easier with Codable. First create custom model for your response
struct Response: Decodable {
let categories: [String]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
}
Then decode your data which your receive using JSONDecoder
if let data = data {
do {
let decoded = try JSONDecoder().decode(Response.self, from: data)
let string = decoded.categories.joined(separator: ", ") // if u need to join
// your array to
// single `String`
print(string)
} catch { print(error) }
}
Use built in swift support for decoding json by conforming to Decodable and also conform to CustomStringConvertible to get a string representation of the values
struct Item: Decodable, CustomStringConvertible {
let categories: [String]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
public var description: String {
return categories.joined(separator: " ")
}
}
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Item.self, from: data)
let descr = result.description
print(descr)
} catch {
print(error)
}
//Model Class should be like this
struct JsonResposne : Codable {
let categories : [String]?
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
categories = try values.decodeIfPresent([String].self, forKey: .categories)
}
}
func getCategoriesResponse() {
Alamofire.request(requestUrl, method: .post, parameters: params, encoding: URLEncoding.default).responseJSON { (response) in
switch response.result {
case .success:
if response.data != nil {
do {
let decoder = JSONDecoder()
let apiResponse:JsonResponse = try decoder.decode(JsonResponse.self, from: responseData)
print(apiResponse.categories.count)
}catch {
print(error.localizedDescription)
}
}
}
break
case .failure:
print("There was something with your call")
break
}
}
}

how yo access item key in swift 4

if let url = URL(string: "https://mysit.com") {
URLSession.shared.dataTask(with: url) {
data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let data = data, error == nil,
let valueEncoding = response?.textEncodingName,
let getContent = String(data: data, encoding: valueEncoding.textEncodingToStringEncoding)
else { return }
print(getContent)
}.resume()
}
my Data
{"Regions":null,"Cities":[{"Id":"9605","Name":"YANBAA AS SENAYAH"},{"Id":"15","Name":"ABHA"},{"Id":"13","Name":"AD DAMMAM"},{"Id":"1542","Name":"AL BAHA"},{"Id":"14","Name":"AL MADINAH AL MUNAWWARAH"},{"Id":"2213","Name":"AR'AR"},{"Id":"11","Name":"BURAYDAH"},{"Id":"10","Name":"HAIL"},{"Id":"17","Name":"JAZAN"},{"Id":"6","Name":"MAKKAH AL MUKARRAMAH"},{"Id":"3417","Name":"NAJRAN"},{"Id":"3","Name":"RIYADH"},{"Id":"2237","Name":"SAKAKA"},{"Id":"1","Name":"TABUK"},
how to get an array list of values "Name" ,can you help me?
You can try
struct Root :Decodable{
let Cities:[InnerItem]
}
struct InnerItem :Decodable{
let Id:String
let Name:String
}
do {
let arr = try JSONDecoder().decode(Root.self, from: data)
print(arr.Cities)
}
catch {
print(error)
}
//
Note : This is the correct json structure
{"Regions":null,"Cities":[{"Id":"9605","Name":"YANBAA AS SENAYAH"},{"Id":"15","Name":"ABHA"},{"Id":"13","Name":"AD DAMMAM"},{"Id":"1542","Name":"AL BAHA"},{"Id":"14","Name":"AL MADINAH AL MUNAWWARAH"},{"Id":"2213","Name":"AR'AR"},{"Id":"11","Name":"BURAYDAH"},{"Id":"10","Name":"HAIL"},{"Id":"17","Name":"JAZAN"},{"Id":"6","Name":"MAKKAH AL MUKARRAMAH"},{"Id":"3417","Name":"NAJRAN"},{"Id":"3","Name":"RIYADH"},{"Id":"2237","Name":"SAKAKA"},{"Id":"1","Name":"TABUK"}]}
let responseData = try JSONSerialization.jsonObject(with: (response["Cities"] as! String).data(using: String.Encoding.utf8)!, options: []) as! [[String: Any]]
for item in responseData{
let name = item["Name"] as! String
}
Together with the decoding step. I added several guards to print an error if one comes up. It is generally good practice to throw the error and handle it on the appropriate level.
func work() {
guard let url = URL(string: "https://mysit.com") else {
fatalError("url is nil.")
}
URLSession.shared.dataTask(with: url) {
data, response, error in
guard error == nil else {
fatalError("\(error!)")
}
guard let response = response as? HTTPURLResponse,
response.statusCode == 200 else {
fatalError("Response is nil.")
}
guard let data = data else {
fatalError("data is nil.")
}
decode(data: data)
}.resume()
}
func decode(data: Data) {
let decoder = JSONDecoder.init()
let welcome = try! decoder.decode(Welcome.self, from: data)
print(welcome.cities.first!)
}
The decoding helpers. enum CodingKeys are used to convert the lowercase attributes to the uppercase JSON attributes and back.
struct Welcome: Codable {
var regions: [Region]?
let cities: [City]
enum CodingKeys: String, CodingKey {
case regions = "Regions"
case cities = "Cities"
}
}
struct City: Codable {
let id, name: String
enum CodingKeys: String, CodingKey {
case id = "Id"
case name = "Name"
}
}
struct Region: Codable {
let id, name: String
enum CodingKeys: String, CodingKey {
case id = "Id"
case name = "Name"
}
}
Some use services like Quicktype to convert JSON strings to the specific programming language. It makes things faster and simpler.

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)")
}

Swift 4 decodable json arrays

So I am currently woking on a program to parse a JSON Api which is linked at the bottom
When I run the code I get some output but not all
Mainly for the optional type of Anime which is good because it shows that it works but I also want to access the name and release date and languages however I have no idea how to work with JSON arrays like this in swift 4. I will attach my current code below.
import UIKit
struct AnimeJsonStuff: Decodable {
let data: [AnimeDataArray]
}
struct AnimeDataArray: Decodable {
let type: String?
}
class OsuHomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
func jsonDecoding() {
let jsonUrlString = "https://kitsu.io/api/edge/anime"
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 animeJsonStuff = try JSONDecoder().decode(AnimeJsonStuff.self, from: data)
print(animeJsonStuff.data)
let animeDataArray = try JSONDecoder().decode(AnimeDataArray.self, from: data)
print(animeDataArray.type as Any)
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
}
}
I have more code after that but it's just for setting up custom collectionViewCells.
Also here is the link to the api
Answer for title "Swift 4 decodable json arrays"
let decoder = JSONDecoder()
do {
let array = try decoder.decode([YouCodableStruct].self, from: response.data!)
debugPrint(array)
} catch {
debugPrint("Error occurred")
}
http://andrewmarinov.com/parsing-json-swift-4/
Please check below :
I din't add for all the keys. I added for some in attributes.
struct AnimeJsonStuff: Decodable {
let data: [AnimeDataArray]
}
struct AnimeLinks: Codable {
var selfStr : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
}
}
struct AnimeAttributes: Codable {
var createdAt : String?
private enum CodingKeys : String, CodingKey {
case createdAt = "createdAt"
}
}
struct AnimeRelationships: Codable {
var links : AnimeRelationshipsLinks?
private enum CodingKeys : String, CodingKey {
case links = "links"
}
}
struct AnimeRelationshipsLinks: Codable {
var selfStr : String?
var related : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
case related = "related"
}
}
struct AnimeDataArray: Codable {
let id: String?
let type: String?
let links: AnimeLinks?
let attributes: AnimeAttributes?
let relationships: [String: AnimeRelationships]?
private enum CodingKeys: String, CodingKey {
case id = "id"
case type = "type"
case links = "links"
case attributes = "attributes"
case relationships = "relationships"
}
}
Json parsing :
func jsonDecoding() {
let jsonUrlString = "https://kitsu.io/api/edge/anime"
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 animeJsonStuff = try JSONDecoder().decode(AnimeJsonStuff.self, from: data)
for anime in animeJsonStuff.data {
print(anime.id)
print(anime.type)
print(anime.links?.selfStr)
print(anime.attributes?.createdAt)
for (key, value) in anime.relationships! {
print(key)
print(value.links?.selfStr)
print(value.links?.related)
}
}
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
}

Resources