Mapping Firebase lists to objects with Mantle - ios

I'm looking for an effective way to map Firebase lists to custom Objective-C objects with Mantle. Firebase doesn't really have a concept of arrays, so every item in a list has an explicit id. A list in Firebase of type event appears something like this:
"events": {
"id0": {
"name": "Event 1",
"date": "2017-03-28T08:00:00+00:00",
"venue": "Venue 1"
},
"id1": {
"name": "Event 2",
"date": "2017-03-29T08:00:00+00:00",
"venue": "Venue 2"
},
...
"id5": {
"name": "Event 6",
"date": "2017-04-05T08:00:00+00:00",
"venue": "Venue 6"
}
}
In Objective-C terms, this translates perfectly well into an NSDictionary, but it's easier to work with an NSArray in my UITableViewController. At the moment I'm using NSDictionary for each event, and simply moving the event's key inside the Dictionary and creating an array of dictionaries as follows (feel free to comment on whether this is a good or bad idea):
{
"eventId": "id0",
"name": "Event 1",
"date": "2017-03-28T08:00:00+00:00",
"venue": "Venue 1"
},
{
"eventId": "id1",
"name": "Event 2",
"date": "2017-03-29T08:00:00+00:00",
"venue": "Venue 2"
}
...
My idea is to then create an Event object:
#interface Event: NSObject <MLTModel>
#property (nonatomic, strong) NSString *eventId;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSDate *date;
#property (nonatomic, strong) NSString *venue;
#end
So my question is this: is there a way to map this Firebase list to a custom Event object with Mantle so that it can be easily used for UITableViews in iOS? Specifically, how does one map the key (id0, id1 etc) of the Firebase event to be a property of the custom Event object, and back again? Or would it be better to move the key "manually" and then hand over to Mantle?

Related

Nested Select Query missing pointer and className -- Parse.com

