Unable to fetch data from json through codable swift - json

This is json code:
{
"status":"success",
"data":
[
{"id":"3",
"city_name":"Delhi",
"city_image":"delhi.png"},
{"id":"4",
"city_name":"Mumbai",
"city_image":"tickmark.png"}
]
}
My Swift Code :
struct city: Decodable{
let status : String
let id: String
let data : String
let city_name: String
let city_image: String
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let jsonUrl = "http://parking.onlinekiduniya.org/api/cityList.php"
let url = URL(string: jsonUrl)
URLSession.shared.dataTask(with: url!) {(data, response, error) in
do {
let cities = try JSONDecoder().decode([city].self, from: data!)
for city in cities {
print(city.id)
}
}
catch {
print("we got error")
}
}.resume()
}
}

Replace
let cities = try JSONDecoder().decode([city].self, from: data!)
with
let root = try JSONDecoder().decode(Root.self, from: data!)
let cities = root.data
cities.forEach {
print($0.id)
}
struct Root: Codable {
let status: String
let data: [City]
}
struct City: Codable {
let id, cityName, cityImage: String // you can use snake case also
enum CodingKeys: String, CodingKey {
case id
case cityName = "city_name"
case cityImage = "city_image"
}
}
Your root is a dictionary not an array

Related

Swift JSON with dynamic Keys

I am trying to parse JSON using Swift, which has dynamic keys. Tried several ways but still did not find the solution. Could you please help me ?
I am trying to parse NativeName, which is dynamic based on which language country name is present.
API: https://restcountries.com/v3.1/all
struct Objects: Codable {
let name: Name
let cca2 : String
let flag: String
}
struct Name: Codable {
let common, official: String
let nativeName: NativeName
}
struct NativeName: Codable {
var deu : Deu
}
struct Deu: Codable {
let official, common: String?
}
and here is JSON Model:
class ParsingService {
static let shared = ParsingService()
func fetchData() {
guard let url = URL(string: "https://restcountries.com/v3.1/all") else {
print("DEBUG: URL is nill")
return}
let session = URLSession.shared
let task = session.dataTask(with: url) { data, _, error in
guard let retrievedData = data, error == nil else {
print("DEBUG: Data is not available")
return}
print("DEBUG: Data is available \(retrievedData)")
guard let decodedData = self.JSONParsing(inputData: retrievedData) else {
print("DEBUG: Missing data")
return}
print("DEBUG: Data is there")
print("DEBUG: \(decodedData[0].cca2)")
print("DEBUG: \(decodedData[0].flag)")
print("DEBUG: \(decodedData[0].name.nativeName.deu.official)")
DispatchQueue.main.async {
print(decodedData.currencies)
}
}
task.resume()
}
func JSONParsing(inputData: Data)-> [Objects]? {
let decoder = JSONDecoder()
do {
let data = try? decoder.decode([Objects].self, from: inputData)
return data
} catch {
print("DEBUG: Cannot get data")
return nil
}
}
}
you could try this approach:
struct ContentView: View {
var body: some View {
Text("testing")
.onAppear {
fetchData() { results in
print("---> results: \(results.count) \n")
for i in 0..<3 {
print("---> results[\(i)]: \(results[i].name.nativeName)")
}
}
}
}
// todo deal with errors
func fetchData(completion: #escaping ([Objects]) -> Void) {
let url = URL(string: "https://restcountries.com/v3.1/all")
guard let url = url else { completion([]); return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { completion([]); return }
do {
let results = try JSONDecoder().decode([Objects].self, from: data)
completion(results)
}
catch {
print("Error: \(error)")
completion([])
}
}.resume()
}
}
struct Objects: Codable {
let name: Name
let cca2 : String
let flag: String
}
struct Deu: Codable {
let official, common: String?
}
struct Name: Codable {
let common, official: String
let nativeName: NativeName? // <-- here
}
// -- here
struct NativeName: Codable {
var lang: [String: Deu]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
lang = try container.decode([String: Deu].self)
}
func encode(to encoder: Encoder) throws {
// todo
}
}
Note, you could also use a Tuple, such as var lang: (key: String, value: Deu)

How to parse JSON using Codable in Swift?

