Restkit: Post Json Array and Map response to Managed Objects - objective-c

Here is my setup, Post a list of objects in Json to server and got back with updated results that will be mapped to core data and updated.
-- Post Boday --
{
"memberId": "1000000",
"contacts": [
{
"phoneNumber": "+12233333333",
"firstName": "john",
"lastName": "H"
},
{
"phoneNumber": "+12244444444",
"firstName": "mary",
"lastName": "K"
}
]
}
-- Post Response --
{
"contacts": [
{
"phoneNumber": "+12233333333",
"firstName": "john",
"lastName": "k",
"isMember": "yes"
},
{
"phoneNumber": "+12244444444",
"firstName": "mary",
"lastName": "k",
"isMember": "no"
}
]
}
I found another thread which discusses very similar to my case. RestKit: How does one post an array of objects?
This is my setup.
-- SHContact.h --
#interface SHContact : NSManagedObject {}
#property (nonatomic, strong) NSString* phoneNumber;
#property (nonatomic, strong) NSString* firstName;
#property (nonatomic, strong) NSString* lastName;
#property (nonatomic, strong) NSNumber* isMember;
#end
-- SHContactPost.m --
#import "SHContactPost.h"
#implementation SHContactPost
#synthesize contacts;
#synthesize memberId;
- (NSArray*)contacts {
return <list-of-SHContact>;
}
- (NSString *)memberId {
return #"my-member-id";
}
#end
-- RK Mapping --
// Setup our object mappings for SHContact
RKManagedObjectMapping* contactMapping =
[RKManagedObjectMapping mappingForClass:[SHContact class]
inManagedObjectStore:objectManager.objectStore];
contactMapping.primaryKeyAttribute = #"phoneNumber";
[contactMapping mapAttributes:#"phoneNumber",
#"firstName", #"lastName", #"isMember", nil];
[objectManager.mappingProvider setObjectMapping:contactMapping
forKeyPath:#"contacts"];
[[objectManager mappingProvider]
setSerializationMapping:[contactMapping inverseMapping]
forClass:[SHContact class]];
RKObjectMapping *cPostMapping = [RKObjectMapping
mappingForClass:[SHContactPost class]];
[cPostMapping mapKeyPath:#"contacts"
toRelationship:#"contacts"
withMapping:contactMapping];
[cPostMapping mapAttributes:#"memberId", nil];
[objectManager.mappingProvider
setSerializationMapping:[cPostMapping inverseMapping]
forClass:[SHContactPost class]];
[objectManager.router routeClass:[SHContactPost class]
toResourcePath:#"/contacts"
forMethod:RKRequestMethodPOST];
objectManager.serializationMIMEType = RKMIMETypeJSON;
-- Post to server --
SHContactPost *post = [[SHContactPost alloc] init];
RKObjectManager* manager = [RKObjectManager sharedManager];
[manager postObject:post delegate:self];
Server post and response is SUCCESSFUL. However, Reskit was not able to map the result back to the list of SHContact objects. This is the exception. Am I missing something?
restkit.network:RKObjectLoader.m:222 Encountered errors during mapping:
Cannot map a collection of objects onto a non-mutable collection.
Unexpected destination object type 'SHContactPost'
UPDATE: This is what I changed to make it work for me.
-- RK Mapping --
// Setup our object mappings for SHContact
RKManagedObjectMapping* contactMapping =
[RKManagedObjectMapping mappingForClass:[SHContact class]
inManagedObjectStore:objectManager.objectStore];
contactMapping.primaryKeyAttribute = #"phoneNumber";
[contactMapping mapAttributes:#"phoneNumber",
#"firstName", #"lastName", #"isMember", nil];
[objectManager.mappingProvider addObjectMapping:contactMapping];
-- Post to server --
SHContactPost *post = [[SHContactPost alloc] init];
RKObjectManager* manager = [RKObjectManager sharedManager];
RKObjectMapping* responseMapping = [manager.mappingProvider
objectMappingForClass:[SHContact class]];
[manager postObject:post usingBlock:^(RKObjectLoader *loader) {
loader.delegate = self;
loader.targetObject = nil;
responseMapping.rootKeyPath = #"contacts";
loader.objectMapping = responseMapping;
}];
loader.targetObject needs to set it to nil, otherwise, it won't work.

Related

Post complex json using RestKit in Objective-C

I'm having an issue posting json by using RestKit, but I can't find what I'm doing wrong.
The request json sample is:
{
"Name": "sample string 1",
"Interests": {
"Array": [
1,
2,
3
]
}
}
My code:
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[requestMapping addAttributeMappingsFromDictionary:#{#"Name": #"Name"}];
RKObjectMapping *interestsMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKObjectMapping *interestIDMapping = [RKObjectMapping mappingForClass:[KLInterest class]];
[interestIDMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"InterestID"]];
[interestsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"Array" toKeyPath:#"Array" withMapping:interestIDMapping]];
[requestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"Interests" toKeyPath:#"Interests" withMapping:interestsMapping]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[NSMutableDictionary class] rootKeyPath:nil method:RKRequestMethodPOST];
[self.objectManager addRequestDescriptor:requestDescriptor];
NSDictionary *data = [business toDictionary];
[self sendRequest:#"api/Business/Create" Method:#"POST" Data:data withCallback:^(BOOL success, NSDictionary response, NSError error) {
callback(success, response, error);
}];
KLInterestID.h
#interface KLInterestID : NSObject
#property (nonatomic) NSNumber *InterestID;
#end
Please let me know what I'm doing wrong. I'm not familiar with the relationshipMappingFromKeyPath and attributeMappingFromKeyPath:nil functions.
I'm not sure how to send int array.
Thanks

Mapping NSDictionary from JSON as a result of fetch HTTP request

I have NSDictionary object as result of NSJSONSerialization, and it has child that is array of dictionary called list. Look at my json result:
{
"tags": [
"rofl",
"lmao",
"funny",
"haha",
"lmfao"
],
"result_type": "exact",
"list": [
{
"defid": 3689813,
"word": "Lol",
"author": "Lol B",
"permalink": "http://lol.urbanup.com/3689813",
"definition": "The name 'Lol' is an abreviated form of the name '[Laurence]'.",
"example": "\"Hey Lol, you alright?\"\r\n\r\n\"..Well i was chattin to Lol and he said..\"",
"thumbs_up": 44617,
"thumbs_down": 9926,
"current_vote": ""
},
------- bla bla bla ----------
],
"sounds": [
"http://media.urbandictionary.com/sound/lol-871.mp3",
------- bla bla bla ----------
]
}
I also created a class model for item (child of list).
#import <Foundation/Foundation.h>
#interface Item : NSObject
#property (strong, nonatomic) NSString *defid;
#property (strong, nonatomic) NSString *word;
#property (strong, nonatomic) NSString *author;
#property (strong, nonatomic) NSURL *permalink;
#property (strong, nonatomic) NSString *definition;
#property (strong, nonatomic) NSString *example;
-(instancetype)initWithDictionary:(NSDictionary *)dict;
#end
I also created its initializer
-(instancetype)initWithDictionary:(NSDictionary *)dict {
if (self = [super init]) {
self.defid = [dict valueForKey:#"defid"];
self.word = [dict valueForKey:#"word"];
self.author = [dict valueForKey:#"author"];
self.permalink = [dict valueForKey:#"permalink"];
self.definition = [dict valueForKey:#"definition"];
self.example = [dict valueForKey:#"example"];
}
return self;
}
I already create an array of list item (NSDictionary), my problem is I want to map those into my class so I can create an instance of list item. So how to convert item in array into my item class?
Should I iterate each item of array and call that initialization?
or any elegant method for doing that?
Additional question
I am relatively new into iOS development. I just want to parse my request result. Is there any tips for fetch some request and assign its result for datasource a table view (array)?
This is my fetch method:
- (void)fetchDataFromMashape:(NSURL *)URL {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:#"GET"];
[request setValue:API_KEY_MASHAPE forHTTPHeaderField:API_MASHAPE_HEADER_1];
[request setValue:API_ACCEPT forHTTPHeaderField:API_MASHAPE_HEADER_2];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *jsonResult = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"Result: %#", jsonResult);
_results = [[NSArray alloc] initWithArray:[jsonResult valueForKey:#"list"]];
}];
[task resume];
}
My advice is to create a static method with the json list as a parameter
+ (NSArray<Item*>*) parseJson:(NSArray*) json {
NSMutableArray<Item*>* results = [[NSMutableArray alloc] initWithCapacity:son.length];
for (NSDictionary* anItem in json){
[results append:[[Item alloc] initWithDictionary:anItem]];
}
return results;
}

RestKit: mapping nsnumber

Currently NSNumber get mapped to string, e.g.:
#interface Blah : NSObject
#property NSNumber *num;
#end
will get mapped to
{
num: "5"
}
instead of
{
num: 5
}
Does anyone know how to correctly map NSNumber to int, and NOT string?
Update:
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[requestMapping addAttributeMappingsFromArray:#[#"num"]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[MyClass class] rootKeyPath:nil method:RKRequestMethodPOST];
[_manager addRequestDescriptor:requestDescriptor];

RESTKIT .20.1 nested array with relationship mapping assistance

I am having trouble with RESTKIT and mapping the following JSON via RESTKit .20.1. I have a nested JSON array and I am trying to map via the RelationshipMapper. I am able to map the 5 chart_data object successfully but the related nested chart_datatum objects do not map to the ChartDataDetails class. All of the properties are blank.
I would like the following object structure.
Alert Instance
Array of ChartData objects
Each ChartData object should contain 1 ChartDataDetails object <---- this object is blank
Please see the link at the bottom for the screenshot of the XCode Debugger for the current state.
Can someone please provide some assistance?
Thanks,
G
{
"alert_instance": {
"alert_template_id": 1,
"alert_time_period": "2013-05-29",
"chart_data": [
{
"chart_datum": {
"alert_instance_id": 1,
"chart_data_id": 1,
"chart_data_name": "provider_id",
"datatype": 1,
"chart_data_value": "1"
}
},
{
"chart_datum": {
"alert_instance_id": 1,
"chart_data_id": 1,
"chart_data_name": "Provider_name",
"datatype": 2,
"chart_data_value": "Stubbs, Drew"
}
},
{
"chart_datum": {
"alert_instance_id": 1,
"chart_data_id": 1,
"chart_data_name": "Posting_date",
"datatype": 3,
"chart_data_value": "05/12/2013"
}
},
{
"chart_datum": {
"alert_instance_id": 1,
"chart_data_id": 1,
"chart_data_name": "Charges",
"datatype": 5,
"chart_data_value": "229"
}
},
{
"chart_datum": {
"alert_instance_id": 1,
"chart_data_id": 1,
"chart_data_name": "Payments",
"datatype": 5,
"chart_data_value": "-1023.11"
}
}
]
}
Model classes
AlertInstance.h
#interface AlertInstance : NSObject
#property (nonatomic, copy) NSNumber* alertTemplateId;
#property (nonatomic, copy) NSDate* alertTimePeriod;
#property (nonatomic, retain) NSMutableArray* chartData;
#end
ChartData.h
#interface ChartData : NSObject
#property (nonatomic, retain) ChartDataDetails *chartDataDetails;
#end
ChartDataDetails.h
#interface ChartDataDetails : NSObject
#property (nonatomic, copy) NSNumber* chartDataId;
#property (nonatomic, copy) NSString* chartDataName;
#property (nonatomic, copy) NSNumber* chartDataType;
#property (nonatomic, copy) NSNumber* chartDataValue;
#end
code for mappings..
RKObjectMapping* chartDataDetailMapping = [RKObjectMapping mappingForClass:[ChartDataDetails class]];
[chartDataDetailMapping addAttributeMappingsFromDictionary:#{
#"chart_data_id": #"chartDataId",
#"chart_data_name": #"chartDataName",
#"datatype": #"chartDataType",
#"chart_data_value": #"chartDataValue",
}];
RKObjectMapping* chartDataMapping = [RKObjectMapping mappingForClass:[ChartData class]];
[chartDataMapping addAttributeMappingsFromDictionary:#{
#"chart_data":#"chartData"}];
RKObjectMapping* alertInstanceMapping = [RKObjectMapping mappingForClass:[AlertInstance class]];
[alertInstanceMapping addAttributeMappingsFromDictionary:#{
#"alert_template_id": #"alertTemplateId",
#"alert_time_period": #"alertTimePeriod",
}];
[alertInstanceMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"chart_data"
toKeyPath:#"chartData"
withMapping:chartDataMapping]];
[alertInstanceMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"chart_datum"
toKeyPath:#"chartDataDetails"
withMapping:chartDataDetailMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:alertInstanceMapping
pathPattern:nil
keyPath:#"alert_instance"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
NSURL *URL = [NSURL URLWithString:#"http://ideainnovationsmobiledev1.cloudapp.net:3000/alert_instances/1.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
RKObjectRequestOperation *objectRequestOperation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[ responseDescriptor ]];
[objectRequestOperation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
link to screen shot of the debugger
https://docs.google.com/file/d/0Bwaj6O1rXiKaeFIwT2s4Rm1Gb00/edit?usp=sharing
EDIT:
Ok I figured it out.
I needed to chain the ChartDetailsMapping to the ChartData as follows.
[chartDataMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"chart_datum"
toKeyPath:#"chartDataDetails"
withMapping:chartDataDetailMapping]];
[alertInstanceMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"chart_data"
toKeyPath:#"chartData"
withMapping:chartDataMapping]];

RestKit - Not key value coding-compliant

I have a managed object MyEntity that I use without any problem throughout my application.
#interface MyEntity : NSManagedObject
#property (nonatomic, retain) NSString *code;
#property (nonatomic, retain) NSNumber *def_value;
#property (nonatomic, retain) NSString *label;
#property (nonatomic, retain) NSString *link_label;
#property (nonatomic, retain) NSNumber *order;
#end
I have a web service http://my_server/MyEntity which returns me:
[
{
CODE: "TYPE",
LABEL: "Administrative",
ORDER: "3",
DEF_VALUE: "0",
LINK_LABEL: ""
},
{
CODE: "TOPIC",
LABEL: "NDF",
ORDER: "1",
DEF_VALUE: "0",
LINK_LABEL: "Administrative"
},
{
CODE: "TOPIC",
LABEL: "Report",
ORDER: "2",
DEF_VALUE: "1",
LINK_LABEL: "Administrative"
},
{
CODE: "TOPIC",
LABEL: "Other",
ORDER: "3",
DEF_VALUE: "0",
LINK_LABEL: "Administrative"
}, ...
]
I'm trying to map and fetch this list
NSURL *baseUrl = [NSURL URLWithString: #"http://my_server"];
NSString *objName = #"MyEntity";
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[RKManagedObjectStore setDefaultStore:managedObjectStore];
[managedObjectStore createPersistentStoreCoordinator];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL: baseUrl];
RKEntityMapping *objMap = [RKEntityMapping mappingForEntityForName:objName inManagedObjectStore:managedObjectStore];
[objMap addAttributeMappingsFromDictionary: #{
#"CODE": #"code",
#"LABEL": #"label",
#"ORDER": #"order",
#"DEF_VALUE": #"def_value",
#"LINK_LABEL": #"link_label",
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:objMap pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
[objectManager getObjectsAtPath:objName parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"ok");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"ko");
}];
It's giving me this error
I restkit.network:RKHTTPRequestOperation.m:185 GET'http://my_server/MyEntity'(200 OK)
CoreData: error: Failed to call designated initializer on NSManagedObject class 'MyEntity'
Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
'[ valueForUndefinedKey:]: the entity (null) is not key value coding-compliant for the key "link_label".'
I saw this error on 2 or 3 SO questions but each with a different problem and seem not any of mine.
Can someone help me to debug that ?
You need to fully configure the objectManager:
objectManager.managedObjectStore = managedObjectStore;
You also aren't completing the Core Data stack initialisation:
[managedObjectStore createManagedObjectContexts];