NSOrderedSet response from server and Core Data - ios

I want to show data as it came from the backend So let's have an example json file:
{
"fonts": [
{
"name": "Helvetica",
"styleIdentifier": "H0",
"size": 17
},
{
"name": "Helvetica",
"styleIdentifier": "H1",
"size": 14
},
{
"name": "Helvetica-Bold",
"styleIdentifier": "H0Bold",
"size": 17
},
{
"name": "HelveticaNeue-Light",
"styleIdentifier": "H0Light",
"size": 40
}
]
}
So i create a relationship (many - many) with ordered option selected. And by the input i see it's always write in the same way to Core Data, but when I try to fetch it
configuratation.fonts where fonts is a NSOrderedSet i get items in completly random order. I miss sth in spec? Or I should sort it somehow?
__EDIT__
Firstly when i get a data from above json I have a configuration set with empty font relation. Then I fetch this and insert it into core data with:
NSMutableArray *returnArray = [NSMutableArray new];
for(NSDictionary *fontDictionary in jsonArray) {
Font *fontObj = [Font font:fontDictionary inContext:context];
[returnArray addObject:fontObj];
}
And in this array data is in correct order. Then in configuration object i add it to NSOrderedSet by:
-(void)appendTracks:(NSArray<Font*>*)fontArray {
self.fonts = [NSOrderedSet orderedSetWithArray: fontArray];
}
And then i try to fetch it by simply use reference:
configuration.fonts
And in this step data are completly not in correct order.

Do not set the NSOrderedSet directly.
Either modify the existing set:
[self.fonts addObjectsFromArray:fontArray];
Or:
Xcode generates methods to add and remove entries from ordered sets within the generated NSManagedObject class.
Assuming you have an Entity called ManagedConfiguration which holds an ordered to many relation called fonts:
ManagedConfiguration *managedObjectConfigurationInstance = //create or fetch configuration in ManagedObjectContext
NSOrderedSet<ManagedFont> *fonts = //created or fetched fonts in wanted order
managedObjectConfigurationInstance.addToFonts(fonts)
replaceFonts, removeFromFontsAtIndex aso. methods are also generated.
Depending on your requirements, you might want to store the fonts in random order and apply a NSSortDescriptor to your NSFetchRequest to fetch the data in a specific order.