I am able to parse JSON using JSONSerialization, but unable to parse with Codable.
the json look like this:
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
}
Please help me with the code.
able to parse using JSONSerialization: data coming
Unable to parse JSON with Codable: data not coming
struct jsonDataModel: Codable{
var name: String
var userName: String
var email: String
init(name: String, username: String, email: String){
self.name = name
self.userName = username
self.email = email
}
}
class WebviewViewController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var testWebView: WKWebView!
//var itemsArray = [jsonDataModel]()
override func viewDidLoad() {
super.viewDidLoad()
serviceCall()
}
func serviceCall()
{
let jsonString = "https://jsonplaceholder.typicode.com/users"
let jsonData = jsonString.data(using: .utf8)!
do {
let jsonDecoder = JSONDecoder()
let user = try jsonDecoder.decode(jsonDataModel.self, from: jsonData)
print("all data \(user)")
print("Hello \(user.name), \(user.userName), \(user.email) ")
} catch {
print("Unexpected error: \(error).")
}
}
}
Please help me to parse json with codable.
Try this example.
import UIKit
import Foundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
do{
let jsonDataModels = try JSONDecoder().decode([JSONDataModel].self, from: data)
print(String(data: data, encoding: .utf8)!)
print("jsonDataModels: \(jsonDataModels)")
}catch{}
}
task.resume()
}
}
struct JSONDataModel: Codable {
let id: Int
let name, username, email: String
let address: Address
let phone, website: String
let company: Company
}
struct Address: Codable {
let street, suite, city, zipcode: String
let geo: Geo
}
struct Geo: Codable {
let lat, lng: String
}
struct Company: Codable {
let name, catchPhrase, bs: String
}
First of all, if you're using a URL, then to get data you need to use a networking api. URLSession is the iOS provided api to perform network operations like download/upload.
So, just using Codable doesn't make any sense. You need to first have the data in order to parse it with Codable.
Here is the model,
struct Model: Codable {
let id: Int
let name, username, email: String
}
And you can use it in your controller's viewDidLoad() method,
if let url = URL(string: "https://jsonplaceholder.typicode.com/users") {
URLSession.shared.dataTask(with: url) { (data, urlResponse, error) in
if let data = data {
do {
let response = try JSONDecoder().decode([Model].self, from: data)
print(response.map({ $0.name })) //prints all the names
} catch {
print(error)
}
}
}.resume()
}

I need to create a table view with a nested Json

