What method of RKObjectLoaderDelegate gets called after the method - (RKObjectLoader *)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate is called?
I'm trying to run some more code once my POST is successful.
EDIT (more info):
Neither of the methods that #Paul mentions are being called, but my server is interpreting the POST somewhat correctly because the DB is being written to.
During app initialization, I am doing the following:
// Grab the reference to the router from the manager
RKObjectRouter *router = [RKObjectManager sharedManager].router;
// Define a default resource path for all unspecified HTTP verbs
[router routeClass:[EventMessage class] toResourcePath:#"/events_messages/:idPrimaryKey"];
[router routeClass:[EventMessage class] toResourcePath:#"/event_messages" forMethod:RKRequestMethodPOST];
And when I am ready to POST, I am doing this:
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[EventMessage class]];
[objectMapping mapKeyPath:#"user_id" toAttribute:#"userId"];
[objectMapping mapKeyPath:#"event_id" toAttribute:#"eventId"];
[objectMapping mapKeyPath:#"message" toAttribute:#"message"];
[[RKObjectManager sharedManager].mappingProvider registerMapping:objectMapping withRootKeyPath:#"event_message"];
[[RKObjectManager sharedManager] postObject:eventMessage delegate:self];
And this is the error that I get in the console:
Error Domain=JKErrorDomain Code=-1 "Unexpected token, wanted '{', '}', '[', ']', ',', ':', 'true', 'false', 'null', '"STRING"', 'NUMBER'." UserInfo=0x7b9f510 {JKAtIndexKey=0, JKLineNumberKey=1, NSLocalizedDescription=Unexpected token, wanted '{', '}', '[', ']', ',', ':', 'true', 'false', 'null', '"STRING"', 'NUMBER'.}
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects;
or
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error;
Once you have setup your mappings correctly the posting of objects is very easy. Like Paul Cezanne pointed out, the corresponding callbacks will be used.
For posting objects, however, you'll also need a serialisation mapping that tells the framework which parameters should be converted to POST parameters:
Mapping Example:
// Serialisation Mapping
RKObjectMapping *messageSendMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[messageSendMapping mapAttributes:#"name", #"number", #"text", nil];
[[RKObjectManager sharedManager].mappingProvider
setSerializationMapping:messageSendMapping
forClass:[Message class]];
Update: For trouble shooting purposes you might want to increase the log level for RestKit like so:
RKLogConfigureByName("RestKit", RKLogLevelTrace);
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKLogConfigureByName("RestKit/CoreData", RKLogLevelTrace);
Make sure that RKObjectManager has been initialized:
NSString *url = #"http://example.com";
RKObjectManager* mgr = [RKObjectManager managerWithBaseURLString:url];
I wasted a bunch of time before noticing that sharedManager is not really a singleton, you have to initialize it.
After that you can use the sharedManager and get an reference to the first one created:
[[RKObjectManager sharedManager].mappingProvider setObjectMapping:mapping forResourcePathPattern:#"/RKKeyValueMappingExample"];
Related
I'm attempting to use RestKit to handle posting an updated User object to my remote web service.
Currently my GET requests seem to be working fine but I'm having issues using
[[RKObjectManager sharedManager] postObject:updatedUser path:#"path" parameters:nil success:nil failure:nil];
Invoking this method is throwing a EXC_BAD_ACCESS exception.
My mappings are set up as follows, I believe I have both the RKRequestDescriptor and RKResponseDescriptor's.
User Response Mapping:
RKEntityMapping * userMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([User class])
inManagedObjectStore:[manager managedObjectStore]];
….Setup mapping (I excluded a relationship mapping on this object)
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:userMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"currentUser"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]]
Request mapping:
[manager addRequestDescriptorsFromArray:#[
[RKRequestDescriptor requestDescriptorWithMapping:[userMapping inverseMapping]
objectClass:[User class]
rootKeyPath:nil
method:RKRequestMethodPOST]]];
The mappings seem to set up fine, the EXC_BAD_ACCESS exception is thrown when I call postObject
The test method looks like this, _updatedUser is a CoreData object fetched using [RKObjectManager sharedManager] getObjectsAtPath:…
-(void) doPost{
//this user is a CoreData object fetched using
[_updatedUser setBio:#"This is an update!"];
RKObjectManager * objectManager = [RKObjectManager sharedManager];
[objectManager postObject:_updatedUser
path:#"update/user"
parameters:nil
success:…
failure:…];
}
I've attempted using NSZombies to find the cause of this but I have't had much luck.
From what I can tell the start of the issue seems to be coming from RKObjectParameterization's -[RKObjectParameterization mappingOperation:didSetValue:forKeyPath:usingMapping:] where it looks like everything passed into the method is nil or an empty string.
Thanks!
Much thanks to Wain, after spending way too much time on this the error became instantly apparent after I turned on logging:
RKLogConfigureByName("RestKit", RKLogLevelWarning);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
It turns out I had a circular reference between mapped objects.
I have a one to one relationship where a User contains a UserProfile
I incorrectly set up a bidirectional relationship mapping between User and UserProfile
[userProfileMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"user"
toKeyPath:#"user"
withMapping:userMapping]];
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"userProfile"
toKeyPath:#"userProfile"
withMapping:userProfileMapping]];
It looks like my endless loop was caused by userProfileMapping
Thanks Wain, logging lesson learned.
I'm trying to post some data (an authentication token for a website, to be specific) with RESTKit and I'm having trouble. Each time I run the method, I get this:
'NSInvalidArgumentException', reason: '`RKRequestDescriptor` objects must be initialized
with a mapping whose target class is `NSMutableDictionary`, got 'Login' (see
`[RKObjectMapping requestMapping]`)'
Also, there are many 'Incompatible pointer types' warnings in the code.
Here is my method which is really ugly and bad, and I would like some help fixing up. I do a [self postToken] inside an IBAction method when a button is pressed. I am getting variable userAuthToken from another file, where it is set using with the json response from a POST request not using RESTKit. I will eventually convert that POST to RESTKit when I know how to successfully. The method "userAuthTokenMethod" is in the same file as the "postToken" method, and it allows me to use the userAuthToken object, which is initialized in AppDelegate so it acts as a global variable.
- (void)postToken
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[Login class]];
[mapping addAttributeMappingsFromDictionary:#{#"token": #"token"}];
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *tokenResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:nil pathPattern:nil keyPath:nil statusCodes:statusCodeSet];
RKRequestDescriptor *tokenRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:mapping objectClass:self rootKeyPath:nil method:nil];
[[RKObjectManager sharedManager] addRequestDescriptor:tokenRequestDescriptor];
RKObjectManager *tokenManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"https://websitetest.com/doctors/find"]];
[tokenManager addResponseDescriptor:tokenResponseDescriptor];
[tokenManager addRequestDescriptor:tokenRequestDescriptor];
[tokenManager postObject:[[self userAuthTokenMethod] userAuthToken] path:nil parameters:nil success:nil failure:nil];
NSURL *tokenURL = [NSString stringWithFormat:#"https://websitetest.com/doctors/find?name=%#&location=%#",nameIDTextField.text, locationTextField.text];
NSMutableURLRequest *tokenRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:tokenURL]];
RKObjectManager *tokenObjectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:tokenURL]];
[tokenObjectManager.HTTPClient setDefaultHeader:#"Auth-Token" value:[[self network360Edge] userAuthToken]];
RKObjectRequestOperation *tokenOperation = [[RKObjectRequestOperation alloc] initWithRequest:tokenRequest responseDescriptors:#[tokenResponseDescriptor]];
NSLog(#"Token being POSTed ==> %#", [[self userAuthTokenMethod] userAuthToken]);
[tokenOperation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result){
NSLog(#"Response for POST request with token ==> %#", [result array]);
}failure:nil];
[tokenOperation start];
}
Here is the code for my mapping provider file called MappingProvider:
//MappingProvider.h
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#interface MappingProvider : NSObject
+(RKMapping *)tokenMapping;
#end
//MappingProvider.m
#import "MappingProvider.h"
#import <RestKit/RestKit.h>
#import "Login.h"
#implementation MappingProvider
+(RKMapping *)tokenMapping
{
RKObjectMapping *tokenMapping = [RKObjectMapping mappingForClass:[Login class]];
[tokenMapping addAttributeMappingsFromArray:#[#"token"]];
return tokenMapping;
}
#end
Here is the code for the Login file where 'token' is initialized:
//Login.h
#import <Foundation/Foundation.h>
#interface Login : NSObject
#property (nonatomic, copy)NSString *token;
#end
I'm very new to RESTKit and somewhat new with Objective-C (I've been coding in it for about a month). I would appreciate all help in fixing up my code.
This question is old, but the issue is that requests should use mapping without class specification, because requests use descriptors that specify class (see 2 lines below), like this:
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
Probably you also have issue in line:
RKRequestDescriptor *tokenRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:mapping objectClass:self rootKeyPath:nil method:nil];
more concretely in objectClass:self, it should be objectClass:[self class] or the needed class that contains token property, like this:
RKRequestDescriptor *tokenRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[self class] rootKeyPath:nil method:nil];
Hope this helps
Um beginner with RestKit, first example for me was on foursquare API's and I've used RestKit with Blocks not delegates.
I want to retrive the name's of venues,this is the JSON response
and this is my code:
// App Delegate.m
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURLString:#"https://api.Foursquare.com/v2"];
RKManagedObjectStore *objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:#"Venue.sqlite"];
objectManager.objectStore = objectStore;
objectManager.serializationMIMEType = RKMIMETypeJSON;
RKManagedObjectMapping *venueMapping = [RKManagedObjectMapping mappingForClass:[Venue class] inManagedObjectStore:objectStore];
[venueMapping mapKeyPath:#"id" toAttribute:#"id"];
[venueMapping mapKeyPath:#"name" toAttribute:#"name"];
venueMapping.primaryKeyAttribute = #"id";
[objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"response.venue"];
then in myViewController.m
-(void)loadVenues{
// When caling loadObjectsAtResourcePath method it specify RKObjectLoader which is the actual request.
// within these block you can take more options to controll the request.
[[RKObjectManager sharedManager]loadObjectsAtResourcePath:#"/venues/40a55d80f964a52020f31ee3?oauth_token=FNQPN5P5EKLJ5IQ44TMWO00I3W033M0Y1TKINW2OTF2VIOTP&v=20130512" usingBlock:^(RKObjectLoader* loader)
{
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Venue class]];
loader.onDidLoadObject = ^(NSArray *objects)
{
NSLog(#"onDidLoadObject Blocks");
self.data = objects;
[self.tableView reloadData];
};
}
];
}
and the app is entering the block of onDidLoadObject but every time the array is empty !!
even when I test the link on browser it comes with data.
When I debug the loader.URL it always come with these
https://api.Foursquare.com/v2/venues/40a55d80f964a52020f31ee3?v=20130512&oauth_token=FNQPN5P5EKLJ5IQ44TMWO00I3W033M0Y1TKINW2OTF2VIOTP -- https://api.Foursquare.com/v2 -- https://api.Foursquare.com/v2
I don't know why load.URL is wrong ?!
I think um calling the 4square API's with the wrong way, anyone can help ? :)
-Put Your mapping as a class method to be accessed from all application classes, and done only once.
-change the Attribute "id" as it is reserved in Objective-C.
-add this to the block
[loader setObjectMapping:[RestKitMapping yourMapping]];
-then add this with your mapping code
[objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"response.venue"];
-And use Delegates instead of blocks
I've two concerns regarding the above code:
1- Apparently the above JSON response, lists just one Venue .. So KeyPath should be "response.venue" not "response.venues"
2- Where's the mapping for ID? .. which is the primary key that RestKit uses to insert into DB? You need to set the primary key mapping.
I just started learning RestKit. I am trying to use it to get nearby venues using Foursquare apis. but every time I try " objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects " I get this warning "WARNING: Failed mapping nested object: (null)".
I am really confused. Following is my mapping code which is in init method:
_objectManager = [RKObjectManager objectManagerWithBaseURL:#"https://api.foursquare.com"];
RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[Location class]];
[locationMapping mapKeyPath:#"address" toAttribute:#"address"];
[locationMapping mapKeyPath:#"city" toAttribute:#"city"];
[locationMapping mapKeyPath:#"state" toAttribute:#"state"];
[_objectManager.mappingProvider setMapping:locationMapping forKeyPath:#"location"];
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[Groups class]];
[venueMapping mapRelationship:#"location" withMapping:locationMapping];
[_objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"groups"];
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[Response class]];
[responseMapping mapRelationship:#"groups" withMapping:venueMapping];
[_objectManager.mappingProvider setMapping:responseMapping forKeyPath:#"response"];
return self;
Following is the code in "locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation "
NSString *resourceURLString = [NSString stringWithFormat:#"/v2/venues/search?ll=%f,%f&limit=10&client_id=LEA1MYN1Z1QWSBFIOA1T4FREVVNF0R1ADQKMA0AMGL3CN5N4&client_secret=M2W204MKEAO4KV5L4ZXUYWK2GC32PO0KT5PWYJQBP4FKBTMO",newLocation.coordinate.latitude, newLocation.coordinate.longitude];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:resourceURLString delegate:self];
[_locationManager stopUpdatingLocation];
and finally this code is in "objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects"
_objects = [objects retain];
NSLog(#"object that i receive is %#",objects);
Following is the response i get in console:
2012-02-26 13:15:39.872 restKitPractice[8609:690b] W restkit.object_mapping:RKObjectMappingOperation.m:372 WARNING: Failed mapping nested object: (null)
2012-02-26 13:15:39.873 restKitPractice[8609:207] object that i receive is (
"Response: 0x6b5cb20"
)
You can check the Venues json from Foursquare using this link:
https://api.foursquare.com/v2/venues/search?ll=40.562362,-111.938689&limit=10&client_id=LEA1MYN1Z1QWSBFIOA1T4FREVVNF0R1ADQKMA0AMGL3CN5N4&client_secret=M2W204MKEAO4KV5L4ZXUYWK2GC32PO0KT5PWYJQBP4FKBTMO
Any help or suggestions would be appreciated as I am really struggling with this issue.
Thanks
Vik
You should take a look at the object mapping docs.
Your mapping definitions do not match the json you're trying to parse.
I realize this is an old question, but I've seen the same warning. The cause is that you have JSON like this {"response":{}} that you want to map to an object. Since there are no keys in the object, it essentially maps to nil. So, the warning is pretty harmless. You will not get a Response object back because the server essentially returned something that RestKit can't map because there are no keys in the JSON that match any of your attribute mappings. RestKit is letting you know that with the warning and falling back to not creating an object.
I'm just starting to use Restkit and am trying to send a regular POST request "serverUrl/account/authenticate.xml?OPTIONS" to get a response as XML. This is the code I use to call the URL:
-(void) function{
// OBJECT MANAGER
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:serverUrl];
[manager.router routeClass:[CBUser class] toResourcePath:#"/account/authenticate" forMethod:RKRequestMethodPOST];
// OBJECT MAPPING
RKObjectMapping* mapping = [RKObjectMapping mappingForClass:[CBUser class]];
mapping = [manager.mappingProvider objectMappingForKeyPath:#"user"];
[mapping mapKeyPath:#"id" toAttribute:#"userId"];
[manager loadObjectsAtResourcePath:str objectMapping:mapping delegate:self];
}
// DELEGATE
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
CBUser* user = [objects objectAtIndex:0];
NSLog(#"Loaded Contact ID #%# -> Firstname: %#, Lastname: %#", user.userId, user.firstname, user.lastname);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
NSLog(#"Encountered an error: %#", error);
}
Looking at the server log it seems it receives a GET request instead of a POST even though I'm using the "RKRequestMethodPOST" option.
Here is my error form my logs:
Started GET "/account/authenticate.xml?commit=Login&authenticity_token=iPhone&user%5Bpassword=XXXXX&user%5Bemail=XXXXXXX%5D&user%5Bdevice_token=XXXXXXX" for 192.168.106.30 at 2012-01-25 19:20:03 -0800
AbstractController::ActionNotFound (The action 'show' could not be found for AccountController):
Rendered /Users/Guillaume/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.0.9/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb within rescues/layout (1.2ms)
What am I missing?
Hopefully this code snippet of my app will help. You setup your mappings, say in your delegate. Note the "forMethod"
RKObjectRouter *router;
RKObjectMapping* createAccountSerializationMapping = [RKObjectMapping mappingForClass:[Cr eateAccount class]];
[createAccountSerializationMapping mapAttributes:#"email", #"pwd", #"uname", nil];
[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:createAccountSerializationMapping forClass:[CreateAccount class]];
router = [RKObjectRouter new] ;
[router routeClass:[CreateAccount class] toResourcePath:#"/registration/rest/users/create_account" forMethod:RKRequestMethodPOST];
[RKObjectManager sharedManager].router = router;
and then later, when you want to post an object
[[RKObjectManager sharedManager] postObject:user delegate:self];