How to Display Json Response using get method - ios

Here is my Response and I want to print the response using array I want to take some details in the response like "Id" and "available" and "leaves" and I have to show in a label in my VC
{
"id": 1,
"emp_id": "001",
"termination_date": "active",
"blood_group": "A+",
"rating": 0,
"noOfStars": 0,
"starOfMonth": false,
"gender": "Female",
"expertise": "",
"experience": "",
"leaves": 0,
"available": 5,
"compoff": 0,
"earnedLeaves": null,
"wfh": 0
}
my code is
struct jsonstruct8:Decodable {
var available: String
var leaves: String
}
var arrdata = [jsonstruct8]()
func getdata(){
let url = URL(string: "MY URL")
URLSession.shared.dataTask(with: url!) { (data, response, error )in
do{if error == nil{
self.arrdata = try JSONDecoder().decode([jsonstruct8].self, from: data!)
for mainarr in self.arrdata{
print(mainarr.available,":",mainarr.leaves)
print(data)
}
}
}catch{
print("Error in get json data")
}
}.resume()
}
I am getting "Error in get json data"

Sample JSON:
{
"id": 1,
"emp_id": "001",
"termination_date": "active",
"blood_group": "A+",
"rating": 0,
"noOfStars": 0,
"starOfMonth": false,
"gender": "Female",
"expertise": "",
"experience": "",
"leaves": 0,
"available": 5,
"compoff": 0,
"earnedLeaves": null,
"wfh": 0
}
Model:
struct Employee: Codable {
let id: Int
let empId: String
let terminationDate: String
let available: Int
let leaves: Int
//add other properties as well....
}
Parsing:
if let data = data {
if let data = data {
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
var employee = try JSONDecoder().decode(Employee.self, from: data)
print("\(employee.available) : \(employee.leaves)") //here you can modify then employee details...
} catch {
print(error)
}
}
}
Edit:
Always update the UI on main thread.
DispatchQueue.main.async {
self.totalLeaves.text = "\(employee.leaves)"
}

Related

Section Index Titles after Parsing JSON Swift

I am trying to create dynamic sectionIndexTitles for my tableView. I want to get all of the character names and then get the first letter of the name to create sectionIndexTitles, but I am having difficulty doing so.
I get the JSON from URL:
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "mytestURL")
let configuration = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: configuration)
session.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
characters = try JSONDecoder().decode([CharacterStats].self, from: data!)
DispatchQueue.main.async {
completed()
}
} catch {
print("JSON Error")
}
}
}.resume()
}
The JSON looks like this:
[
{
"name": "Batman",
"type": "DC",
"gender": "male",
},
{
"name": "Captain America",
"type": "Marvel",
"gender": "male",
},
{
"name": "Captain Marvel",
"type": "Marvel",
"gender": "female",
}
]
And my struct looks like this:
public struct CharacterStats: Decodable {
let name: String
let type: String?
let gender: String?
}
I have posted a sample code for this here https://github.com/achuaswani/ListItemExample.git

Codable protocol error "The given data was not valid JSON."

