Swift Does not see data - ios

I am trying to parse the data and display on the screen but i am getting " Value of type 'EmployeeData' has no member 'employee_name' "
What i am missing ?
I created my struct, parsed data and tried to divide into two parts. first part will be related with listing, second part is all data.
struct EmployeeData: Codable {
var data: Employee
var status: String
}
struct Employee: Codable {
var employee_name: String
var employee_salary: String
var employee_age: String
}
class WebServices {
func getData(completion: #escaping (EmployeeData?) -> ()){
guard let url = URL(string:"http://dummy.restapiexample.com/api/v1/employees")
else { fatalError("There is error!") }
URLSession.shared.dataTask(with: url) { (data, response,error) in
guard let data = data, error == nil else {
DispatchQueue.main.async{
completion(nil)
}
return
}
let empleyees = try? JSONDecoder().decode(EmployeeData.self, from: data)
DispatchQueue.main.async {
completion(empleyees)
}
}.resume()
}
}
class MVDesingnListView: ObservableObject {
}
struct MVDesignCellView {
let employeeDatas: EmployeeData
init(employeeDatas: EmployeeData) {
self.employeeDatas = employeeDatas
}
var employee_name: String {
self.employeeDatas.employee_name
}
}

The compiler is all right. Your struct EmployeeData has no member employee_name.
You need to go to the employee first, to get her name:
var employee_name: String {
self.employeeDatas.data.employee_name
}
should do the job.

Related

Swift Error The data can not be Read because it is missing

I am trying to display the data from API . Here is the API Link .https://coinmap.org/api/v1/venues/ . I want to display the properties of the Vanues Array fields into IOS app . I created model by using Quick type . I use the Map like self.vanues = respone.results.map{$0} but still same result Here is the model .
import Foundation
// MARK: - Welcome
struct Coin: Codable {
let venues: [Venue]
}
// MARK: - Venue
struct Venue: Codable {
let id: Int
let lat, lon: Double
let category, name: String
let createdOn: Int
let geolocationDegrees: String
enum CodingKeys: String, CodingKey {
case id, lat, lon, category, name
case createdOn = "created_on"
case geolocationDegrees = "geolocation_degrees"
}
}
I convert that to list by using another swift file . Here is the code .
import Foundation
struct VanueResponse: Decodable {
let results: [Venue]
}
Here is my Network Manager .
import Foundation
class NetworkManager {
func getCoins(from url: String, completion: #escaping (Result<VanueResponse, NetworkError>) -> Void ) {
guard let url = URL(string: url) else {
completion(.failure(.badURL))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(.other(error)))
return
}
if let data = data {
//decode
do {
let response = try JSONDecoder().decode(VanueResponse.self, from: data)
completion(.success(response))
} catch let error {
completion(.failure(.other(error)))
}
}
}
.resume()
}
}
Here is the presenter class.
import Foundation
class VenuePresenter : VanueProtocol{
// creating instance of the class
private let view : VanueViewProtocol
private let networkManager: NetworkManager
private var vanues = [Venue]()
var rows: Int{
return vanues.count
}
// initilanize the class
init(view:VanueViewProtocol , networkmanager:NetworkManager = NetworkManager()){
self.view = view
self.networkManager = networkmanager
}
func getVanue(){
let url = "https://coinmap.org/api/v1/venues/"
networkManager.getCoins(from: url) { result in
switch result {
case.success(let respone):
self.vanues = respone.results
DispatchQueue.main.async {
self.view.resfreshTableView()
}
case .failure(let error):
DispatchQueue.main.async {
self.view.displayError(error.localizedDescription)
print(Thread.callStackSymbols)
}
}
}
}
func getId(by row: Int) -> Int {
return vanues[row].id
}
func getLat(by row: Int) -> Double {
return vanues[row].lat
}
func getCreated(by row: Int) -> Int {
return vanues[row].createdOn
}
func getLon(by row: Int) -> Double? {
return vanues[row].lon
}
}
I put the break point and find this in console windows .
Here is the screenshot when I run the Applications .
The Decoding Error is clear:
The key in the root dictionary is venues (not results) so the proper struct is Coin.
In getCoins replace both occurrences of VanueResponse with Coin

