Alamofire JSON parsing of dictionary of arrays - swift

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

Related

Swift Decoder, trying to decode file with layers

How would I decode this json file with JSONDecoder().decode
{
"text": "pear",
"parsed": [
{
"food": {
"foodId": "food_bq6stkiaxkwhxia9q4v7wanjnew0",
"label": "Pear",
"nutrients": {
"ENERC_KCAL": 57,
"PROCNT": 0.36,
"FAT": 0.14,
"CHOCDF": 15.23,
"FIBTG": 3.1
},
"category": "Generic foods",
"categoryLabel": "food",
"image": "https://www.edamam.com/food-img/65a/65aec51d264db28bbe27117c9fdaaca7.jpg"
}
}
],
"hints": [
{
"food": {
"foodId": "food_bq6stkiaxkwhxia9q4v7wanjnew0",
"label": "Pear",
"nutrients": {
"ENERC_KCAL": 57,
"PROCNT": 0.36,
"FAT": 0.14,
"CHOCDF": 15.23,
"FIBTG": 3.1
},
"category": "Generic foods",
"categoryLabel": "food",
"image": "https://www.edamam.com/food-img/65a/65aec51d264db28bbe27117c9fdaaca7.jpg"
},
"measures": [
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_unit",
"label": "Whole",
"qualified": [
{
"qualifiers": [
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Qualifier_large",
"label": "large"
}
]
},
{
"qualifiers": [
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Qualifier_small",
"label": "small"
}
]
},
{
"qualifiers": [
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Qualifier_medium",
"label": "medium"
}
]
}
]
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_serving",
"label": "Serving"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_half",
"label": "Half"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_slice",
"label": "Slice"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_gram",
"label": "Gram"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_ounce",
"label": "Ounce"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_pound",
"label": "Pound"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_kilogram",
"label": "Kilogram"
},
{
"uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_cup",
"label": "Cup"
}
The file goes on and on.
I try to do [String] but it says it expects a dictionary,
So I put in a dictionary, then it says it expects another dictionary,
This repeats until I have this:
let posts = try! JSONDecoder().decode([String: [String: [String: String]]].self, from: data!)
Then I get the error
"Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))
Though when I go back one to do a string/data I get this error
"Expected to decode String but found a dictionary instead.", underlyingError: nil))
Help?
As mentioned in the comments you can use a quick parser, however
you need to understand why is this happening, the issue here is that you're passing [String: [String: [String: String]]] which is not valid skeleton for this key you can start breaking down each layer alone to understand first level for example [String, Any] and then start exporting each key and decode that stand alone, using the above link your models should be something like that.
// MARK: - Foo
struct Foo: Codable {
let text: String
let parsed: [Parsed]
let hints: [Hint]
}
// MARK: - Hint
struct Hint: Codable {
let food: Food
let measures: [Measure]
}
// MARK: - Food
struct Food: Codable {
let foodID, label: String
let nutrients: Nutrients
let category, categoryLabel: String
let image: String
enum CodingKeys: String, CodingKey {
case foodID = "foodId"
case label, nutrients, category, categoryLabel, image
}
}
// MARK: - Nutrients
struct Nutrients: Codable {
let enercKcal: Int
let procnt, fat, chocdf, fibtg: Double
enum CodingKeys: String, CodingKey {
case enercKcal = "ENERC_KCAL"
case procnt = "PROCNT"
case fat = "FAT"
case chocdf = "CHOCDF"
case fibtg = "FIBTG"
}
}
// MARK: - Measure
struct Measure: Codable {
let uri: String
let label: String
let qualified: [Qualified]?
}
// MARK: - Qualified
struct Qualified: Codable {
let qualifiers: [Qualifier]
}
// MARK: - Qualifier
struct Qualifier: Codable {
let uri: String
let label: String
}
// MARK: - Parsed
struct Parsed: Codable {
let food: Food
}
And you can use it simply like this:
let foo = try? newJSONDecoder().decode(Foo.self, from: jsonData)

Search a datamodel with multiple section in collectionview