I am getting an error having to do with the codable protocol
struct UsersResponse : Codable {
let directoryItems: [DirectoryItem]
enum CodingKeys : String, CodingKey {
case directoryItems = "directory_items"
}
}
struct DirectoryItem : Codable {
let user : User
}
struct User : Codable {
let id: Int
let userName: String
let imageURL: String
enum CodingKeys : String, CodingKey {
case id = "id"
case userName = "username"
case imageURL = "avatar_template"
}
}
This is my JSON response on Postman, the keys are well defined in the protocol, honestly I don't really know what is going wrong.
Here is the full error object.
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
{
"directory_items": [
{
"id": 0,
"likes_received": 0,
"likes_given": 0,
"topics_entered": 0,
"topic_count": 0,
"post_count": 0,
"posts_read": 0,
"days_visited": 0,
"user": {
"id": 0,
"username": "string",
"avatar_template": "string",
"name": "string",
"title": { }
}
}
],
"total_rows_directory_items": 0,
"load_more_directory_items": "string"
}
This is where I ask for the data
func send<T: APIRequest>(request: T, completion: #escaping(Result<T.Response, Error>) -> ()) {
let request = request.requestWithBaseUrl()
let task = session.dataTask(with: request) { data, response, error in
do {
guard let response = response as? HTTPURLResponse else { return }
if response.statusCode >= 400 && response.statusCode < 500 {
if let data = data {
let errorModel = try JSONDecoder().decode(DiscourseAPIError.self, from: data)
DispatchQueue.main.async {
let errorString = errorModel.errors?.joined(separator: ", ") ?? "Unknown error"
completion(.failure(NSError(domain: "request error", code: 0, userInfo: [NSLocalizedDescriptionKey: errorString])))
}
} else {
DispatchQueue.main.async {
completion(.failure(SessionAPIError.emptyData))
}
}
}
if let data = data {
let json = try JSONSerialization.jsonObject(with: data) as! Dictionary<String, AnyObject>
print(json)
let model = try JSONDecoder().decode(T.Response.self, from: data)
DispatchQueue.main.async {
completion(.success(model))
}
} else {
DispatchQueue.main.async {
completion(.failure(SessionAPIError.emptyData))
}
}
} catch {
DispatchQueue.main.async {
print(error)
completion(.failure(error))
}
}
}
task.resume()
}
}
In a playground, I copied your sample json and got this output:
let sample = """
{
"directory_items": [
{
"id": 0,
"likes_received": 0,
"likes_given": 0,
"topics_entered": 0,
"topic_count": 0,
"post_count": 0,
"posts_read": 0,
"days_visited": 0,
"user": {
"id": 0,
"username": "string",
"avatar_template": "string",
"name": "string",
"title": { }
}
}
],
"total_rows_directory_items": 0,
"load_more_directory_items": "string"
}
""".data(using: .utf8)!
let json = try! JSONSerialization.jsonObject(with: sample, options: [])
print(json)
//{
// "directory_items" = (
// {
// "days_visited" = 0;
// id = 0;
// "likes_given" = 0;
// "likes_received" = 0;
// "post_count" = 0;
// "posts_read" = 0;
// "topic_count" = 0;
// "topics_entered" = 0;
// user = {
// "avatar_template" = string;
// id = 0;
// name = string;
// title = {
// };
// username = string;
// };
// }
// );
// "load_more_directory_items" = string;
// "total_rows_directory_items" = 0;
//}
The json is definitely valid. I'd be willing to bet that the suggested comment by Paulw11 is absolutely on point. There's probably a leading value that's breaking the rest of the JSON.
A potential simple way to clean it up would be to convert to a utf8 string and back to data, but that's more cpu cycles than necessary, especially if this is a frequently run code snippet.
There would be other, more efficient ways to clean it up, but this would at least let you confirm if this is a valid path forward or not.
(ps. please don't judge my force unwraps! it's just a playground and proof of concept!)

How To Pass JSON Response to my ArrayList in IOS Swift

I am new to ios swift. Here I Have a jsonResponse, which has came from my web service. Now I want to parse this response to my array. Here is my code of model class.
struct OrderItem:Codable{
let purchaseId : Int
let purchasedDate: String
let purchasedStatus: Int
let purchasedItems: [String]
}
Here my response from my webservice.
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error)-> Void in
do{
// let data = try JSONSerialization.jsonObject(with: data!, options: []) as! Array<Any>
let decoder = JSONDecoder()
self.orderMenu = try! decoder.decode([OrderItem].self, from: data!)
print("===122===",self.orderMenu)
}catch{
print("ERROR")
}
DispatchQueue.main.async {
self.MytableView.reloadData()
}
}).resume()
this is my jsonResponse
[
{
"purchaseId": 19,
"purchasedDate": "2018-07-27 23:48",
"purchasedStatus": 0,
"purchasedItems": [
{
"purchasedItemsId": 72,
"purchasedCount": 2,
"purchasedPrice": 34,
"purchasedGST": 3,
"itemDescription": "abcd2",
"itemCategoryDescription": "Cloths_Type_6",
},
{
"purchasedItemsId": 73,
"purchasedCount": 3,
"purchasedPrice": 44,
"purchasedGST": 9.1,
"itemDescription": "Item2",
"itemCategoryDescription": "Flags",
},
{
"purchasedItemsId": 74,
"purchasedCount": 4,
"purchasedPrice": 123,
"purchasedGST": 3,
"itemDescription": "Item3",
"itemCategoryDescription": "Food",
},
{
"purchasedItemsId": 75,
"purchasedCount": 5,
"purchasedPrice": 42,
"purchasedGST": 9.1,
"itemDescription": "Item4",
"itemCategoryDescription": "Flags",
}
]
}
]
Some One please get me a solution, Any solution is appreciated.
purchasedItems is an array of dictionaries, for the dictionary you have to create another struct
struct OrderItem : Decodable {
let purchaseId : Int
let purchasedDate: String
let purchasedStatus: Int
let purchasedItems: [PurchasedItem]
}
struct PurchasedItem : Decodable {
let purchasedItemsId, purchasedCount, purchasedPrice : Int
let purchasedGST : Double
let itemDescription, itemCategoryDescription : String
}
Then you can print for example the itemDescription from orderMenu with
for order in orderMenu {
for item in order.purchasedItems {
print(item.itemDescription)
}
}
And - once again – print always the error instance in the catch block
} catch {
print(error)
}