I have to read a json from the url: https://randomuser.me/api/?results=100
I created the People.swift file that contains the structure created through the site: https://app.quicktype.io/?l=swift
I tried to use this code but I can not then insert the json into the structure and then recall it via cell.people.name for example.
ViewController.swift:
var dataRoutine = [People]() // this is the structure that I created with the site indicated above.
this one is my function to download the Json and parse.
func downloadJsonData(completed : #escaping ()->()){
guard let url = URL(string: "https://randomuser.me/api/?results=100")else {return}
let request = URLRequest.init(url: url)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
let statuscode = httpResponse.statusCode
if statuscode == 404{
print( "Sorry! No Routine Found")
}else{
if error == nil{
do{
self.dataRoutine = try JSONDecoder().decode(People.self, from: data!)
DispatchQueue.main.async {
completed()
print(self.dataRoutine.count) // I don't know why my result is ever 1.
}
}catch{
print(error)
}
}
}
}
}.resume()
}
my structure is :
import Foundation
struct People: Codable {
let results: [Result]?
let info: Info?
}
struct Info: Codable {
let seed: String?
let results, page: Int?
let version: String?
}
struct Result: Codable {
let gender: Gender?
let name: Name?
let location: Location?
let email: String?
let login: Login?
let dob, registered: Dob?
let phone, cell: String?
let id: ID?
let picture: Picture?
let nat: String?
}
struct Dob: Codable {
let date: Date?
let age: Int?
}
enum Gender: String, Codable {
case female = "female"
case male = "male"
}
struct ID: Codable {
let name: String?
let value: String?
}
struct Location: Codable {
let street, city, state: String?
let postcode: Postcode?
let coordinates: Coordinates?
let timezone: Timezone?
}
struct Coordinates: Codable {
let latitude, longitude: String?
}
enum Postcode: Codable {
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Postcode.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Postcode"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
struct Timezone: Codable {
let offset, description: String?
}
struct Login: Codable {
let uuid, username, password, salt: String?
let md5, sha1, sha256: String?
}
struct Name: Codable {
let title: Title?
let first, last: String?
}
enum Title: String, Codable {
case madame = "madame"
case mademoiselle = "mademoiselle"
case miss = "miss"
case monsieur = "monsieur"
case mr = "mr"
case mrs = "mrs"
case ms = "ms"
}
struct Picture: Codable {
let large, medium, thumbnail: String?
}
The main issue is a type mismatch.
The root object of the JSON is not the People array, it's the umbrella struct, I named it Response
Please change the structs to
struct Response: Decodable {
let results: [Person]
let info: Info
}
struct Info: Decodable {
let seed: String
let results, page: Int
let version: String
}
struct Person: Decodable {
let gender: Gender
let name: Name
let location: Location
let email: String
let login: Login
let dob, registered: Dob
let phone, cell: String
let id: ID
let picture: Picture
let nat: String
}
struct Dob: Decodable {
let date: Date
let age: Int
}
enum Gender: String, Decodable {
case female, male
}
struct ID: Codable {
let name: String
let value: String?
}
struct Location: Decodable {
let street, city, state: String
let postcode: Postcode
let coordinates: Coordinates
let timezone: Timezone
}
struct Coordinates: Codable {
let latitude, longitude: String
}
enum Postcode: Codable {
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Postcode.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Postcode"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
struct Timezone: Codable {
let offset, description: String
}
struct Login: Codable {
let uuid, username, password, salt: String
let md5, sha1, sha256: String
}
struct Name: Codable {
let title: Title
let first, last: String
}
enum Title: String, Codable {
case madame, mademoiselle, miss, monsieur, mr, mrs, ms
}
struct Picture: Codable {
let large, medium, thumbnail: String
}
Almost all properties can be declared non-optional, the date key in Dob is an ISO8601 date string you have to add the appropriate date decoding strategy. The People array is the property results of the root object.
var dataRoutine = [Person]()
func downloadJsonData(completed : #escaping ()->()){
guard let url = URL(string: "https://randomuser.me/api/?results=100")else {return}
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
let statuscode = httpResponse.statusCode
if statuscode == 404{
print( "Sorry! No Routine Found")
}else{
if error == nil{
do{
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let jsonResponse = try decoder.decode(Response.self, from: data!)
self.dataRoutine = jsonResponse.results
DispatchQueue.main.async {
completed()
print(self.dataRoutine.count) // I don't know why my result is ever 1.
}
}catch{
print(error)
}
}
}
}
}.resume()
}

How to get value in json Swift

I hava a json file:
jsonpElixo({
"response":{
"parks":[
{
"Park":{
"id":"2",
"name":"PARQUE VILLA-LOBOS",
"type":"Urbano"
},
"Address":{
"lat":"-23.547245206920508",
"long":"-46.71627699999999",
"cep":null,
"street":"Avenida Professor Fonseca Rodrigues",
"number":"1025",
"neighborhood":"Alto Pinheiros",
"city":"S\u00e3o Paulo",
"state":"SP",
"id":"9"
}
}
]
}
})
But I can't get the elements inside the {}. Because the "jsonpElixo(" is breaking during the decodable.
How can I fix that?
The func to get info about json file.
func getParks() {
var request = URLRequest(url:gitUrl)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let welcome = try? decoder.decode(Welcome.self, from: data)
} catch let err {
print("Err", err)
}
}.resume()
}
The structs to Decodable the elements. But I dont know how can I scape this first element ("jsonpElixo(")
struct Welcome: Decodable {
let response: Response
}
struct Response: Decodable {
let parks: [Park]
}
struct Park: Decodable {
let park: ParkClass
let address: Address
enum CodingKeys: String, CodingKey {
case park = "Park"
case address = "Address"
}
}
struct Address: Decodable {
let lat, long: String
let cep: String?
let street, number, neighborhood, city: String
let state, id: String
}
struct ParkClass: Decodable {
let id, name, type: String
}
You can create a function that will remove the outer jsonpElixo() object and return the json to work with.
Start with an extension on Data so we can create something similar to this:
extension Data {
func decodeJsonpElixo() -> Data {
guard let jsonpString = String(data: self, encoding: .utf8) else {return self}
if jsonpString.hasPrefix("jsonpElixo(") && jsonpString.hasSuffix(")") {
var decoderString = jsonpString.replacingOccurrences(of: "jsonpElixo(", with: "")
decoderString.remove(at: String.Index(encodedOffset: decoderString.endIndex.encodedOffset - 1))
return Data(decoderString.utf8)
}
return self
}
}
Then you can use this in your URLSession closure like this:
guard let data = data else { return }
let decoderData = data.decodeJsonpElixo()
let decoder = JSONDecoder()
do {
let welcome = try decoder.decode(Welcome.self, from: decoderData)
} catch let err {
print(err)
}