I have the API response as given below:
{
"success": true,
"message": "List Loaded Successfully.",
"data": [
{
"movie_category": "Comedy",
"movie_category_code": "comedy",
"movie_list": [
{
"name": "Dhamaal",
"description": "Four lazy slacker conmen buddies who are jobless, homeless and broke learn about the secret of a hidden treasure from a dying thief and later embark on a race against time to find the mobster's buried treasure and claim it while being pursued by a determined police inspector who is hellbent to get the treasure all by himself.",
"release_year": "2007-09-07 07:00:00",
"movie_banner": "http:\/\/apis.dev.ganniti.com\/assets\/images\/dhamaal.jpg",
"movie_icon": "http:\/\/apis.dev.ganniti.com\/assets\/images\/dhamaal.jpg",
"ratings": "3.7",
"theatre_locations": [
{
"latitude": "19.099441290747407",
"longitude": "72.91631061735934"
},
{
"latitude": "18.99418207785123",
"longitude": "72.82443855189815"
},
{
"latitude": "19.065931910428006",
"longitude": "73.00118649905338"
},
{
"latitude": "19.031441299550934",
"longitude": "72.88098383204391"
}
]
},
{
"name": "Bhagam Bhag",
"description": "Champak, the owner of a theatre group, travels to London to organise a show along with Bunty and Bablya, two of his group members. They soon find themselves embroiled in a murder they did not commit.",
"release_year": "2006-12-22 08:00:00",
"movie_banner": "http:\/\/apis.dev.ganniti.com\/assets\/images\/bhagam_bhag.jpg",
"movie_icon": "http:\/\/apis.dev.ganniti.com\/assets\/images\/bhagam_bhag.jpg",
"ratings": "3.3",
"theatre_locations": [
{
"latitude": "19.099441290747407",
"longitude": "72.91631061735934"
}
]
},
{
"name": "Hulchul",
"description": "Anjali and Jai belong to two feuding families and pretend to be in love only to seek revenge. However, their plan goes awry when they really fall in love and decide to bring their families together.",
"release_year": "2004-11-26 07:30:00",
"movie_banner": "http:\/\/apis.dev.ganniti.com\/assets\/images\/hulchul.jpg",
"movie_icon": "http:\/\/apis.dev.ganniti.com\/assets\/images\/hulchul.jpg",
"ratings": "4",
"theatre_locations": [
{
"latitude": "19.099441290747407",
"longitude": "72.91631061735934"
},
{
"latitude": "18.99418207785123",
"longitude": "72.82443855189815"
}
]
}
]
},
{
"movie_category": "Action",
"movie_category_code": "action",
"movie_list": [
{
"name": "Don",
"description": "Vijay, a lookalike of criminal kingpin Don, is hired by DCP D'Silva in order to find Don's secrets. But after D'Silva dies, Vijay struggles to reveal his real identity.",
"release_year": "2006-10-20 07:00:00",
"movie_banner": "http:\/\/apis.dev.ganniti.com\/assets\/images\/don.jpg",
"movie_icon": "http:\/\/apis.dev.ganniti.com\/assets\/images\/don.jpg",
"ratings": "4.1",
"theatre_locations": [
{
"latitude": "19.099441290747407",
"longitude": "72.91631061735934"
},
{
"latitude": "18.99418207785123",
"longitude": "72.82443855189815"
},
{
"latitude": "19.065931910428006",
"longitude": "73.00118649905338"
},
{
"latitude": "19.031441299550934",
"longitude": "72.88098383204391"
}
]
},
{
"name": "Bang Bang!",
"description": "Harleen leads a boring life with her grandmother and works as a bank receptionist. However, her life takes a sudden turn after she falls in love with Rajveer, a thief.",
"release_year": "2014-10-02 08:00:00",
"movie_banner": "http:\/\/apis.dev.ganniti.com\/assets\/images\/bang-bang.jpeg",
"movie_icon": "http:\/\/apis.dev.ganniti.com\/assets\/images\/bang-bang.jpeg",
"ratings": "3.1",
"theatre_locations": [
{
"latitude": "19.099441290747407",
"longitude": "72.91631061735934"
}
]
},
{
"name": "Dhoom 2",
"description": "Mr A, a fearless thief, steals valuable artefacts and teams up with the girl he is attracted to but cannot trust. Close on their heels are three police officers trying to apprehend them.",
"release_year": "2006-11-24 07:30:00",
"movie_banner": "http:\/\/apis.dev.ganniti.com\/assets\/images\/dhoom_2.jpg",
"movie_icon": "http:\/\/apis.dev.ganniti.com\/assets\/images\/dhoom_2.jpg",
"ratings": "4.3",
"theatre_locations": [
{
"latitude": "19.099441290747407",
"longitude": "72.91631061735934"
},
{
"latitude": "18.99418207785123",
"longitude": "72.82443855189815"
}
]
}
]
}
]
}
The model class for it is as given below:
import Foundation
// MARK: - MovieModel
struct MovieModel: Codable,Equatable {
static func == (lhs: MovieModel, rhs: MovieModel) -> Bool {
return false
}
let success: Bool
let message: String
var data: [Datum]
}
// MARK: - Datum
struct Datum: Codable,Equatable {
static func == (lhs: Datum, rhs: Datum) -> Bool {
return false
}
let movieCategory, movieCategoryCode: String
let movieList: [MovieList]
enum CodingKeys: String, CodingKey {
case movieCategory = "movie_category"
case movieCategoryCode = "movie_category_code"
case movieList = "movie_list"
}
}
// MARK: - MovieList
struct MovieList: Codable,Equatable {
static func == (lhs: MovieList, rhs: MovieList) -> Bool {
return false
}
let name, movieListDescription, releaseYear: String?
let movieBanner, movieIcon: String?
let ratings: String?
let theatreLocations: [TheatreLocation]
enum CodingKeys: String, CodingKey {
case name
case movieListDescription = "description"
case releaseYear = "release_year"
case movieBanner = "movie_banner"
case movieIcon = "movie_icon"
case ratings
case theatreLocations = "theatre_locations"
}
}
// MARK: - TheatreLocation
struct TheatreLocation: Codable {
let latitude, longitude: String?
}
This data from the API will be shown with multiple sections and multiple items inside.
The collection view datasource and delegate is as follows:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return (self.moviemodel?.data.count)!
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0:
return (self.moviemodel?.data[section].movieList.count)!
default:
return (self.moviemodel?.data[section].movieList.count)!
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = alllistcollview.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MovielistCell
return cell
}
Now,there is a search textfield which will search for any keyword and filter the result to be shown in collectionview.
The search button function is as given below:
var filteredShopsA = searchDataModel?.data.filter({ item in
print("item is ",item)
$0.matchAddress(keyword)
return false
})
moviemodel?.data = filteredShopsA!
self.alllistcollview.reloadData()
extension MovieList {
func matchAddress(_ string: String?) -> Bool {
guard let string = string else { return true }
guard let name = name else { return true }
guard let desc = movieListDescription else { return true }
guard let year = releaseYear else { return true }
// guard let rating = ratings else { return true }
return (name.contains(string) || desc.contains(string) || year.contains(string))
}
}
The filter function is what i am not getting the logic correct to showcase the values on the collection view.How to search based on any text in this multiple sections of the collection view?

