Map array of objects in array of parent objects - ios

I am having trouble mapping an array of objects (comments) in array of parent objects (requests) from JSON via RestKit's mapping functionality.
All my data returns properly, but for some reason the comment objects are never populated!
See my code below:
request.json:
{
"data": {
"priorityRequests": [
{
"id": 123456,
"title": "Request 1",
"comments": [
{
"author": "John Smith",
"content": "This is a comment"
}, {
"author": "Jane Smith",
"content": "This is another comment"
}
]
}, {
"id": 654321,
"title": "Request 2",
"comments": [
{
"author": "John Smith",
"content": "This is a comment"
}, {
"author": "Jane Smith",
"content": "This is another comment"
}
]
}
]
}
}
Comment.h/m
#interface Comment : NSObject
#property ( strong, nonatomic ) NSString *author;
#property ( strong, nonatomic ) NSString *content;
#end
#implementation Comment
#end
Request.h/m
#import "Request.h"
#interface Request : NSObject
#property ( strong, nonatomic ) NSString *id;
#property ( strong, nonatomic ) NSString *title;
#property ( strong, nonatomic ) Comment *comments;
#end
#implementation Request
#end
RequestManager.m snippet
RKObjectMapping *requestMapping = [ RKObjectMapping mappingForClass: [ Request class ] ];
[ requestMapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"title" : #"versionNumber"
}];
RKObjectMapping *commentMapping = [ RKObjectMapping mappingForClass: [ Comment class ] ];
[ commentMapping addAttributeMappingsFromDictionary:#{
#"title": #"title",
#"author": #"author"
}];
// Failed attempt 1:
[ requestMapping addPropertyMapping: [ RKRelationshipMapping
relationshipMappingFromKeyPath: #"comments"
toKeyPath: #"comments"
withMapping: commentMapping ]
];
// end
// Failed attempt 2:
RKRelationshipMapping* requests_comments = [ RKRelationshipMapping
relationshipMappingFromKeyPath: #"comments"
toKeyPath: #"comments"
withMapping: commentMapping
];
[ requestMapping addPropertyMapping: requests_comments ];
// end
RequestCommunicator.m snippet
NSDictionary *mappingsDictionary = #{ "data.priorityRequest" : requestMapping };
RKMapperOperation *mapper = [ [ RKMapperOperation alloc ]
initWithRepresentation: parsedData // parsed json as above
mappingsDictionary: mappingsDictionary
];
NSError *mappingError = nil;
BOOL isMapped = [ mapper execute: &mappingError ];
// If no errors, returned array of mapped objects
if (isMapped && !mappingError) {
// All data except for comments here
// _comments = (Comment *) nil
[ self.delegate receivedResponseObject: [ mapper mappingResult ].array ];
... etc.

I found a fix to this issue, and although it might not be everyone's cup of tea, hopefully it can help someone else down the track.
In my Requests NSObject, I changed the mapping from type 'Comment' to 'NSArray':
- #property ( strong, nonatomic ) Comment *comments;
+ #property ( strong, nonatomic ) NSArray *comments;

Related

How to update existing object with partial data in restkit?

I have get API which returns following data
{
"id": "45832",
"name": "test name",
"parentId":"xyz",
//other fields
}
I am able to save this data using restkit. I have another api to get changes which returns only updated fields. for example
{
"id": "45832",
"name": "test name2",
//other updated fields
}
I want this data to update existing object in db. But in my case it sets parentId and other missing attributes to null. I have set identification attribute in entity mapping.
EDIT:
Here is the class for managed object with entity mapping.
#import "ZEntity.h"
#import "ZContent.h"
#import "ZThumb.h"
#implementation ZEntity
#dynamic availableFrom;
#dynamic dueDate;
#dynamic entityId;
#dynamic name;
#dynamic perCompleted;
#dynamic state;
#dynamic type;
#dynamic thumb;
#dynamic contents;
#dynamic completedOn;
#dynamic totalScore;
#dynamic parent;
#dynamic settings;
+ (RKEntityMapping *)map{
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
mapping.persistentStore = [RKManagedObjectStore defaultStore].persistentStoreCoordinator.persistentStores.firstObject;
NSAssert(mapping.persistentStore != nil, #"Requires Persistent Store");
mapping.identificationAttributes = #[#"entityId"];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"entityId",
#"type" : #"type",
#"name" : #"name",
#"dueDate" : #"dueDate",
#"state" : #"state",
#"availableFrom" :#"availableFrom",
#"percCompletion" : #"perCompleted",
#"completedOn" : #"completedOn",
#"totalScore" : #"totalScore"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"thumb" toKeyPath:#"thumb" withMapping:[ZThumb map]]];
return mapping;
}
#end
Response descriptor
RKResponseDescriptor* entitiesD = [RKResponseDescriptor responseDescriptorWithMapping:[ZEntity map]
method:RKRequestMethodAny pathPattern:nil keyPath:#"objects"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Json response
{
"total": 9,
"start": 1,
"count": 10,
"objects": [{
"id": "636488166759829858",
"type": "UPDATE",
"name": "copy1",
"canReattempt": false,
"score": 0,
"totalScore": 300,
"thumb": {
"id": "THUMB_QUPDATE4",
"expiry": 1472545125,
"url_180_120": "//s3-ap-southeast-1.amazonaws.com/mtgame-cdn.mindtickle.com/allaboard4.0/thumb/Q-Update-Thumbnail_04.png?AWSAccessKeyId=AKIAJYDLQOOEIA6NZLPQ&Expires=1472545125&Signature=To8a5SN7CoCiWDmmtXLkyANKulw%3D"
},
"dueDate": 1440845008,
"dueDateObj": {
"dueOn": 1440845008,
"dueDateExpiryAction": "NONE"
},
"state": "ACTIVE",
"percCompletion": 0.0,
"availableFrom": 1440585808,
"completedOn": 0,
"startTime": null,
"expiryTime": null,
"certificateCutOffScore": -1,
"lastPublishTime": 1440585653,
"completionCertificateEnabled": null
}]
}
In earlier calls I have set parent field for Entity class which is a relationship (I have verified this through sqlite browser too). In this json there is no parent returned. Once this call finished, parent is set to null. I have to keep existing parent value.

JSON Parsing of ios

I am trying to parse the below JSON but am not getting the data from this JSON.
In the below JSON I am trying to fetch choices but its not getting with my below code
NSString *filePathChoices = [[NSBundle mainBundle] pathForResource:#"questions" ofType:#"json"];
NSData *JSONDataChoices = [NSData dataWithContentsOfFile:filePathChoices options:NSDataReadingMappedIfSafe error:nil];
NSMutableDictionary *jsonObjectChoices = [NSJSONSerialization JSONObjectWithData:JSONDataChoices options:NSJSONReadingMutableContainers error:nil];
NSArray *arrayChoices = [jsonObjectChoices objectForKey:#"choices"];
//NSDictionary *jsonDict = [arrayChoices objectAtIndex:indexPath.row];
cell.textLabel.text = [arrayChoices objectAtIndex:indexPath.row];
From this below JSON i am fetching the choices into tableview
{
"questions": [
{
"question": "1. An ITM option has is priced at $3.00. The strike is at $20 and the underlying is trading at $19. What is the extrinsic value of the option?",
"choices": ["Ontario","New Brunswick","Manitoba","Quebec"],
"correctAnswer": 0
},
{
"question": "2. True or False. If a trader is long some calls and long some puts, he is likely to be?",
"choices": ["Ontario", "New Brunswick", "Nova Scotia", "Quebec"],
"correctAnswer": 3
},
{
"question": "3. Which of these provinces start with 'New'?",
"choices": ["Ontario", "New Brunswick", "Quebec", "Manitoba"],
"correctAnswer": 1
},
{
"question": "4. Which of these begin with the word 'Man'?",
"choices": ["Ontario", "New Brunswick", "Quebec", "Manitoba"],
"correctAnswer": 3
},
{
"question": "5. Which of these begin with the word 'Nova'?",
"choices": ["Ontario", "Nova Scotia", "British Columbia", "New Brunswick"],
"correctAnswer": 1
},
]
}
Choice is not on the top level.
So if you data structure is exactly as you've described it, you first need to get a question, and then get choice on this question.
Example:
NSArray *questions = [jsonObjectChoices objectForKey:#"questions"];
Now, get a question (here we take the first one)
NSDictionnary *question=[questions objectAtIndex:0]
And then, if you want to get the choices for this question
NSArray *choices=[question objectForKey:#"choices"];
you can't directly dispaly the array of values in cell. Replace the code with below code to solve it
NSString *filePathchoice = [[NSBundle mainBundle] pathForResource:#"questions" ofType:#"json"];
NSData *JSONData = [NSData dataWithContentsOfFile:filePathchoice options:NSDataReadingMappedIfSafe error:nil];
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableContainers error:nil];
NSArray *array = [jsonObject objectForKey:#"questions"];
questions = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (NSDictionary *dict in array) {
question = [[Question alloc] initWithObject:dict];
[questions addObject:question];
}
cell.textLabel.text = [choices objectAtIndex:indexPath.row];
cell.textLabel.font=[UIFont fontWithName:#"Bold" size:12];
Your json is not vaild modify your json file as below and try
{
"questions": [
{
"question": "1. An ITM option has is priced at $3.00. The strike is at $20 and the underlying is trading at $19. What is the extrinsic value of the option?",
"choices": [
"Ontario",
"New Brunswick",
"Manitoba",
"Quebec"
],
"correctAnswer": 0
},
{
"question": "2. True or False. If a trader is long some calls and long some puts, he is likely to be?",
"choices": [
"Ontario",
"New Brunswick",
"Nova Scotia",
"Quebec"
],
"correctAnswer": 3
},
{
"question": "3. Which of these provinces start with 'New'?",
"choices": [
"Ontario",
"New Brunswick",
"Quebec",
"Manitoba"
],
"correctAnswer": 1
},
{
"question": "4. Which of these begin with the word 'Man'?",
"choices": [
"Ontario",
"New Brunswick",
"Quebec",
"Manitoba"
],
"correctAnswer": 3
},
{
"question": "5. Which of these begin with the word 'Nova'?",
"choices": [
"Ontario",
"Nova Scotia",
"British Columbia",
"New Brunswick"
],
"correctAnswer": 1
}
] }
The go to library I use in my projects is JsonModel.
Example one line request to get the json.
[JSONHTTPClient postJSONFromURLWithString:#"http://example.com/api"
params:#{#"postParam1":#"value1"}
completion:^(id json, JSONModelError *err) {
//check err, process json ...
}];
For this json :
{
"order_id": 104,
"total_price": 103.45,
"products" : [
{
"id": "123",
"name": "Product #1",
"price": 12.95
},
{
"id": "137",
"name": "Product #2",
"price": 82.95
}
]
}
You only need to subclass JSONModel and add this to your .h file :
#protocol ProductModel
#end
#interface ProductModel : JSONModel
#property (assign, nonatomic) int id;
#property (strong, nonatomic) NSString* name;
#property (assign, nonatomic) float price;
#end
#implementation ProductModel
#end
#interface OrderModel : JSONModel
#property (assign, nonatomic) int order_id;
#property (assign, nonatomic) float total_price;
#property (strong, nonatomic) NSArray<ProductModel, ConvertOnDemand>* products;
#end
#implementation OrderModel
#end
Advantages JSONModel :
One line fetches for json / or you can choose another fetch method.
Easily convert JSONModel objects to / from NSDictionary, text.
Optional parameters, direct conversion to the data types you specify
and custom error handling and many more.
Installation :
Clone source, copy paste the JSONModel folder into your project.
Subclass JSONModel, add the matching properties from json to your
header file (be careful, if your json is potentially malformatted,
you should use the parameter that instructs the framework
that in your json, these could be null or missing).
Fetch and parse your json like in your above example, and then test
all of the properties for inconsistencies.
In your example, you have a "," in plus, just before "]"
The corrected code :
{
"questions": [
{
"question": "1. An ITM option has is priced at $3.00. The strike is at $20 and the underlying is trading at $19. What is the extrinsic value of the option?",
"choices": [
"Ontario",
"New Brunswick",
"Manitoba",
"Quebec"
],
"correctAnswer": 0
},
{
"question": "2. True or False. If a trader is long some calls and long some puts, he is likely to be?",
"choices": [
"Ontario",
"New Brunswick",
"Nova Scotia",
"Quebec"
],
"correctAnswer": 3
},
{
"question": "3. Which of these provinces start with 'New'?",
"choices": [
"Ontario",
"New Brunswick",
"Quebec",
"Manitoba"
],
"correctAnswer": 1
},
{
"question": "4. Which of these begin with the word 'Man'?",
"choices": [
"Ontario",
"New Brunswick",
"Quebec",
"Manitoba"
],
"correctAnswer": 3
},
{
"question": "5. Which of these begin with the word 'Nova'?",
"choices": [
"Ontario",
"Nova Scotia",
"British Columbia",
"New Brunswick"
],
"correctAnswer": 1
}
]
}
The model that you can use to parse this :
#interface QuestionsFetch : JSONModel
#property (strong, nonatomic) NSArray<QuestionModel>* questions;
#protocol QuestionModel #end
#interface QuestionModel : JSONModel
#property (strong, nonatomic) NSString* question;
#property (strong, nonatomic) NSArray<ChoicesModel>* choices;
#protocol ChoicesModel #end
#interface ChoicesModel : JSONModel
#property (strong, nonatomic) NSArray* choice;
If you have any problems with importing / parsing, leave a comment.

How to pass an array in an url query parameter using nsurl in objective-c?

The API need to pass an array in an url query parameter, how to acheive this in iOS?
I only know how to pass a single vaue, like the API below : ?title=Design Milk&id=feed/http://feeds.feedburner.com/design-milk
API sample:
"title": "Design Milk",
"id": "feed/http://feeds.feedburner.com/design-milk",
"categories": [
{
"id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/design",
"label": "design"
},
{
"id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/weekly",
"label": "weekly"
},
{
"id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/global.must",
"label": "must reads"
}
]
Create a collection and then use NSJSONSerialization to create JSON data representation. Use the resulting data as the POST data.
NSDictionary *parameters = #{
#"title": #"Design Milk",
#"id": #"feed/http://feeds.feedburner.com/design-milk",
#"categories": #[
#{
#"id": #"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/design",
#"label": #"design"
},
#{
#"id": #"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/weekly",
#"label": #"weekly"
},
#{
#"id": #"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/global.must",
#"label": #"must reads"
}
]
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictData options:0 error:&error];

RestKit Dynamic Mapping with heterogeneouse collections and Core Data

I have the following JSON structure which I want to map with RestKit in CoreData:
[
{
"id": 1,
"type": "folder",
"name": "Folder 1",
"children": [
{
"id": 1,
"type": "document",
"name": "Document 1"
},
{
"id": 2,
"type": "folder",
"name": "Folder 2",
"children": [
{
"id" : 2,
"type": "document",
"name": "Document 2"
}
]
}
]
}
]
It's a typical filesystem structure where a folder can contain other folders or documents.
I have two CoreData entities: Folder and Document.
The Folder entity has the following relationships:
Here is my Mapping Code:
RKEntityMapping* foldersMapping = [RKEntityMapping mappingForEntityForName:#"Folder" inManagedObjectStore:managedObjectStore];
foldersMapping.identificationAttributes = #[ #"objectId" ];
[foldersMapping addAttributeMappingsFromDictionary:#{#"id" : #"objectId",
#"name" : #"name"}];
RKEntityMapping* documentsMapping = [RKEntityMapping mappingForEntityForName:#"Document" inManagedObjectStore:managedObjectStore];
documentsMapping.identificationAttributes = #[ #"objectId" ];
[documentsMapping addAttributeMappingsFromDictionary:#{#"id" : #"objectId",
#"name" : #"name"}];
In my dynamic mapping I use the type of the object to decide if it is a document or a folder.
RKDynamicMapping* dynamicMapping = [RKDynamicMapping new];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKey:#"type"] isEqualToString:#"Folder"]) {
return foldersMapping;
} else if ([[representation valueForKey:#"type"] isEqualToString:#"Document"]) {
return documentsMapping;
}
return nil;
}];
Now I added the dynamic mapping as relationship mapping to the folder mapping for the property children two times.
[foldersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"children" toKeyPath:#"children" withMapping:dynamicMapping]];
[foldersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"children" toKeyPath:#"documents" withMapping:dynamicMapping]];
And here is my Problem: All this works, but I got the following Warnings:
RestKitNesterFolderTestProject[9969:3a03] W
restkit.object_mapping:RKMappingOperation.m:554 WARNING: Failed
mapping nested object: The operation couldn’t be completed. (Cocoa
error 1550.)
I think the Problem is that I doubled the mapping for the relationship children, so RestKit tries to map each child two times, once as Folder and once as Document.
Any help is appreciated.

RestKit Core Data mapping one to many

I am using RestKit to map the following JSON to core data.
This is my model:
this is my methods:
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([User class]) inManagedObjectStore:managedObjectStore];
userMapping.identificationAttributes = #[#"id"];
[userMapping addAttributeMappingsFromDictionary:#{
#"birthday":#"birthday",
#"email":#"email",
#"facebook_token":#"facebook_token",
#"first_name":#"first_name",
#"gender":#"gender",
#"interested_in":#"interested_in",
#"last_name":#"last_name",
#"password":#"password",
#"m8_status":#"m8_status",
#"status":#"status",
#"languages": #"languages",
#"hometown":#"hometown",
#"location":#"location",
#"avatar_url":#"avatar_url",
#"mood":#"mood"
}];
RKEntityMapping *findUserMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([FindUser class]) inManagedObjectStore:managedObjectStore];
findUserMapping.identificationAttributes = #[#"id"];
[findUserMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"users" toKeyPath:#"users" withMapping:userMapping]];
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:findUserMapping
pathPattern:#"/v1/users"
keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]
]];
This is getting objects from example JSON:
{
"users": [
{
"avatar_url": null,
"birthday": "04/11/1984",
"email": "antonina.duminskaya#wildermancruickshank.co.uk", "first_name": "Antonina",
"gender": "female",
"hometown": null,
"id": "5264ee384d61632c4b2f0000",
"interested_in": [
"male",
"female" ],
"languages": [], "last_name": "Duminskaya", "location": null, "m8_status": null,
"mood": null,
"status": null
} ]
}
here:
[objectManager getObjectsAtPath:#"/v1/users" parameters:#{#"session_id":session.id} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
FindUser *findUsers = [[self.fetchedFindUsersResultsController fetchedObjects] lastObject];
NSLog(#"%#", findUsers.users);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
I have message in log:
Relationship 'users' fault on managed object (0x8e964a0) <FindUser: 0x8e964a0> (entity: FindUser; id: 0x8da0790 <x-coredata://083BB731-FDD7-4A35-A174-724F50862A02/FindUser/p1> ; data: {
id = nil;
users = "<relationship fault: 0x8deb260 'users'>";
})
And In mappingResults I have all results about json
Core Data is representing the relationship initially as fault saving memory by not loading all the object graph at once.
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreData/Articles/cdFaultingUniquing.html
Also, do not use the reserved word 'id' as the Restkit identification attribute for your managed objects. Try using findUserId and userId and update your mappings accordingly.

Resources