Instead of trying to set the data directly to your property(fonts), you need to first fetch the mutable copy of your NSOrderedSet from the NSmanagedObject Subclass (I assume it to be Font).
NSMutableOrderedSet *orderedSet = [self mutableOrderedSetValueForKey:#"fonts"];
Then add the objects from the array to this orderedSet.
[orderedSet addObjectsFromArray:array];
Now you would have properly set the the values for the key fonts.
So your appendTracks function would now look like this.
-(void)appendTracks:(NSArray<Font*>*)fontArray {
NSMutableOrderedSet *orderedSet = [self mutableOrderedSetValueForKey:#"fonts"];
[orderedSet addObjectsFromArray:fontArray];
}
Now execute your fetch request. You should receive the data in the set order in the array.
PS:I had used your JSON response to test this.

Related

Dictionary put into Array gets shredded

I try to send data to the server. Server waits from my this structure among other:
{
...
"card": [
{
"child": {...},
"parent":{...}
},
{
"child": {...},
"parent":{...}
}
],
[...],
[...]
}
So it should be Dictionary ({...}) placed into another Dictionary ({"child":..., "parent":...}), placed into an Array ("card": []), and this array is a cell, the final API JSON contains many of such cells.
I realize this structure by something like this:
NSDictionary *card = #{#"key1" : #"val1", #"key2" : #"val2", #"key3" : #"val3"};
NSDictionary *pair = #{#"parent" : card, #"child" : card};
NSArray *cards = [NSArray arrayWithObjects: pair, pair, nil];
After this I add cards Array into Dictionary with other auth data and send it to the server:
[self.userAuthData setObject: cards forKey:#"card"];
And I see in the server logs that data was shredded: http://monosnap.com/image/UbLPA0AK0eotAvG12o1ML4702xy0aj.png
But, if I use Dictionary cards instead of Array cards, everything is ok: http://monosnap.com/image/IzenpFc3Gik01UYhyRYtGFHmxBpCpC.png
What's wrong with idea to store Dictionary in an Array? Why it gets shredded?
Ok, answer is here: AFNetworking posts JSON arrays as multiple single-entry dictionaries
I just added a line in my ApiClient.m
_sharedClient.requestSerializer = [AFJSONRequestSerializer serializer];

How can I implement my logic properly to populate my UITableView

Sorry guys, this problem I am running into is pretty trivial. I just can't wrap my head around it so hope someone can help me. Your help is really appreciated. I am getting JSON data through NSURLConnectDelegate with a web API. I get something like this back:
(
{
id = 340
name = Vicent },
{
id = 339
name = Johny },
{
id = 338
name = Eric }
)
and I save it in a NSMutableArray as a global variable. Now, I have a NSSet of "ids". For example:
{
340, 339
}
In the numberOfRowsInSection, I return the set's count. I am trying to load only the ids in the NSSet from the array with the data saved from the webAPI, so I do something like this in cellForRowIndexPath:
for (NSNumber *num in [set allObjects]) {
NSString *newString = [[savedArray objectAtIndex:indexPath.row]
NSString *new = [num stringValue];
if ([new isEqual:newString]) {
}}
How can I just populate the ids I want?
The JSON makes it look like you have an array of dictionaries, which is a reasonable data structure to use as the data source for a table view.
It sounds like you're trying to filter your array to only include the items that are in your set. Is that right?
If so, you could write code that would create a new array containing the subset of your array elements who's ID is also in your set. There are at least a half-dozen ways to do that. One fairly simple approach would be to use the NSArray method indexesOfObjectsPassingTest. You'd pass that method a block of code that would check each array element to see if it's id object was in your set.
That would give you an NSIndexSet with the indexes of the items in your array who's ID are in your set. Then you could use the NSArray method objectsAtIndexes to get an array of only the objects that are also in the set. Something like this (Assuming that your array of dictionaries is called savedArray and your set is called allObjects:
//get the indexes of items in the array savedArray who's id appears in the set allObjects
NSIndexSet *indexes = [savedArray indexesOfObjectsPassingTest:
^(NSDictionary *obj,
NSUInteger idx,
BOOL *stop)
{
return [allObjects member: obj[#"id"]] != nil;
}
];
//Now build an (immutable) array of just the objects who's ID are in the set
NSArray *subArray = [savedArray objectsAtIndexes: indexes];
The array subArray created above is immutable. If you need a mutable array you would need to make a mutable copy, which is a one-line change.
Disclaimer: I still struggle a little with block syntax, so the above might not be exactly correct, but it gives you the general idea.

Mantle Transform With Nested Dictionaries

I thought this question would work for my situation, but my lack of experience with Mantle and iOS in general has dead ended my train of thought. Basically, I have a big chunk of JSON with nested dictionaries and arrays that I want to convert into Mantle objects.
"features": {
"App": {
"status": "_ACTIVE",
"unavailableReasons": [],
"modernCapabilities": [{
"capabilityType": "LOCK_AUTO_REPLY",
"providerStatuses": [{
"providerType": "MY_PROVIDER",
"status": false,
"unavailableReasons": ["NOT_SUPPORTED_BY_PRODUCT", "DEVICE_OS_NOT_SUPPORTED"]
}]
},
...
...
{
"capabilityType": "LOCK_CONTACT_WHITELIST",
"providerStatuses": [{
"providerType": "OTHER_PROVIDER",
"status": true,
"unavailableReasons": []
}]
}
]}
}
I want to be able to implement a similar solution to that linked above, namely iterating down the dictionary for the "Features" key, and applying a transform to each key/value pair. So, in this case, to the "App" key and it's dictionary value (and later, the "modernCapabilities" key and it's array, etc. etc.) I know that for the later steps I'll need separate model classes, and those exist, but I'm having trouble with the first step, the transform on the "App" key and it's value.
Here's what I have right now:
+ (NSValueTransformer *)featureTypesJSONTransformer {
NSValueTransformer *transformer = [NSValueTransformer valueTransformerForName:#"FeatureStatus"];
return [MTLValueTransformer transformerWithBlock:^NSDictionary *(NSDictionary *features) {
NSMutableDictionary *transformedValues = [[NSMutableDictionary alloc] init];
for (NSString *key in features) {
id transformedValue = [transformer transformedValue:[features objectForKey:key]];
if (transformedValue ) {
[transformedValues setObject:transformedValue forKey:key];
}
}
return transformedValues;
}];
}
As you can see from the code, I'm trying to preserve the key, and attach it to a new Dictionary with the value being another transform, FeatureStatus in this case (#{ "App" : })
The problem is that there is no [FeatureStatus transformedValue:], although I do have a JSONKeyPathsForPropetyKey since I want to map the 'status', 'unavailableReasons' and 'modernCapabilities' keys later on.
What's my next step? How can I register a ValueTransform that does what I want it to do?

How to add additional attributes to RestKit Mapping that are not in JSON

I have implemented a Restkit mapping to core data that contains an attribute of 'timestamp'. My local data model supports 'day', 'month', and 'year' integer attributes that I need to populate using the timestamp from JSON. I am not sure where and how to do this.
My Mapping:
RKEntityMapping *mediaMapping = [[VSObjectStore shared] mappingForEntityForName:#"Media"];
[mediaMapping setIdentificationAttributes:#[#"id"]];
// Remove any relationships
NSMutableArray *mediaMethods = [NSMutableArray arrayWithArray:[_VSMedia propertyNames]];
[mediaMapping addAttributeMappingsFromArray:mediaMethods];
Thanks in advance for your help!
You should change your data model (or at least your approach). Store an NSDate instance in your model - it is the most accurate and well supported choice. Then the mapping should happen automatically. If you still want your 3 properties for day, month and year, fine, but make them derived (transient) and get them when required (or of fetch / save) from the NSDate timestamp.
You should implement the RKObjectLoaderDelegate following method:
- (void)objectLoader:(RKObjectLoader*)loader willMapData:(inout id *)mappableData;
This method is called after parsing the data but before mapping. Thus you could modify the data to add day, month and year.
The code will look something like this:
- (void)objectLoader:(RKObjectLoader *)loader willMapData:(inout id *)mappableData
{
NSMutableDictionary *mediaData = *mappableData;
// parse timestamp here
[mediaData setObject:day forKey:#"day"];
}
and add the key to your mapping list:
NSMutableArray *mediaMethods = [NSMutableArray arrayWithArray:[_VSMedia propertyNames]];
[mediaMethods addObject:#"day"]
[mediaMapping addAttributeMappingsFromArray:mediaMethods];

Restkit/Core Data relationship mappings, entity to same type of entity (parent/child)

I am following the Alexander Edge tutorial on RestKit 0.2.0 but I am confused about how to apply it to my needs. Specifically, I am consuming a web service that returns objects in the following structure:
{
"ObjectIdMember": 200,
"ObjectNameMember": "Baseball Bat",
"SubObjectIdMember": 4124
},
{
"ObjectIdMember": 200,
"ObjectNameMember": "Baseball Glove",
"SubObjectIdMember": 4555
},
The idea is that an Object entity can have many sub-objects. Roughly speaking, the purpose of getting the Object is to use the DisplayName to populate section headers in a table view, and group sub-objects in sections by object.
How do I capture this sort of relationship (or define it) using RestKit + Core Data? The tutorial only suggests what you might do if there is a subobject defined in the response, but this is a different situation.
I know that I could just decorate and use a subclass of Object with a -(NSArray *)getSubObjects, but Core Data would not be aware of what I was doing in the sense that this would not be using any relationships.
I believe what you want is RKConnectionDescription, which can establish a relationship in Core Data using foreign keys.
The example in the docs gives the following json:
{ "project":
{ "id": 12345,
"name": "My Project",
"userID": 1
}
}
with the following mapping configuration:
NSEntityDescription *projectEntity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:managedObjectContext];
NSRelationshipDescription *userRelationship = [projectEntity relationshipsByName][#"user"];
RKConnectionDescription *connection = [[RKConnectionDescription alloc] initWithRelationship:userRelationship attributes:#{ #"userID": #"userID" }];

Resources