error when trying to post json to server Code=3840

am posting to server some data as json and getting json response from it .. like this:
let decoded4 = userDefaults.object(forKey: "nationalities") as! Data
let decodedNationalities = NSKeyedUnarchiver.unarchiveObject(with: decoded4) as! [Nationality]
for nationality in decodedNationalities {
if nationality.name == self.nationality {
idnationality = nationality.id
}
}
if conttype == "Single visit"{
conttype = "single_visit"
}else {
conttype = "multi_visit"
}
print(days)
if days.hasPrefix(","){
days.remove(at: days.startIndex)
}
if days.hasSuffix(","){
days.remove(at: days.endIndex)
}
let todosEndpoint: String = "my link"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
todosUrlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
let newTodo: [String: Any] = ["email": UserDefaults.standard.string(forKey: "CustomerEmail")!, "password": UserDefaults.standard.string(forKey: "CustomerPassword")!, "id_address": addid, "quantity_staff": maidn, "id_service": idservice, "id_region": idregion, "id_city": idcity, "id_nationality": idnationality, "start_date": "2018-05-09", "contract_type": "single_visit", "shift_type": "day", "weekdays": days, "starttime": starttime, "endtime": endtime]
print(newTodo)
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /public/api/register_customer")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let status = receivedTodo["success"] as? Int else {
print("Could not get status from JSON")
return
}
if status == 0{
DispatchQueue.main.async {
self.performSegue(withIdentifier: "segueerr", sender: self)
}
print("The status is: 0")
guard let messages = receivedTodo["message"] as? String else {
print("Could not get messages from JSON")
return
}
print(messages)
}
else {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "successsegue", sender: self)
}
print("Success!")
}
} catch {
print(error)
return
}
}
task.resume()
}
when i run it .. it posted the correct values which are:
["email": "lamatat#gmail.com", "id_service": 3, "id_region": 1,
"id_city": 3, "id_address": 22, "weekdays": "tue", "contract_type":
"single_visit", "id_nationality": 4, "password":
"4169faf51ce3c5fb8850451b441a363906f16d69", "endtime": 12,
"starttime": 8, "shift_type": "day", "quantity_staff": 1,
"start_date": "2018-05-09"]
i got error as response which is:
Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}
when am sure 100% of the values and tried the excat same one in postman and got this as result:
{
"success": true,
"message": "Adding new Order was successful.",
"id_order": 210,
"shift": {
"id": 31,
"id_region": 1,
"id_city": 3,
"id_nationality": 4,
"id_service": 3,
"shift_date": "2018-05-09 00:00:00",
"shift_type": "day",
"weekday": "tue",
"quantity_staff": 64,
"lead_hours": 10,
"created_at": "2018-05-07 12:54:48",
"updated_at": "2018-05-09 10:47:37",
"deleted_at": null,
"price_per_visit": 50
}
}
why would i got this error from the app?!
someone please help! i have no clue whats wrong!
Just try this instead for your one. Change this to
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
to
let jsonTodo = JSONStringify(value: newTodo as AnyObject)
Add This method inside your controller
func JSONStringify(value: AnyObject,prettyPrinted:Bool = false) -> String{
let options = prettyPrinted ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization.WritingOptions(rawValue: 0)
if JSONSerialization.isValidJSONObject(value) {
do{
let data = try JSONSerialization.data(withJSONObject: value, options: options)
if let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
return string as String
}
}catch {
print("error")
//Access error here
}
}
return ""
}
*But if you still facing same issue. Change your httoBody also like this
request.httpBody = jsonTodo.data(using: String.Encoding.utf8)