Codable Issue If Dictionary is Empty

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

How to fetch the following data into the tableview using Swift?

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.

Swift, ObjectMapper: path for nested array

I have json from http://openweathermap.org/, and it looks like this:
{
"coord": {
"lon": 4.85,
"lat": 45.75
},
"weather": [
{
"id": 803,
"main": "Clouds",
"description": "broken clouds",
"icon": "04d"
}
],
"base": "cmc stations",
"main": {
"temp": 278.988,
"pressure": 985.88,
"humidity": 92,
"temp_min": 278.988,
"temp_max": 278.988,
"sea_level": 1032.68,
"grnd_level": 985.88
},
"wind": {
"speed": 1.8,
"deg": 355
},
"clouds": {
"all": 80
},
"dt": 1445249394,
"sys": {
"message": 0.0037,
"country": "FR",
"sunrise": 1445234548,
"sunset": 1445273273
},
"id": 2996944,
"name": "Lyon",
"cod": 200
}
I'm using Alamofire 3.0 for networking, ObjectMapper for mapping json to model, and AlamofireObjectMapper extension to get model objects from request instead of json.
Now I need to get weather description, but don't know how to write path for it. Tried ["weather.0.description"], ["weather.$0.description"], but these are not working, and my weather description is nil.
Here is my model:
class WCurrentWeather: Mappable {
var weatherDescription: String?
var tempriture: Double?
var clouds: Double?
var rain: Double?
var humidity: Double?
var pressure: Double?
var sunrise: NSDate?
var sunset: NSDate?
required init?(_ map: Map){
}
func mapping(map: Map) {
weatherDescription <- map["weather.0.description"]
tempriture <- map["main.temp"]
clouds <- map["clouds.all"]
rain <- map["rain.1h"]
humidity <- map["main.humidity"]
pressure <- map["main.pressure"]
sunrise <- (map["sys.sunrise"], DateTransform())
sunset <- (map["sys.sunset"], DateTransform())
}
}
and my request:
Alamofire.request(.GET, URL, parameters: params)
.responseObject { (response: WCurrentWeather?, error: ErrorType?) in
completionHandler(response, error)
}
Is there any way to get this working.
Thanks in advance.
I have forked ObjectMapper, and added this functionality, and thanks to Tristan Himmelman it is already merged, so now you can access to nested array elements like this map["weather.0.description"]
What you are looking for is:
let jsonDict = // You dict
jsonDict["weather"][0]["description"]
I am just giving you a direction. You would need to align it with Swift type casting rules. Good luck!