I had this previous problem which I fixed, but I know the fix is def not done the right way. Can some one point me in the right direction for the right fix?
Basically I create a object with iOS sim via RESTkit postObject, and I got this message:
Processing PeopleController#create (for 127.0.0.1 at 2012-01-13 03:55:46) [POST]
Parameters: {"name"=>"data"}
Person Create (0.4ms) INSERT INTO "people" ("created_at", "updated_at", "name")
VALUES('2012-01-13 11:55:46', '2012-01-13 11:55:46', NULL)
Completed in 27ms (View: 1, DB: 0) | 200 OK
A nice gentlemen pointed out that my class inside my create function is only accepting a :person class which I overlooked.
def create
#person = Person.new(params[:person]) , thus looking for {"person" => {"name"=>"data"}}
I fixed this by doing
#person =Person.new(name:=>params[:name]) since i am only sending {"name"=>"data"}
now it creates ok, and I can see the entry on my ios sim. But I know this is not the right way. The right way should be {"person" => {"name"=>"data"}} sent in to the original function. Plus I get an error could not find an object mapping for keyPath:". Any thoughts?
Here is my xcode:
#interface Data: NSObject{// imaginary over arching class
Person * person;
NSArray *dog;
#property (nonatomic, retain) Person * person;
#property (nonatomic, retain) NSArray * dog;
#interface Data : NSObject {
Person *person;
NSArray *dogs;
}
#property (nonatomic ,retain) Person *person;
#property (nonatomic ,retain) NSArray *dogs;
#end
#interface Person : NSObject {
NSString *name;
NSNumber *personId;
NSDate *updatedAt;
NSDate *createdAt;
}
#property (nonatomic , retain) NSDate * createdAt;
#property (nonatomic , retain) NSDate * updatedAt;
#property (nonatomic , retain) NSNumber *personId;
#property (nonatomic , retain) NSString *name;
#end
RKObjectMapping* userMapping = [RKObjectMapping mappingForClass:[Person class]];
[userMapping mapKeyPath:#"created_at" toAttribute:#"createdAt"];
[userMapping mapKeyPath:#"updated_at" toAttribute:#"updatedAt"];
[userMapping mapKeyPath:#"name" toAttribute:#"name"];
[userMapping mapKeyPath:#"id" toAttribute:#"personId"];
RKObjectMapping* dogMapping = [RKObjectMapping mappingForClass:[Dog class]];
[dogMapping mapKeyPath:#"created_at" toAttribute:#"createdAt"];
[dogMapping mapKeyPath:#"person_id" toAttribute:#"spersonId"];
[dogMapping mapKeyPath:#"name" toAttribute:#"name"];
[dogMapping mapKeyPath:#"updated_at" toAttribute:#"updatedAt"];
[dogMapping mapKeyPath:#"id" toAttribute:#"dogId"];
RKObjectMapping *dataMapping = [RKObjectMapping mappingForClass:[Data class]];
[dataMapping mapKeyPath:#"dog" toAttribute:#"dogs"];
[dataMapping mapKeyPath:#"person" toRelationship:#"person" withMapping:userMapping];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:dataMapping];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/people"
objectMapping:dataMapping delegate:self];
RKObjectRouter * router = [RKObjectManager sharedManager].router;
[router routeClass: [Person class] toResourcePath:#"/people/:personId"];
[router routeClass: [Person class] toResourcePath:#"/people"
forMethod:RKRequestMethodPOST];
RKObjectMapping *personSerializationMapping = [RKObjectMapping mappingForClass:
[NSMutableDictionary class]];
[personSerializationMapping attribute:#"name", nil];
[RKObjectManager sharedManager].mappingProvider
setSerializationMapping:personalSerializationMapping forClass: [Person class]];
Person *dave = [[Person alloc]init];
dave.name = #"Dave";
[[RKObjectManager sharedManager] postObject:dave delegate:self];
}
Take Person as an example:
RKObjectMapping* userMapping = [RKObjectMapping mappingForClass:[Person class]];
[userMapping mapKeyPath:#"created_at" toAttribute:#"createdAt"];
[userMapping mapKeyPath:#"updated_at" toAttribute:#"updatedAt"];
[userMapping mapKeyPath:#"name" toAttribute:#"name"];
[userMapping mapKeyPath:#"id" toAttribute:#"personId"];
To config RestKit to POST with a root path, replace:
[[[RKObjectManager sharedManager] mappingProvider]
setSerializationMapping:personalSerializationMapping
forClass:[Person class]];
with:
[[[RKObjectManager sharedManager] mappingProvider]
registerMapping:objectMapping
withRootKeyPath:#"person"];
Now Rails will receive {"person" => {"name"=>"data"}} instead of {"name"=>"data"}.
For reference, here is the magic of registerMapping:
- (void)registerObjectMapping:(RKObjectMapping *)objectMapping withRootKeyPath:(NSString *)keyPath {
// TODO: Should generate logs
objectMapping.rootKeyPath = keyPath;
[self setMapping:objectMapping forKeyPath:keyPath];
RKObjectMapping* inverseMapping = [objectMapping inverseMapping];
inverseMapping.rootKeyPath = keyPath;
[self setSerializationMapping:inverseMapping forClass:objectMapping.objectClass];
}
it does both setMapping and setSerializationMapping with the clever [objectMapping inverseMapping] trick.
Yea finally figured it out. The mapping was wrong. Instead of addObjectMapping: dataMapping, it should have been userMapping with keyPath person which is userMapping. btw thnx #favo for editing my chicken scratch =] now it can GET and POST fine!
For completionist sake, the error about "could not find mapping for key path ''" seems to be a thing with Rails create action.
There's a good answer for this problem in this SO question: how to post an object to Rails using RESTKit
Related
I have been trying to map to xml for weather. The xml looks like this
<current_observation>
<observation_epoch>1433740800</observation_epoch>
<weather>Clear</weather>
<temp_c>24</temp_c>
<relative_humidity>61%</relative_humidity>
<wind_dir>North</wind_dir>
<wind_mph>0</wind_mph>
<visibility_km>N/A</visibility_km>
</current_observation>
My weather.h
#interface Weather : NSObject
#property (nonatomic , copy) NSString* weather;
#property (nonatomic , copy) NSString* temp_c;
#property (nonatomic , copy) NSString* relative_humidity;
#property (nonatomic , copy) NSString* wind_dir;
#property (nonatomic , copy) NSString* wind_mph;
#property (nonatomic , copy) NSString* visibility_km;
#property (nonatomic , copy) NSString* observation_epoch;
#end
My mapping function
- (RKObjectManager*) makeWeatherXMLMappingwithURL:(NSString*)mLinkURL{
//Map the Weather class
RKObjectMapping* weatherMapping = [RKObjectMapping mappingForClass:[Weather class]];
[weatherMapping addAttributeMappingsFromDictionary:#{
#"weather.text":#"weather",
#"temp_c.text":#"temp_c",
#"relative_humidity.text":#"relative_humidity",
#"wind_dir.text":#"wind_dir",
#"wind_mph.text":#"wind_mph",
#"visibility_km.text":#"visibility_km",
#"observation_epoch.text":#"observation_epoch"
}];
//register mappings with the provider using a response descriptor
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping: weatherMapping
method: RKRequestMethodAny
pathPattern: nil
keyPath: #"current_observation"
//keyPath:#"rss.channel.item"
statusCodes: RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
//Initialize RestKit for xml (rss/feed) parsing & mapping
NSURL *baseURL = [NSURL URLWithString: mLinkURL];
//make a new instance of RKObjectManager (Parser which inherits from RKObjectManager)
RKObjectManager *objectManager = [Parser managerWithBaseURL:baseURL];
[objectManager setRequestSerializationMIMEType:RKMIMETypeTextXML];
// [objectManager setAcceptHeaderWithMIMEType:#"application/rss+xml"];
[objectManager setAcceptHeaderWithMIMEType:#"text/xml"];
// [RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:#"application/rss+xml"];
[RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:#"text/xml"];
//add the responseDescriptor to RKObjectManager
[objectManager addResponseDescriptor:responseDescriptor];
return objectManager;
}
My call
- (void) parseWeatherXMLwithURL:(NSString*)mLinkURL{
//Make the XML Mapping
RKObjectManager* objectManager = [self makeWeatherXMLMappingwithURL:mLinkURL];
//asychronous mapping (Calling getObjectsAtPath doesn't block the thread until it has completed)
[objectManager getObjectsAtPath:#""
parameters:nil
//Asynchronous Success block
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSArray *weather = mappingResult.array;
if (weather!=nil && [weather count]>0){
//Delegate response to processAsynchronousLiveStreamingRSSComplete Handler
[self.delegate performSelector:#selector(processAsynchronousXMLWeatherComplete:) withObject:(Item*)[weather objectAtIndex:0]];
}
else{
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:#"Data is not available!" forKey:NSLocalizedDescriptionKey];
NSError *error = [NSError errorWithDomain:#"Data is not available!" code:200 userInfo:details];
//Delegate response to processAsynchronousLiveStreamingRSSFailed Handler
[self.delegate performSelector:#selector(processAsynchronousXMLWeatherFailed:) withObject:error];
}
}
//Asynchronous Failed block
failure:^(RKObjectRequestOperation *operation, NSError *error) {
//Delegate response to processAsynchronousLiveStreamingRSSFailed Handler
[self.delegate performSelector:#selector(processAsynchronousXMLWeatherFailed:) withObject:error];
}];
}
After my call i get 1key/value pair which is #"current_observation":#"0 objects". I think that this means it maps to the root element - current-observation - but it cannot map to the rest of the elements.
Finally i got the solution. I had to rename my class "weather" to "WeatherCO" -can be renamed to anything-, clean and rebuild my project and everything was fixed!
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];
I am trying to bind my core data to a NSTableView. I am getting information from an API then wanting to add it to NSTableView. It looks like it is setup correctly because each time I have it call the API and get information back, a blank line is added to the NSTableView data.
Why is it adding a blank line instead of the data I have it binded too?
AppController.h
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
I then am using the new Xcode where it auto synth's.
Items.h
#class TimeLog;
#interface Items : NSManagedObject
#property (nonatomic, retain) NSNumber * itemId;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSString * itemType;
#property (nonatomic, retain) TimeLog *relationship;
#end
Items.m
#implementation Items
#dynamic itemId;
#dynamic title;
#dynamic itemType;
#dynamic relationship;
#end
ItemObject.h
#interface ItemObject : NSObject
#property (nonatomic, retain) NSString * itemId;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSString * itemType;
#end
ItemObject.m
#implementation ItemObject
#end
Method making API Call
This method makes the API call and adds it to a temp object. It then adds that temp object to core data.
+ (void)searchForItemByType:(NSString *)itemType andId:(NSString *)searchId
{
NSLog(#"Search Feature By ID: %#", searchId);
RKObjectMapping *itemMapping = [RKObjectMapping mappingForClass:[ItemObject class]];
[itemMapping addAttributeMappingsFromDictionary:#{
#"id": #"itemId",
#"name": #"title",
#"item_type": #"itemType"
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:itemMapping pathPattern:nil keyPath:#"data" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
// The entire value at the source key path containing the errors maps to the message
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"errorMessage"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError);
// Any response in the 4xx status code range with an "errors" key path uses this mapping
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping pathPattern:nil keyPath:#"error_description" statusCodes:statusCodes];
RKObjectManager *manager = [RKObjectManager sharedManager];
NSLog(#"HTTP Client: %#", manager.HTTPClient);
[manager addResponseDescriptorsFromArray:#[ responseDescriptor, errorDescriptor ]];
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"false", #"with_lock"
, nil];
NSString *path = [NSString stringWithFormat:#"/api/v1/%#/%#", [itemType lowercaseString], searchId];
NSLog(#"Manager: %#", manager);
[manager getObjectsAtPath:path parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSLog(#"Results: %#", [result firstObject]);
Items *insertItem = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:[[CoreDataHelper sharedInstance] managedObjectContext]];
insertItem = [result firstObject];
NSLog(#"Name: %#", [insertItem title]);
// Handled with articleDescriptor
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Transport error or server error handled by errorDescriptor
NSLog(#"Error: %#", [error localizedDescription]);
NSAlert *alert = [NSAlert alertWithMessageText:#"Error" defaultButton:#"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:#"%#", [error localizedDescription]];
[alert runModal];
}];
}
Log from above code
2013-03-15 10:15:21.817 Project[59074:403] Results: <ItemObject: 0x1034ab360>
2013-03-15 10:15:21.818 Project[59074:403] ManagedObjectContext
2013-03-15 10:15:21.818 Project[59074:403] Name: Custom Mod is missing from Face Lift
IB
I think the problem may be in this code:
NSLog(#"Results: %#", [result firstObject]);
Items *insertItem = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:[[CoreDataHelper sharedInstance] managedObjectContext]];
insertItem = [result firstObject];
NSLog(#"Name: %#", [insertItem title]);
In your log it looks like [result firstObject] is part of the 'ItemObject' class not the 'Items' class. Even though they share the same structure, 'ItemObject' does not inherit from NSManagedObject, but is being assigned to one. The system doesn't know how to translate an 'ItemObject' object into an 'Items' object so it simply keeps all the values in insertItem blank, which translates into a blank line showing up in your table. Try this instead:
Items *insertItem = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:[[CoreDataHelper sharedInstance] managedObjectContext]];
ItemObject *tempObject = [result firstObject];
insertItem.itemID = tempObject.itemID;
insertItem.title = tempObject.title;
insertItem.itemType = tempObject.itemType;
This has probably been asked a million times, but I'm not getting anywhere from all the examples that I'm finding.
I have the following class:
#interface LoginRequest : NSObject
{
NSString* _deviceId;
}
#property (nonatomic, retain) NSString* deviceId;
#end
And I'm trying to post this to a server, encoded as form key/value in the HTTP body.
I'm using the following to setup the mapping:
objectManager.serializationMIMEType = RKMIMETypeFormURLEncoded;
RKObjectMapping* pmsg = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[pmsg mapKeyPath: #"deviceId" toAttribute:#"DeviceId"];
RKObjectMapping* pmsgSerializeMapping = [pmsg inverseMapping];
[objectManager.mappingProvider setSerializationMapping:pmsgSerializeMapping forClass:[LoginRequest class]];
[objectManager.router routeClass:[LoginRequest class] toResourcePath:#"/login" forMethod:RKRequestMethodPOST];
This was taken from the example here: https://github.com/RestKit/RestKit/wiki/Posting-Data-Objects
I'm getting the following error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<LoginRequest 0x8c9c980> valueForUndefinedKey:]: this class is not key value coding-compliant for the key DeviceId.'
Here's how I'm trying to send my object:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
LoginRequest* request = [LoginRequest alloc];
request.deviceId = #"Test";
[objectManager postObject:request delegate: self];
Can anyone help?
Update: I can confirm that when I don't use the custom key-code mapping stuff, it works. The following I have working:
RKObjectMapping* pmsg = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[pmsg mapAttributes:#"DeviceId", nil];
[objectManager.mappingProvider setSerializationMapping:pmsg forClass:[LoginRequest class]];
[objectManager.router routeClass:[LoginRequest class] toResourcePath:#"/login" forMethod:RKRequestMethodPOST];
Well, your property is named "deviceId", which is lowercase. But you're saying the attribute is "DeviceId" which starts with an uppercase "D".
I'm using RestKit to convert a nested core data model into a JSON file and upload it to a web service. Then I'm trying to get request the same JSON back and re-inflate it into a core data object. I'm getting this error during inflation:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'AppUser'
2012-04-26 10:25:50.850 DropboxSync[3824:5843] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppUser 0x2b7820> valueForUndefinedKey:]: the entity (null) is not key value coding-compliant for the key "lastName".'
The class has a lastName property:
#property (nonatomic, retain) NSString * firstName;
#property (nonatomic, retain) NSString * lastName;
#property (nonatomic, retain) NSString * localDataFilepath;
#property (nonatomic, retain) NSSet *events;
#property (nonatomic, retain) AppUserWrapper *wrapper;
**What could be causing this?**
I got 3 entities defined in my Core Data model:
User
Event
Images
user has multiple events, events have multiple images
I've defined a RestKit object mapping as defined below.
-(void)setupObjectMapping
{
RKObjectManager *objectManager = [RKObjectManager sharedManager ] ;
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[Article class]];
[mapping mapAttributes:#"articleID", #"title", #"body", nil];
//********************************
RKObjectMapping *imageMapping = [RKObjectMapping mappingForClass:[ImageEntity class]];
[imageMapping mapAttributes:#"createDate", #"localFilePath", nil];
[objectManager.mappingProvider addObjectMapping:bleedImageMapping];
[objectManager.mappingProvider setSerializationMapping:[imageMapping inverseMapping] forClass:[ImageEntity class]];
[objectManager.mappingProvider imageMapping forKeyPath:#"images"];
//********************************
RKObjectMapping *eventMapping = [RKObjectMapping mappingForClass:[Event class]];
[eventMapping mapAttributes:#"createDate", #"severity", nil];
[eventMapping mapRelationship:#"images" withMapping:imageMapping];
[objectManager.mappingProvider addObjectMapping:eventMapping];
[objectManager.mappingProvider setSerializationMapping:[eventMapping inverseMapping] forClass:[Event class]];
[objectManager.mappingProvider setMapping:eventMapping forKeyPath:#"bleedEvents"];
//********************************
//setup App user mapping
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[AppUser class]];
[userMapping mapAttributes:#"firstName", #"lastName", nil];
[userMapping mapRelationship:#"events" withMapping:eventMapping];
[objectManager.mappingProvider addObjectMapping:userMapping];
[objectManager.mappingProvider setSerializationMapping:[userMapping inverseMapping] forClass:[AppUser class]];
[objectManager.mappingProvider setMapping:userMapping forKeyPath:#"appUser"];
//********************************
//setup App user wrapper mapping
RKObjectMapping *userWrapperMapping = [RKObjectMapping mappingForClass:[AppUserWrapper class]];
[userWrapperMapping mapRelationship:#"appUser" withMapping:userMapping];
[objectManager.mappingProvider addObjectMapping:userWrapperMapping];
[objectManager.mappingProvider setSerializationMapping:[userWrapperMapping inverseMapping] forClass:[AppUserWrapper class]];
[objectManager.mappingProvider setMapping:userWrapperMapping forKeyPath:#"appUserWrapper"];
}
Here's how I convert the nested data structure to JSON:
-(void)convertTestUserToJSON
{
NSString* fullPath = [[$ documentPath] stringByAppendingPathComponent:#"sampleJSONFolder"];
[[NSFileManager defaultManager] createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:nil error:nil];
// Make the file
NSString* fullFilepath = [NSString stringWithFormat:#"%#_%#.json",appUser.firstName,appUser.lastName];
NSString* dataFile = [fullPath stringByAppendingPathComponent:fullFilepath];
appUser.localDataFilepath = dataFile;
[[AppUser managedObjectContext] save:nil];
NSError* error = nil;
RKObjectMapping *serMap = [[[RKObjectManager sharedManager] mappingProvider] serializationMappingForClass:[AppUser class]];
NSDictionary *d = [[RKObjectSerializer serializerWithObject:appUser mapping:serMap] serializedObject:&error];
if(error!=nil)
{
NSLog(#"!!!!! Error: %#",[error localizedDescription]);
}
//this is where the JSON is generated
NSString* dataContents = [d JSONString];
BOOL success = [dataContents writeToFile:dataFile atomically:YES encoding:NSUTF8StringEncoding error:nil];
if(!success)
{
NSLog(#"Error writing to data file!");
}
}
Here's the result of the JSON conversion
{"firstName":"First Name0","events":[{"severity":0,"images":[{"createDate":"2005-04-21 08:28:47 +0000","localFilePath":"localPhoto#10.png"},{"createDate":"2009-12-19 07:26:54 +0000","localFilePath":"localPhoto#11.png"}],"createDate":"2003-05-25 15:32:53 +0000"},{"severity":1,"images":[{"createDate":"2008-08-02 19:40:14 +0000","localFilePath":"localPhoto#10.png"},{"createDate":"2007-01-12 05:50:27 +0000","localFilePath":"localPhoto#12.png"},{"createDate":"2007-05-28 12:19:39 +0000","localFilePath":"localPhoto#11.png"}],"createDate":"2001-05-28 23:38:23 +0000"},{"severity":2,"images":[{"createDate":"2002-10-09 19:47:39 +0000","localFilePath":"localPhoto#10.png"},{"createDate":"2008-08-17 03:21:12 +0000","localFilePath":"localPhoto#11.png"}],"createDate":"2005-10-18 03:40:52 +0000"}],"lastName":"Last Name83"}
When I'm trying to inflate this JSON back into a nested core data entity, I get an error:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'AppUser'
2012-04-26 10:25:50.850 DropboxSync[3824:5843] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppUser 0x2b7820> valueForUndefinedKey:]: the entity (null) is not key value coding-compliant for the key "lastName".'
It appears that the inflater does not instantiate all relationships before attempting mapping. Am I wrong? What could be causing this to happen? I've tried to re-inflate my JSON in two different ways, and both times I got this error. How can it be resolved?
Thank you for any input!
The problem is using RKObjectMapping instead of RKEntityMapping when dealing with Core Data, so RestKit's instantiating an NSManagedObject as regular NSObject.