parsing two different object's array from JSON response

I'm trying to parse JSON data .. i know how to do it for one object array .. but i don't know how to do it for a response which has to two arrays of different objects ..
for example this is the JSON:
{
"shifts": [
{
"id": 4,
"region": "Eastren",
"city": "Khobar",
"nationality": "1",
"id_service": 2,
"shift_date": "2018-04-05",
"shift_type": "night",
"weekday": "sunday",
"quantity_staff": 8,
"lead_hours": 2,
"created_at": "2018-04-23 11:46:20",
"updated_at": "2018-04-24 08:46:14",
"deleted_at": null
},
{
"id": 5,
"region": "Eastren",
"city": "Khobar",
"nationality": "Phili",
"id_service": 2,
"shift_date": "2018-04-04",
"shift_type": "night",
"weekday": "sunday",
"quantity_staff": 8,
"lead_hours": 2,
"created_at": "2018-04-23 11:47:25",
"updated_at": "2018-04-23 12:53:05",
"deleted_at": "2018-04-23"
}
],
"prices": [
{
"id": 1,
"id_service": 2,
"nationality": "Phili",
"price": 150,
"created_at": "2018-04-23 11:43:40",
"updated_at": "2018-04-23 11:43:40",
"deleted_at": null
}
]
}
it has two array of objects .. shifts and prices .. how to parse each one of them?
my function:
func GetShiftsAndPrices(id: Int){
let todosEndpoint: String = "my link"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
todosUrlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
let newTodo: [String: Any] = ["id_service": id]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /public/api/login_customer")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
//WHAT SHOULD I DO HERE?
print("Success!")
} catch {
print("error parsing response from POST")
return
}
}
task.resume()}
}
I have classes for both shift and price .. and know how to get each one if it was alone in a response .. like:
shifts:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let shifts1 = try decoder.decode([Shifts].self, from: responseData)
var shifts = [Shift]()
for shift in shifts1{
let newshift = Shift(id: shift.id, region: shift.region, city: shift.city, nationality: shift.nationality, idService: shift.idService, shiftDate: shift.shiftDate, shiftType: shift.shiftType, weekday: shift.weekday, quantityStaff: shift.quantityStaff, leadHours: shift.leadHours)
shifts.append(newshift)
}
let userDefaults = UserDefaults.standard
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: shifts)
userDefaults.set(encodedData, forKey: "shifts")
userDefaults.synchronize()
let decoded = userDefaults.object(forKey: "shifts") as! Data
let decodedShift = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! [Shift]
for shift in decodedShift {
print(shift.id)
}
prices:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let prices1 = try decoder.decode([Prices].self, from: responseData)
print("1")
var prices = [Price]()
for price in prices1{
let newprice = Price(id: price.id, idService: price.idService,nationality: price.nationality, price: price.price)
print("2")
prices.append(newprice)
print(newprice.nationality)
}
let userDefaults = UserDefaults.standard
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: prices)
userDefaults.set(encodedData, forKey: "prices")
userDefaults.synchronize()
let decoded = userDefaults.object(forKey: "prices") as! Data
let decodedPrice = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! [Price]
for price in decodedPrice {
print(price.nationality)
}
how to parse them both in one JSON response.. I'm new to this.. can someone please tell me how to do it?
Use an umbrella struct
struct Root : Decodable {
let shifts : [Shift]
let prices : [Price]
}
and two different structs for shifts and prices:
struct Shift : Decodable {
let id: Int
let region, city, nationality : String
let idService : Int
let shiftDate, shiftType, weekday : String
let quantityStaff, leadHours : Int
let createdAt, updatedAt : String
let deletedAt : String?
}
struct Price : Decodable {
let id, idService : Int
let nationality : String
let price : Int
let createdAt, updatedAt : String
let deletedAt : String?
}
To decode the JSON write
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder .decode(Root.self, from: responseData)
print(result.prices)
print(result.shifts)
} catch { print(error) }
Further I recommend to decode keys like shiftType and weekday directly into an enum for example
enum ShiftType : String, Decodable {
case day, night
}
struct Shift : Decodable {
let id: Int
...
let shiftType : ShiftType
...
let shifts = responseJson.valueFor(key:"shifts") as? [String] ?? []
let prices = responseJson.valueFor(key:"prices") as? [String] ?? []
print("\(shifts) and \(prices)")

Resources