How to make GET API calls that has large JSON response in Swift 5?

I am trying to fetch the Covid 19 data of all countries and their states from disease.sh.
I have previously fetched json data from different APIs using this method. The response in those cases were shorter compared to this.
I have posted the codes below:
// Webservice.swift
import Foundation
class Webservice {
let countriesURL: String = "https://disease.sh/v3/covid-19/jhucsse"
func getAllCountries(completion: #escaping ([Country]?) ->()) {
guard let url = URL(string: countriesURL) else {
completion(nil)
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("No data in response: \(error?.localizedDescription ?? "Unknown error").")
DispatchQueue.main.async {
completion(nil)
}
return
}
let countries = try? JSONDecoder().decode([Country].self, from: data)
DispatchQueue.main.async {
countries == nil ? completion(nil) : completion(countries)
}
}.resume()
}
}
Since, I was using MVVM design, here are my Model, ViewModel and View.
// Model
// Country.swift
import Foundation
struct Country: Decodable {
var country: String
var updatedAt: String
var stats: Stats
var coordinates: Coordinates
var province: String
}
struct Stats: Decodable {
var confirmed: Int
var deaths: Int
var recovered: Int
}
struct Coordinates: Decodable {
var latitude: String
var longitude: String
}
// ViewModel
// CountryListViewModel.swift
import Foundation
class CountryListViewModel: ObservableObject {
#Published var countries = [CountryViewModel]()
init() {
fetchCountries()
}
func fetchCountries() {
Webservice().getAllCountries() { countries in
if let countries = countries {
self.countries = countries.map(CountryViewModel.init)
}
}
}
}
class CountryViewModel {
var country: Country
init(country: Country) {
self.country = country
}
let id = UUID()
var name: String {
return self.country.country
}
var updatedAt: String {
return self.country.updatedAt
}
var stats: Stats {
return self.country.stats
}
var coordinates: Coordinates {
return self.country.coordinates
}
var province: String {
return self.country.province
}
}
// View
// ContentView.swift
import SwiftUI
struct ContentView: View {
#ObservedObject private var countryListVM = CountryListViewModel()
var body: some View {
List( self.countryListVM.countries, id:\.id) { country in
Text(country.name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
My issue is when I call the Webservice.getAllCountries() it returns nil. Could anyone look at the code and tell me what is wrong, please? Thank you!
PS: I created a mock json with fewer objects (20-30) and called Webservice.getAllCountries() in this case it returned and mapped the values. It is not working with larger JSON response. Help!!
You should avoid using try ? except in situations where you really don't care about failures. do/try/catch is a better approach since it will tell you why something failed.
Changing your code to
do {
let countries = try JSONDecoder().decode([Country].self, from: data)
DispatchQueue.main.async {
completion(countries)
}
} catch {
print(error)
completion(nil)
}
Gives us an error on the console -
Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 509", intValue: 509), CodingKeys(stringValue: "province", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil))
which makes sense, since not all countries have provinces. To fix this, make province an optional in your data model
struct Country: Decodable {
var country: String
var updatedAt: String
var stats: Stats
var coordinates: Coordinates
var province: String?
}

Json parsing Swift not getting print

I am new to Swift. I want to fetch some json data from the server using the url. I tried many other solutions but they didn't work. I want to print the duration key (text and value) from the array and then print it in console.
The Json data is attached below
{
"status": "OK",
"rows": [
{
"elements": [
{
"duration": {
"text": "3 hours 49 mins",
"value": 13725
},
"distance": {
"text": "225 mi",
"value": 361715
},
"status": "OK"
}
]
}
],
"origin_addresses": [
"Washington, DC, USA"
],
"destination_addresses": [
"New York, NY, USA"
]
}
Attached Code
func getdatajson1(){
if let url = URL(string: "http://www.json-generator.com/api/json/get/bQywstyfkO?indent=2") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode(Root.self, from: data)
print(res.rows)
} catch let error {
print(error)
}
}
}.resume()
}
}
struct Root: Codable {
let rows: [Root2]
}
struct Root2: Codable {
let elements: [Root3]
}
struct Root3: Codable {
let elements: [node]
}
struct node: Codable {
let duration : [valuesarray]
}
struct valuesarray: Codable {
let text : String
let value : Int
}
The duration is an Object and not an Array, also change your names and you can use this:
struct Root: Decodable {
let rows: [Rows]
}
struct Rows: Decodable {
let elements: [Elements]
}
struct Elements: Decodable {
let duration, distance: LocationValues
}
struct LocationValues: Decodable {
let text: String
let value: Int
}
func getdatajson1(){
if let url = URL(string: "http://www.json-generator.com/api/json/get/bQywstyfkO?indent=2") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode(Root.self, from: data)
if let row = res.rows.first, let elements = row.elements.first {
print(elements.duration.text) //This is how you can get the text value
print(elements.distance.text) //This will print the distance
}
} catch let error {
print(error)
}
}
}.resume()
}
}
Replace your codable struct with the below
class Result: Codable {
var status:String?
var rows:[Row]?
var origin_addresses:[String]?
var destination_addresses:[String]?
}
class Row: Codable {
var elements:[Element]?
}
class Element: Codable {
var status:String?
var duration:Duration?
var distance:Distance?
}
class Duration: Codable {
var text:String?
var value:Int?
}
class Distance: Codable {
var text:String?
var value:Int?
}
You should update your node model like below
struct node: Codable {
let duration : valuesarray
let distance : valuesarray
let status : String
}
And you can access your duration data from API response like below
if let rows = res.rows, rows.count > 0 {
//Access the element objects from rows
let arrElements = rows[0].elements, arrElements.count > 0 {
if let durationData = arrElements[0].duration { //get your duration object
print(durationData.text)
print(durationData.value)
}
}
}
try this:
and a hint: go to https://app.quicktype.io/ -> here you can paste your json and you will get your datastructs for free! ;)
func getdatajson1(){
if let url = URL(string: "http://www.json-generator.com/api/json/get/bQywstyfkO?indent=2") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode(Welcome.self, from: data)
print(res.rows)
} catch let error {
print(error)
}
}
}.resume()
}
}
getdatajson1()
struct Welcome: Codable {
let status: String
let rows: [Row]
let originAddresses, destinationAddresses: [String]
enum CodingKeys: String, CodingKey {
case status, rows
case originAddresses = "origin_addresses"
case destinationAddresses = "destination_addresses"
}
}
// MARK: - Row
struct Row: Codable {
let elements: [Element]
}
// MARK: - Element
struct Element: Codable {
let duration, distance: Distance
let status: String
}
// MARK: - Distance
struct Distance: Codable {
let text: String
let value: Int
}

