I am trying to make use of Codable protocol with swift
Issue is I need to dequeue nested dictionary getting from server. Suppose
Data : its values and inside data one more dictionary Goal
if goal inside data has a value it works fine
but in case goal inside Dictionary turns out to be empty it says
Error - The data couldn’t be read because it isn’t in the correct format.
My JSON Response
case 1 - Without Goal - Issue facing
{
"code": "1",
"message": "Data fetched successfully",
"data": {
"id": "2",
"organization_id": "2",
"first_name": "iOS",
"last_name": "test",
"user_name": "iOS",
"email": "ios#gmail.com",
"password": "4399578cc31cf62535a7dba566e4aea0",
"security_hash": "fdf4d3bb5731de23b42771e01d9e0c3e",
"google_id": "525256562",
"facebook_id": "525256562",
"access_code": "iosTest1",
"gender": "",
"contact": "1234567890",
"user_profile": "",
"profile_thumb": "",
"dob": "",
"weight": "",
"height": "",
"is_corporate": "0",
"status": "1",
"created_at": "2018-04-19 10:37:46",
"updated_at": "2018-12-10 15:32:59",
"profile_image_url": "",
"goal": {}
}
}
case 2: When Goal has values
{
"code": "1",
"message": "Data fetched successfully",
"data": {
"id": "2",
"organization_id": "2",
"first_name": "iOS",
"last_name": "test",
"user_name": "ios",
"email": "ios#gmail.com",
"password": "4399578cc31cf62535a7dba566e4aea0",
"security_hash": "fdf4d3bb5731de23b42771e01d9e0c3e",
"google_id": "525256562",
"facebook_id": "525256562",
"access_code": "iosTest1",
"gender": "",
"contact": "1234567890",
"user_profile": "",
"profile_thumb": "",
"dob": "",
"weight": "",
"height": "",
"is_corporate": "0",
"status": "1",
"created_at": "2018-04-19 10:37:46",
"updated_at": "2018-12-10 15:32:59",
"profile_image_url": "",
"goal": {
"id": "4",
"client_id": "2",
"ambition_to_achieve": "adfadf",
"current_assessment": "dfadfafa",
"expected": "fasdfasdfsafsf",
"expected_date": "2018-12-15",
"description": "asdfadsfadf",
"goal_status": "1",
"created_at": "2018-12-12 18:15:36",
"updated_at": ""
}
}
}
///--- Main Class Which handle main Data
struct LoggedUser: Codable {
var dob, weight, height: String?
let accesscode, contact, contactcode, email, facebookid: String?
let firstname, gender, googleid, id, iscorporate, lastname: String?
let organizationid, password, profilethumb, securityhash, status: String?
let username, userprofile : String?
let goal:Goal?
enum CodingKeys: String, CodingKey {
case accesscode="access_code"
case contact, dob, email, gender, height, id, password, status, weight, goal
case contactcode="contact_code"
case facebookid="facebook_id"
case firstname="first_name"
case googleid="google_id"
case iscorporate="is_corporate"
case lastname="last_name"
case organizationid="organization_id"
case profilethumb="profile_thumb"
case securityhash="security_hash"
case username="user_name"
case userprofile="user_profile"
}
}
/// ---> Class For Goal
struct Goal: Codable {
let id, clientID, ambitionToAchieve, currentAssessment: String?
let expected, expectedDate, description, goalStatus: String?
let createdAt, updatedAt: String?
enum CodingKeys: String, CodingKey {
case id
case clientID = "client_id"
case ambitionToAchieve = "ambition_to_achieve"
case currentAssessment = "current_assessment"
case expected
case expectedDate = "expected_date"
case description
case goalStatus = "goal_status"
case createdAt = "created_at"
case updatedAt = "updated_at"
}
}
----> Update
Just a correction
Posting actual response
Get My Goal API Response ==> ["code": 1, "message": Data fetched successfully, "data": {
"access_code" = iosTest4;
contact = 1111111111;
"contact_code" = 91;
"created_at" = "2019-02-12 14:42:34";
dob = "1996-05-10";
email = "testing#email.com";
"facebook_id" = "";
"first_name" = iOS;
gender = 1;
goal = (
);
"google_id" = "";
height = "5.10";
id = 35;
"is_corporate" = 0;
"last_name" = test;
"organization_id" = 0;
password = 915729f1e48bda300dabaac7d1ac8358;
"profile_image_url" = "";
"profile_thumb" = "";
"security_hash" = 991f5cd86be6fc485633d1946dc84a7a;
status = 1;
"updated_at" = "2019-02-14 16:25:01";
"user_name" = "testing#email.com";
"user_profile" = "";
weight = "0.10";
}]
As I was getting 2 Different Formats
when Goal is empty it gives me an array
when had some value it returns a Dictionary
So I Explicitly Created my Own Data() by manually Casting goal as [String:Any]
var loginDataDict = jsonDict["data"] as! [String:Any]
if let goalData = loginDataDict["goal"] as? [String:Any] {
loginDataDict.updateValue(goalData, forKey: "goal")
} else {
loginDataDict.updateValue([:], forKey: "goal")
}
This Might not be a proper solution but may help someone if having same scenario as I had here
Related
For my Calendar i get the following data as JSON from the Backend (*JAVA-Type = Map<LocalDate, List<Event>>):
{
"2022-05-28": [
{
"id": 2,
"title": "Multi day Event",
"fromDate": "2022-05-27T12:22:03.873569",
"toDate": "2022-05-28T11:22:03.873569",
"room": {
"id": 1,
"name": "TestRoom",
},
"user": {
"id": 1,
"name": "Andi",
"city": "",
"email": "test#gmail.com",
},
"eventType": "sozial"
}
],
"2022-05-27": [
{
"id": 2,
"title": "Multi day Event",
"fromDate": "2022-05-27T12:22:03.873569",
"toDate": "2022-05-28T11:22:03.873569",
"room": {
"id": 1,
"name": "TestRoom",
},
"user": {
"id": 1,
"name": "Andi",
"city": "",
"email": "test#gmail.com",
},
"eventType": "sozial"
},
{
"id": 1,
"title": "Testevent",
"fromDate": "2022-05-27T11:21:04.573754",
"toDate": "2022-05-27T12:21:04.573754",
"room": {
"id": 1,
"name": "TestRoom",
},
"user": {
"id": 1,
"name": "Andi",
"city": "",
"email": "test#gmail.com",
},
"eventType": "normal"
}
],
}
My Event Class looks like:
Class Event {
int id;
String title;
DateTime fromDate;
DateTime toDate;
Room room;
User user;
String eventType;
}
Now i need the same structure i had in the Backend (Map<DateTime, <List<Event>>) for my Calendar widget and i have no real clue on how to do it. I know how to convert json data into an object if i get a list of an object, but how can i store the date as key of the resulting map?
My code by now:
Future<Map<DateTime, List<Event>>> getEvents(DateTime _fromDate, DateTime
_endDate) async {
String _from = _fromDate.toString().split('.').first;
String _end = _endDate.toString().split('.').first;
final response = await get('${_url}calendar/events/$_from/$_end',
headers: {HttpHeaders.authorizationHeader: 'Bearer $_bearer'});
if (response.status.hasError) {
return Future.error('${response.statusText}');
} else {
final parsed = jsonDecode(response.body);
return parsed;
}
}
You need to do something like that:
var json = {...}; // <-- json obj
// method to parse data to map with list Event
dynamic fromJson(Map<String, dynamic> json){
var map = new Map();
json.keys.forEach((key){
// key is the date
map[key] = json[key].map((e) => Event.fromJson(e)).toList(); // <- need to create a method fromJson in your Event class
});
return map;
}
(...)
class Event {
int id;
String title;
DateTime fromDate;
DateTime toDate;
Room room;
User user;
String eventType;
fromJson(Map<String, dynamic> json) => Event(...); // <- parse json to Event class
}
Trying to parse this dictionary of Arrays.
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
},
{
"id": 2,
"name": "Ervin Howell",
"username": "Antonette",
"email": "Shanna#melissa.tv",
"address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
"geo": {
"lat": "-43.9509",
"lng": "-34.4618"
}
},
"phone": "010-692-6593 x09125",
"website": "anastasia.net",
"company": {
"name": "Deckow-Crist",
"catchPhrase": "Proactive didactic contingency",
"bs": "synergize scalable supply-chains"
}
}
]
Is it possible to parse this Dictionary of arrays into an array of Model ([Artist]) for convenience.
AF.request(Constants.kGetArtistsAPIEndPoint).responseDecodable(of: [ArtistsResponse].self) { (response) in
guard let artistsResponse = response.value else { return }
print("ArtistsResponse is \(artistsResponse)")
if let artists = artistsResponse.first {
completion(artists)
}
}
My Artist class looks like below
struct ArtistsResponse: Codable {
let artists: [Artist]
}
struct Artist: Codable {
let artistId: String
let name: String
let username: String?
let emailId: String?
let address: Address?
let phone: String?
let website: String?
let company: Company?
enum CodingKeys: String, CodingKey {
case artistId = "id"
case name
case username
case emailId
case address
case phone
case website
case company
}
}
struct Address: Codable {
let street: String
let suite: String?
let city: String
let zipcode: String
let geo: GeoLocation
enum CodingKeys: String, CodingKey {
case street
case suite
case city
case zipcode
case geo
}
}
struct GeoLocation: Codable {
let latitude: Double
let longitude: Double
enum CodingKeys: String, CodingKey {
case latitude = "lat"
case longitude = "lng"
}
}
Tried with above code but it fails with below error code
Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))))
Consider the following data structure:
{
"company": {
"idCompany1": {
"data": {
"address": "",
"companyName": "Company 1",
"logo": "assets/Logo1.png",
"nit": "",
"phone": ""
}
},
"idCompany2": {
"data": {
"address": "",
"companyName": "Company 2",
"logo": "assets/Logo2.png",
"nit": "",
"phone": ""
}
},
"idCompany3": {
"data": {
"address": "",
"companyName": "Company 3",
"logo": "assets/Logo3.png",
"nit": "",
"phone": ""
}
}
},
"users": {
"idUser1": {
"data": "user1#test.com",
"companies": {
"idCompany1": true,
"idCompany3": true
}
},
"idUser2": {
"data": "user2#test.com",
"companies": {
"idCompany2": true
}
}
}
}
Basically what I need to do in the case of user1 is to read the data of the companies to which it belongs, this is Company 1 and Company 3. How can I do that?
The way I found, is by obtaining a list of IDs of those companies, which I have in listaIdEmpresas and then consulting each one through a forEach loop in the following way:
Future<List<EmpresaDatosModel>> cargarEmpresaDatosListado(List<String> listaIdEmpresas) async {
final List<EmpresaDatosModel> listaEmpresas = new List();
listaIdEmpresas.forEach((id) async {
Query resp = db.child('company/$id/data');
final snapshot = await resp.once();
final temp = EmpresaDatosModel.fromJson(Map<String,dynamic>.from(snapshot.value));
temp.idEmpresa = id;
listaEmpresas.add(temp);
print('${temp.companyName} up');
await resp.once().then((snapshot) {});
});
listaEmpresas.forEach((element) {print('Emp ${element.companyName}');});
return listaEmpresas;
}
However, this process is not efficient and I need to manage a delay for waiting the loop.
What would be the right way to do query data from a list of Ids directly?
I'm posting parameters to my API with Alamofire.
I'm trying to use different parameters based on if we have a password or not. See a part of my code below:
let parameters: [String: Any] = [:]
if (password != nil) {
let parameters: [String : Any] = [
"displayName": firstName + " " + lastName,
"firstName": firstName,
"lastName": lastName,
"password": password ?? "",
"passwordConfirmation": password ?? "",
"location": location?.json ?? [:],
"wallet": wallet?.json ?? [:],
"gender": gender.rawValue,
"avatarId": 1,
"email": email,
"authExternalAccessToken": accessToken ?? "",
]
} else {
let parameters: [String : Any] = [
"displayName": firstName + " " + lastName,
"firstName": firstName,
"lastName": lastName,
"password": password ?? "",
"passwordConfirmation": password ?? "",
"location": location?.json ?? [:],
"wallet": wallet?.json ?? [:],
"gender": gender.rawValue,
"avatarId": 1,
"email": email,
"authExternalAccessToken": accessToken ?? "",
]
}
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [
when I set a breakpoint behind the if/else statement it says there are 0 elements. What am I doing wrong? I can probably also speed this up as it takes longer to build this way.
In if-else block you are creating new constant parameters, so change your code like this.
let parameters: [String: Any]
if (password != nil) {
parameters = [
"displayName": firstName + " " + lastName,
"firstName": firstName,
"lastName": lastName,
"password": password ?? "",
"passwordConfirmation": password ?? "",
"location": location?.json ?? [:],
"wallet": wallet?.json ?? [:],
"gender": gender.rawValue,
"avatarId": 1,
"email": email,
"authExternalAccessToken": accessToken ?? "",
] as [String : Any]
} else {
parameters = [
"displayName": firstName + " " + lastName,
"firstName": firstName,
"lastName": lastName,
"password": password ?? "",
"passwordConfirmation": password ?? "",
"location": location?.json ?? [:],
"wallet": wallet?.json ?? [:],
"gender": gender.rawValue,
"avatarId": 1,
"email": email,
"authExternalAccessToken": accessToken ?? "",
] as [String : Any]
}
You are redeclaring your parameters variable rather then assigning to it.
You can only assign to a let variable a single time, however that assignment does not have to occur as part of the same statement as the declaration.
For example:
let parameters: [String: Any]
if (password != nil) {
parameters = [
"displayName": firstName + " " + lastName,
"firstName": firstName,
"lastName": lastName,
"password": password ?? "",
"passwordConfirmation": password ?? "",
"location": location?.json ?? [:],
"wallet": wallet?.json ?? [:],
"gender": gender.rawValue,
"avatarId": 1,
"email": email,
"authExternalAccessToken": accessToken ?? "",
]
} else {
parameters = [
"displayName": firstName + " " + lastName,
"firstName": firstName,
"lastName": lastName,
"password": password ?? "",
"passwordConfirmation": password ?? "",
"location": location?.json ?? [:],
"wallet": wallet?.json ?? [:],
"gender": gender.rawValue,
"avatarId": 1,
"email": email,
"authExternalAccessToken": accessToken ?? "",
]
}
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [
First lets see the json data.
[
{
"id": "244",
"name": "PIZZAS",
"image": "",
"coupon": "1",
"icon": "",
"order": "1",
"aname": "",
"options": "2",
"subcategory": [
{
"id": "515",
"name": "MARGARITA",
"description": "Cheese and Tomato",
"image": "",
"icon": "",
"coupon": "1",
"order": "1",
"aname": "",
"options": "2",
"item": [
{
"id": "1749",
"name": "9 Inch Thin & Crispy Margarita",
"description": "",
"price": "3.40",
"coupon": "1",
"image": "",
"options": "2",
"order": "1",
"addon": "495",
"aname": "",
"icon": ""
}]
}]
}]
I have used Alamofire and getting response through this code below:
Alamofire.request(.GET, myUrl, parameters:nil , encoding: .JSON)
.validate()
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
if let jsonResult = response as? Array<Dictionary<String,String>> {
let Name = jsonResult[0]["name"]
let ID = jsonResult[0]["id"]
let Order = jsonResult[0]["order"]
print("JSON: Name: \(Name)")
print("JSON: ID: \(ID)")
print("JSON: Order: \(Order)")
}
}
But after getting response data I am not able to get any value. Here I want to fetch all data like name,id and subcategory - how to implement this?
You have more than one problem there.
First response is of type Response<Anyobject, NSError>, it's not the parsed object you're looking for, instead you should use response.result.value as you did for the log.
Second even if you tried to cast response.result.value to Array<Dictionary<String,String>> it will not pass because in your json data you have ann inner array subcategory which cannot be casted to Dictionary<String, String>
This code should work for you:
Alamofire.request(.GET, myUrl, parameters:nil , encoding: .JSON)
.validate()
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
let array = response.result.value as! Array<NSDictionary>
for item in array
{
let Name = item["name"]
let ID = item["id"]
let Order = item["order"]
let Subcategories = item["subcategory"] as! Array<NSDictionary>
for subCategory in Subcategories
{
let subId = subCategory["id"]
}
}
}
And here is the results in the playground:
Cheers.