When I do a query that requests just some selected fields of a pointer, the response that is received does not include the pointer type and class name.
Example :
PFQuery * query = [PFQuery queryWithClassName:#"ClassA"];
[query includeKey:#"column1"]; // This is a pointer with classname : ClassB
[query selectKeys:#[#"column1.subColumn1",#"column1.subColumn2"]];
The result is
"column1": {
"createdAt": "2015-10-29T19:46:17.167Z",
"subColumn1": "Some Value1",
"subColumn2": "Some Value2",
"objectId": "iCK9CpgKAh",
"updatedAt": "2015-11-16T14:30:11.312Z"
},
But, if I don't just select some fields, and include all subcolumns fields, it will work :
PFQuery * query = [PFQuery queryWithClassName:#"ClassA"];
[query includeKey:#"column1"]; // This will select all fields inside column1
Result :
"column1": {
"__type": "Pointer",
"className": "ClassB",
"createdAt": "2015-10-29T19:46:17.167Z",
"subColumn1": "Some Value1",
"subColumn2": "Some Value2",
"subColumn3": "Some Value3",
"objectId": "iCK9CpgKAh",
"updatedAt": "2015-11-16T14:30:11.312Z"
},
This create a problem when we use a PFSubclassing, because the pointer column is not treated as a pointer, but as a NSDictionary.
For example :
PFClassA and PFClassB are parse subclassess.
#interface PFClassB : PFObject<PFSubclassing>
#property (copy) NSString * subColumn1;
#property (copy) NSString * subColumn2;
#property (copy) NSString * subColumn2;
#end
#interface PFClassA : PFObject<PFSubclassing>
#property (strong) PFClassB * column1;
#end
When we do a query that just select 2 fields of the PFClassB like the above query,
the result of column1 will be a dictionary, and not a PFClassB.
Is there a workaround?
Thanks.

Filter an array with predicate in iOS

I have an array of JSONModel objects. The JSONModel object looks like this
#interface ProductModel : JSONModel
#property(nonatomic, strong) NSString *name;
#property(nonatomic, strong) NSString *price;
#property(nonatomic, strong) NSArray *productCategories;
#end
Inside the productCategory model I have this
#interface ProductCategoryModel : JSONModel
#property (nonatomic,assign) NSString *id;
#end
The json looks like this
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"prouctCategories":[
{ "id": "50" }
{ "id": "10" }
]
}
I have to filter the array checking the id property of a productCategory in the array productCategories.One product can have multiple categories. I need to get for example the list of products with the id : 10.
I want to get that list using filteredArrayUsingPredicate method of an NSArray but I cannot find the right predicate format to get this. Is it possible to do that using predicate.
I tried this predicate but I didn't get anything
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# == '%#"', #"id", #"50"];
NSPredicate documentation is pretty clear about building this kind of queries. You can use ANY clause to apply filter on many-to-many relationships to get the products whose any of the category's ID is 10.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY productCategories.id == '10')"];
NSArray *filteredProducts = [products filteredArrayUsingPredicate:predicate];
To simplify the problem change the json to look like this:
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"productCategories":["50","10"]
}
So you can use such predicate:
[NSPredicate predicateWithFormat:#"productCategories CONTAINS '10')"]
You can use NSPredicate as your json is like
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"prouctCategories":[
{ "id": "50" }
{ "id": "10" }
]
}
then you may check it with this:
NSArray *arrFiltered = [arrProducts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(self.prouctCategories.id == '10')"]];
Or you may also change your json format to make things more easier like
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"prouctCategories":[
{ "id": 50,10,20 }
]
}
So you may filtered id like this
NSArray *arrFiltered = [arrProducts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(self.prouctCategories.%K CONTAINS '10')", 'id']];
Hope it will help you. :)

Mapping nested nodes with RESTKit

I am implementing a simple iOS app that consumes reddit feeds and I need to show the feed comments. I am using RESTKit to do the ORM part and I am having an annoyance with mapping the comments.
Reddit comments can have replies and the replies can have replies and so on. It's a tree of comments. This is no big deal. The problem is that among the children nodes there might exist one that's different (used to sign that there are more comments to load on that particular thread).
Here's a JSON example:
"replies":{
"data":
{
"after": null,
"before": null,
"children":
[
{
"data":
{
"approved_by": null,
"author": "InstaRamen",
"author_flair_css_class": null,
"author_flair_text": null,
"banned_by": null,
"body": "that or he counted it all wrong",
"body_html":"<div class=\"md\"><p>that or he counted it all wrong</p>\n</div>",
"controversiality": 0,
"created": 1405093511,
"created_utc": 1405064711,
"distinguished": null,
"downs": 0,
"edited": false,
"gilded": 0
"id": "ciubfdk",
"likes": null,
"link_id": "t3_2ae8oa",
"name": "t1_ciubfdk",
"num_reports": null,
"parent_id": "t1_ciuao19",
"replies":
{
"data":
{
"after": null,
"before": null,
"children":
[
{
"data":
{
"approved_by": null,
"author": "Everlasting",
"author_flair_css_class": null,
"author_flair_text": null,
"banned_by": null,
"body": "3, sir.",
"body_html":"<div class=\"md\"><p>3, sir.</p>\n</div>",
"controversiality": 0,
"created": 1405093986,
"created_utc": 1405065186,
"distinguished": null,
"downs": 0,
"edited": false,
"gilded": 0,
"id": "ciubj8h",
"likes": null,
"link_id": "t3_2ae8oa",
"name": "t1_ciubj8h",
"num_reports": null,
"parent_id": "t1_ciubfdk",
"replies":
{
"data":
{
"after": null,
"before": null,
"children":
[
{
"data":
{
"children":
[
"ciublgq"
],
"count": 0,
"id":"ciublgq",
"name":"t1_ciublgq",
"parent_id": "t1_ciubj8h"
},
"kind": "more"
}
],
"modhash": ""
},
"kind": "Listing"
},...
So, you can see that the typical comment replies has a structure of
replies
|-data
|-children
|-data
|- #comment key/values
But the "Load more" item has a structure of:
replies
|-data
|-children
|-data
|-children
|-more loading info...
My DataObject is:
#interface RedditComment : NSObject
#property (nonatomic, copy) NSString *author;
#property (nonatomic, readwrite) NSInteger createdUTC;
#property (nonatomic, readwrite) NSInteger score;
#property (nonatomic, copy) NSString *subreddit;
#property (nonatomic, copy) NSString *subredditId;
#property (nonatomic, readwrite) NSInteger ups;
#property (nonatomic, copy) NSString *body;
#property (nonatomic, copy) NSArray *replies;
+ (RKObjectMapping*)restKitMapping;
#end
And my mapping implementation is:
#implementation RedditComment
+ (RKObjectMapping*)restKitMapping;
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[self class]];
[mapping addAttributeMappingsFromDictionary:#{
#"author": #"author",
#"created_utc": #"createdUTC",
#"score": #"score",
#"subreddit": #"subreddit",
#"subreddit_id": #"subredditId",
#"body": #"body",
#"ups": #"ups",
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"replies" toKeyPath:#"replies" withMapping:mapping]];
return mapping;
}
#end
This yields an error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFConstantString 0x1daa0d0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key body.'
This is because the "Load more" node doesn't have a body key (it's missing other keys as well, but this is the first one that RESTKit complains about).
I've tried to use a dynamic mapping block to change the mapping in runtime but couldn't figure out how to isolate the problematic node (i.e. the "Load More").
My question is how can I distinguish between both possible values in "replies" JSON nodes?
Can I have two mappings for the same partial key?
That is a mapping for replies > data > children > data and another to replies > data > children > data > children. If so how to do it?
Or is there anything wrong in my line of thinking?
Thanks in advance to everyone that is able to help or that simply take the time to read... ;-)
EDIT
including response descriptor
As requested on comments here's the RKResponseDescriptor:
RKResponseDescriptor *commentResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[PPRedditComment restKitMapping]
method:RKRequestMethodAny
pathPattern:nil
keyPath:#"data"
statusCodes:nil];
EDIT #2
including a "good comment" node to show what I am interested to map to my DO
"data":
{
"approved_by": null,
"author": "PerimeterBlue",
"author_flair_css_class":null,
"author_flair_text":null,
"banned_by":null,
"body": "This actually looks really cool.",
"body_html": "<div class=\"md\"><p>This actually looks really cool.</p>\n</div>",
"controversiality":0,
"created": 1405095679,
"created_utc": 1405066879,
"distinguished":null,
"downs": 0,
"edited": false,
"gilded": 0,
"id": "ciubwld",
"likes":null,
"link_id": "t3_2aelr4",
"name": "t1_ciubwld",
"num_reports": null,
"parent_id": "t3_2aelr4",
"replies": {...},
"saved": false,
"score": 48,
"score_hidden": false,
"subreddit": "gaming",
"subreddit_id": "t5_2qh03",
"ups": 48
}
Ok, apparently got the issue. Wain was right: it was a path related issue.
There were certain nodes which was supposed to contain dictionaries and sometimes contained strings. In the way the paths were defined this caused an non-existent selector (the property from DO) to be called on the NSString class. Since it didn't contain it, it went BOOM!
Mapping was corrected to this:
+ (RKObjectMapping*)restKitMapping;
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[self class]];
[mapping addAttributeMappingsFromDictionary:#{
#"author": #"author",
#"created_utc": #"createdUTC",
#"score": #"score",
#"subreddit": #"subreddit",
#"subreddit_id": #"subredditId",
#"body": #"body",
#"ups": #"ups",
}];
RKDynamicMapping *dynamicMapping = [RKDynamicMapping new];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
RKObjectMapping *result = mapping;
// When node is empty string don't map...
if([representation isKindOfClass:[NSString class]] && [representation isEqualToString:#""])
{
result = nil;
}
return result;
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"replies"
toKeyPath:#"replies"
withMapping:dynamicMapping]];
return mapping;
}
Response descriptor corrected to:
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[PPRedditFeedCollection restKitMapping]
method:RKRequestMethodAny
pathPattern:nil
keyPath:#"data"
statusCodes:nil];
Hope this helps someone! :-)

RKObjectMapping connections

I have neŃ…t response:
{
"orders": [
{
"date": "2013-11-18T13:00:39.000Z",
"state": "new",
"id": 11,
"order_item_ids": [
27,
28
]
}
],
"order_items": [
{
"count": 2,
"id": 27,
"item_id": 1,
"order_id": 11
},
{
"count": 1,
"id": 28,
"item_id": 2,
"order_id": 11
}
]
}
And next object I want to map it:
#interface Order : NSObject
#property (nonatomic, strong) NSString* state;
#property (nonatomic, strong) NSMutableArray* orderItems;
#property (nonatomic, strong) NSDate* visitDate;
#end
orderItems should be array of:
#interface OrderItem : NSObject
#property (nonatomic) NSUInteger ID;
#property (nonatomic) NSUInteger itemID;
#property (nonatomic) NSUInteger count;
#end
In case of RKEntityMapping I should use addConnectionForRelationship to map order_item_ids to orderItems array via order_items response. But what should I do to do to connect it in case of RKObjectMapping? Off course I can map both orders and orderItems with separate response descriptors and than parse it, but I want to make RestKit do it for me. Another idea is to use CoreData and RKEntityMapping but I'm not sure that I want in this case, it will be overkill in this case.
You can't have it done automatically with RKObjectMapping because you can't index into both arrays in any way. The entity mapping only works because you can use foreign key mapping after both mappings have been performed. You can duplicate this by using multiple response descriptors and mappings and then combining the contents of the mapping result.

Parsing JSON in iOS, inner dictionaries

I am new to JSON and iOS, I have some json data that I want to load into an array,
This is the json data
{
"items": [
{
"name": "abc",
"description": "cheeseburger",
},
{
"name": "def",
"description": "ostrichburger",
},
{
"name": "zxc",
"description": "sh1tjustgotreal",
},
{
"name": "scfs",
"description": "mylifeforaiur",
}
]
}
Now I keep on getting dictionaries with dictionaries? Why is that?
On another note, If I can modify the structure of this json cos I really just want to access the inner nodes ( abc, def ) what would I change in it to make it simpler for me and others to use it? Can I get rid of the "items" node?
When the data on the sender's end is structured as a collection of named fields, you get back a dictionary for each group of named fields. In this particular case, it looks like the outermost object has one field, which is a collection (i.e. a list or an array) of objects that each have a name and description. In other words, the sender sends you something like this:
#interface Item
#property (readwrite, copy) NSString* name;
#property (readwrite, copy) NSString* description;
#end
#interface MyObject
// This array contains objects of type Item
#property (readwrite) NSArray *items;
#end

Resources