How to Convert a model object into JSON - Swift 4

How to Convert a model object into JSON?
I want "Answer" object.
// Answers Class
class Answers {
var cat_id: String!
var responses = [Response]()
var date: String!
var comment: String!
var time: String!
var lat: String!
var lon: String!
var address: String!
}
// Response Class
class Response {
var que_id: String!
var question: String!
var response: String!
}
Make both types conform to Codable:
class Answers: Codable {
...
}
class Response: Codable {
...
}
And then use JSONEncoder:
let answers: Answers = ...
do {
let data = try JSONEncoder().encode(answers)
// use data here
} catch {
print(error)
}
See Encoding and Decoding Custom Types.
if you are using swift4, You can use encodable and decodable protocol. I'm still working on a heterogeneous list of objects. But this should work for you. Make your class conform to ABEncodable.
protocol ABDecodable: Decodable {
static func decodeFromData(_ data: Data) -> Decodable?
}
protocol ABEncodable: Encodable {
static func encodeFromObject<T>(_ object: T) -> Data? where T: Encodable
}
extension ABDecodable {
static func decodeFromData(_ data: Data) -> Decodable? {
do {
return try JSONDecoder().decode(self, from: data)
}
catch {
print(error)
}
return nil
}
}
extension ABEncodable {
static func encodeFromObject<T>(_ object: T) -> Data? where T: Encodable {
do {
return try JSONEncoder().encode(object)
}
catch {
return nil
}
}
}
//MARK: Decode mapper class
//Send jsonString or data to decode it into an required Object
final class Decode<T: Decodable> {
private func decodeData(_ data: Data) -> T? {
if let klass = T.self as? ABDecodable.Type {
if let object = klass.decodeFromData(data) as? T {
return object
}
}
else {
do {
return try JSONDecoder().decode(T.self, from: data)
}
catch {
print(error)
}
}
return nil
}
func fromJsonString(_ json: String) -> T? {
guard let data = json.data(using: String.Encoding.utf8) else { return nil }
if let object = decodeData(data) {
return object
}
return nil
}
func fromData(_ data: Data) -> T? {
if let object = decodeData(data) {
return object
}
return nil
}
}
//MARK: Encode mapper class
//Send jsonString or data to decode it into an required Object
final class Encode<N:Encodable> {
private func encodeObject(_ object: N) -> Data? {
if let klass = N.self as? ABEncodable.Type {
if let data = klass.encodeFromObject(object) {
return data
}
}
else {
do {
return try JSONEncoder().encode(object)
}
catch {
print(error)
}
}
return nil
}
func toJsonString(_ object: N) -> String? {
if let data = encodeObject(object) {
return String(data: data, encoding: .utf8)
}
return nil
}
func toData(_ object: N) -> Data? {
if let data = encodeObject(object) {
return data
}
return nil
}
}

