Swift, ObjectMapper: path for nested array - swift

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!

Related

Alamofire JSON parsing of dictionary of arrays

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

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?

restkit, how to access object in response without object mapping

How can I access the original response json data without using object mapping. I have the followingresponse data. it contains a nextSyncToken which used to execute the query and a collection of items (within the items session).
I created a object mapping of the Item object which represent the contents in items. However, I also need the nextSyncToken field. How can I access it without object mapping. Since the syncToken has no relationship with object mapping. How can i deal with this.
{
"kind": "calendar#events",
"nextSyncToken": "COib8eSw78gCEOib8eSw78gCGAU=",
"items": [
{
"id": "_74rk4cpg84o42b9k8or3gb9k74s34b9p8ks34b9m851kac9m64rk4ci36g",
"created": "2010-04-16T11:09:31.000Z",
"updated": "2010-04-16T11:10:27.487Z",
"summary": "iCal test 1",
"start": {
"dateTime": "2010-03-16T21:00:00+08:00"
},
"end": {
"dateTime": "2010-03-16T22:00:00+08:00"
}
},
{
"id": "_752j2h1j6cq4cba68csjeb9k8p33eba1692k4ba284qj8ea688rj2chh6c",
"status": "confirmed",
"created": "2011-10-18T09:36:02.000Z",
"updated": "2011-10-18T09:36:02.000Z",
"summary": "New Event",
"start": {
"dateTime": "2011-10-18T03:45:00+08:00"
},
"end": {
"dateTime": "2011-10-18T08:15:00+08:00"
}
}
]
}
My code of mapping:
let eventMapping = RKEntityMapping(forEntityForName: "SGEvent", inManagedObjectStore: managedObjectStore)
eventMapping.addAttributeMappingsFromDictionary([
"id": "identifier",
"summary": "summary",
"created": "createdAt",
"updated": "updatedAt",
"location": "location",
"description": "notes",
"start.date": "allDayStart",
"end.date": "allDayEnd"
])
let startTimeMapping = RKAttributeMapping(fromKeyPath: "start.dateTime", toKeyPath: "startTime")
startTimeMapping.valueTransformer = self.googleDateTransformer
eventMapping.addPropertyMapping(startTimeMapping)
let endTimeMapping = RKAttributeMapping(fromKeyPath: "end.dateTime", toKeyPath: "endTime")
endTimeMapping.valueTransformer = self.googleDateTransformer
eventMapping.addPropertyMapping(endTimeMapping)
eventMapping.identificationAttributes = ["identifier"]
let responseDescriptor = RKResponseDescriptor(mapping: eventMapping, method: .GET,
pathPattern: "calendars/:calendarId/events", keyPath: "items",
statusCodes: RKStatusCodeIndexSetForClass(RKStatusCodeClass.Successful))
objectManager.addResponseDescriptor(responseDescriptor)
My request operation:
objectManager.getObjectsAtPath("calendars/\(identifier)/events",
parameters: [self.ACCESS_TOKEN: accessToken], success: { (operation, results) -> Void in
callback?(results: nil, error: nil)
}) { (_, error) -> Void in
print(error)
}
Generally you would add other response descriptors with appropriate mappings to deal with this issue.
When using objectManager.getObjectsAtPath you can get the raw data, assuming that you have some other response descriptor which will result in the success block being called, by navigating to the response data in the HTTP operation (which you can then unpack however you see fit):
operation.HTTPRequestOperation.responseData
(or use responseString instead of responseData).
if let dict = try? NSJSONSerialization.JSONObjectWithData(operation.HTTPRequestOperation.responseData, options: .AllowFragments) as? [String: AnyObject],
let nextSyncToken = dict?["nextSyncToken"] as? String{
print(nextSyncToken)//get the nextSyncToken
}

Access A Value in Wunderground API

I am trying to incorporate Wunderground into my current project. I have looked at several api tutorials, but I can't seem to figure out how to access a certain part of the API. For example, this is sort of what the API looks like:
{
"response": {
"version":"0.1",
"termsofService":"http://www.wunderground.com/weather/api/d/terms.html",
"features": {
"history": 1
}
}
,
"history": {
"date": {
"pretty": "August 9, 2015",
"year": "2015",
"mon": "08",
"mday": "09",
"hour": "12",
"min": "00",
"tzname": "America/Los_Angeles"
},
Let's say I wanted to return only the hour from the API. How would I do that?
A way to parse JSON without frameworks:
typealias JSONdic = [String: AnyObject]
NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: nsUrl), queue: NSOperationQueue.mainQueue(), completionHandler: { (_, data, _) -> Void in
if let data = data, json = data as? JSONdic, history = json["history"] as? JSONdic, hour = history["hour"] as? String {
println(hour)
}