Parsing JSON with Swift 4 Decodable

I have been getting the following error every time I try to parse JSON in my program. I can't seem to figure it out.
"Expected to decode String but found an array instead.", underlyingError: nil
Here is the code I have been struggling with:
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
var pages: [Page]?
}
struct Page: Decodable {
let id: Int
let text: [String]
}
struct Chapter: Decodable {
var chapterNumber: Int
}
func fetchJSON() {
let urlString = "https://api.myjson.com/bins/kzqh3"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, err) in
if let err = err {
print("Failed to fetch data from", err)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let books = try decoder.decode([Book].self, from: data)
books.forEach({print($0.title)})
} catch let jsonErr {
print("Failed to parse json:", jsonErr)
}
}.resume()
}
Are you sure that's the real error message?
Actually the error is supposed to be
"Expected to decode String but found a dictionary instead."
The value for key text is not an array of strings, it's an array of dictionaries
struct Page: Decodable {
let id: Int
let text: [[String:String]]
}
The struct Chapter is not needed.
Alternatively write a custom initializer and decode the dictionaries containing the chapter number as key and the text as value into an array of Chapter
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
let pages: [Page]
}
struct Page: Decodable {
let id: Int
var chapters = [Chapter]()
private enum CodingKeys : String, CodingKey { case id, chapters = "text" }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
var arrayContainer = try container.nestedUnkeyedContainer(forKey: .chapters)
while !arrayContainer.isAtEnd {
let chapterDict = try arrayContainer.decode([String:String].self)
for (key, value) in chapterDict {
chapters.append(Chapter(number: Int(key)!, text: value))
}
}
}
}
struct Chapter: Decodable {
let number : Int
let text : String
}
This is working:
import UIKit
class ViewController: UIViewController {
private var books = [Book]()
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
var pages: [Page]
}
struct Page: Decodable {
let id: Int
let text: [[String:String]]
}
override func viewDidLoad() {
super.viewDidLoad()
fetchJSON()
}
func fetchJSON() {
let urlString = "https://api.myjson.com/bins/kzqh3"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if error != nil {
print(error!.localizedDescription)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.books = try decoder.decode([Book].self, from: data)
DispatchQueue.main.async {
for info in self.books {
print(info.title)
print(info.chapters)
print(info.pages[0].id)
print(info.pages[0].text)
print("-------------------")
}
}
} catch let jsonErr {
print("something wrong after downloaded: \(jsonErr) ")
}
}.resume()
}
}
// print
//Genesis
//50
//1
//[["1": "In the beg...]]
//-------------------
//Exodus
//40
//2
//[["1": "In the beginning God created...]]
//
If you need to print the value of each chapter in the book, I can use something like this:
import UIKit
class ViewController: UIViewController {
private var books = [Book]()
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
var pages: [Page]
}
struct Page: Decodable {
let id: Int
let text: [[String:String]]
}
override func viewDidLoad() {
super.viewDidLoad()
fetchJSON()
}
func fetchJSON() {
let urlString = "https://api.myjson.com/bins/kzqh3"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if error != nil {
print(error!.localizedDescription)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.books = try decoder.decode([Book].self, from: data)
DispatchQueue.main.async {
for info in self.books {
print(info.title)
print(info.chapters)
print(info.pages[0].id)
//print(info.pages[0].text)
for cc in info.pages[0].text {
for (key, value) in cc {
print("\(key) : \(value)")
}
}
print("-------------------")
}
}
} catch let jsonErr {
print("something wrong after downloaded: \(jsonErr) ")
}
}.resume()
}
}
//Genesis
//50
//1
//1 : In the beginning God ...
//2 : But the earth became waste...
//.
//.
//.
//31 : And God saw everything...
//-------------------
//Exodus
//40
//2
//1 : In the beginning God...
//2 : But the earth became...
//.
//.
//.
//31 : And God saw everything