how to parse this json in swift?

I have a request
Alamofire.request(.GET,HttpHelper.baseURL+HttpHelper.tripsURL,encoding:.JSON).responseJSON {
response in
var json = JSON(data: response.data!)
print(json)
print(json["res"])
}
followed by the result
{
"res" : "[{\"name\":\"testName\",\"lastName\":\"testLastName\"},{\"name\":\"testName\",\"lastName\":\"testLastName\"}]",
"status" : "success",
"out" : "{\"name\":\"testName\",\"lastName\":\"testLastName\"}"
}
[{"name":"testName","lastName":"testLastName"},{"name":"testName","lastName":"testLastName"}]
how i can set data from res to struct or class User
struct User {
var name : String?
var lastName : String?
}
please help to solve this problem) thank you very much !!)
You can do something like that
var result: [User]()
for user in json["res"] {
let userTmp = User(name: user["name"], lastName: user["lastName"])
result.append(userTmp)
}
Regards
Basically, it would be:
class User {
var name : String?
var lastName : String?
}
var theUsers = [User]()
Alamofire.request(.GET,HttpHelper.baseURL+HttpHelper.tripsURL,encoding:.JSON)
.responseJSON { response in
var json = JSON(data: response.data!)
print(json)
theUsers = json["res"].map {
return User (name: $0["name"], lastName: $0.["lastName"])
}
})
However, along the way, you might need some typecasting. For example, maybe replace json["res"] with (json["res"] as Array<Dictionary<String,String>>) in order to keep the type checker and type inferencer happy.
I'm using native Codable protocol to do that:
class MyClass: Codable {
var int: Int?
var string: String?
var bool: Bool?
var double: Double?
}
let myClass = MyClass()
myClass.int = 1
myClass.string = "Rodrigo"
myClass.bool = true
myClass.double = 2.2
if let json = JsonUtil<MyClass>.toJson(myClass) {
print(json) // {"bool":true,"string":"Rodrigo","double":2.2,"int":1}
if let myClass = JsonUtil<MyClass>.from(json: json) {
print(myClass.int ?? 0) // 1
print(myClass.string ?? "nil") // Rodrigo
print(myClass.bool ?? false) // true
print(myClass.double ?? 0) // 2.2
}
}
And I created a JsonUtil to help me:
public struct JsonUtil<T: Codable> {
public static func from(json: String) -> T? {
if let jsonData = json.data(using: .utf8) {
let jsonDecoder = JSONDecoder()
do {
return try jsonDecoder.decode(T.self, from: jsonData)
} catch {
print(error)
}
}
return nil
}
public static func toJson(_ obj: T) -> String? {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(obj)
return String(data: jsonData, encoding: String.Encoding.utf8)
} catch {
print(error)
return nil
}
}
}
And if you have some issue with Any type in yours objects. Please look my other answer:
https://stackoverflow.com/a/51728972/3368791
Good luck :)

Resources