I was using this method to POST my objects, but it's been deprecated:
- (void)postObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate
What should I now use instead? How do I configure the RKObjectMapping of my response?
The selector has been deprecated in favor of - (void)postObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block - thus you use the block to configure a RKObjectLoader instance. Example follows how to set an objectMapping (mapping used to map the response) in the block:
[[RKObjectManager sharedManager] postObject:object
block:^(RKObjectLoader* loader) {
loader.objectMapping = objectMapping;
loader.delegate = delegate;
}];
you could also use
[[RKObjectManager sharedManager] postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate];
This is not deprecated
Related
I am using Restkit for a class that Extends NSManagedObject.
I am aware that rest kit itself has functionality to save to core data from network fetch. However, I cannot use that functionality due to the following reasons:
My application will be fetching data from sockets as well as from rest kit , so I would want a centralised location for saving/deleting logic.
My server does not confirm to rest protocols, so many times I have to send a POST request even when I really want to delete something in server.
So What I wanted to do was have my Model classes extend nsmanaged object, and save it when I want to. But I get this error:
CoreData: error: Failed to call designated initializer on
NSManagedObject class
Is there a way to go around this ?
I am fetching from server like this :
#implementation API_Login
+(void)performLoginWithEmail:(NSString*)email
withPassword:(NSString*)password
success:(void (^)(Token* user) )success
failure:failureblock failure{
RKObjectManager * objectManager = [APIHelper getRestObjectManager];
RKObjectMapping *tokenMapping = [RKObjectMapping mappingForClass:[Token class]];
//add mapping for token
[tokenMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"token"]];
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:tokenMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
// add mapping for error
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[Error class]];
[errorMapping addAttributeMappingsFromDictionary:#{#"message":#"message",#"badRequest":#"badRequest"}];
RKResponseDescriptor *errorResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:errorMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
[objectManager addResponseDescriptor:errorResponseDescriptor];
NSDictionary *queryParams = #{#"email" : email,
#"password" : password,
};
[objectManager postObject:nil
path:#"/users/api/login"
parameters:queryParams
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
Token* token = (Token*)[mappingResult firstObject] ;
// [AppDelegateHandle setToken:token];
success(token);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSArray* e = [[error userInfo] objectForKey:RKObjectMapperErrorObjectsKey];
Error *err = (Error*)[e objectAtIndex:0];
NSLog(#"%#",[err.badRequest allValues] );
failure(operation,error);
}];
}
#end
My Token class looks like:
#interface Token : NSManagedObject
#property NSString* token;
#end
and my api response looks like :
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6XC9cL3JlbWluZGVyLmRldlwvdXNlcnNcL2FwaVwvbG9naW4iLCJpYXQiOjE0Njg1OTM3NjYsImV4cCI6MTQ2OTE5Mzc2NiwibmJmIjoxNDY4NTkzNzY2LCJqdGkiOiIxMDc3ZjBhY2ViYTFjOWZjZWNhYjkyMzYyOTA0ZmI4NSJ9.I6FHJLCCHr3EHQa8HgaDqxQMjF1HVyA5AymPjvBGDrM"
}
When I change Token to extend NSObject instead of NSManagedObject , everything works fine. What could the problem be ?
This happens because you're using RKObjectMapping instead of RKEntityMapping which is required if you're using a subclass of NSManagedObject.
You can't use a subclass of NSManagedObject if you aren't going to add it directly into a context.
If your request simply has a token then I wouldn't bother with RestKit probably, but in the general case I'd map to NSDictionary with the keys being the same as your managed object classes and then when you want to create your managed objects you can do so and 'import' the data to them with setValuesForKeysWithDictionary:.
I can't figure out how to correct the errors I'm getting after having upgraded from RestKit 0.10 to 0.20... Can anyone help?
Thanks!
ViewController.m
- (void)viewDidLoad {
// Wain, I added this RKResponseDescriptor
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping:[Feed mapping] method:RKRequestMethodAny
pathPattern:nil keyPath:#"springs" statusCodes:nil];
// These 4 lines has errors and needs to be fixed
// No visible #interface for 'RKObjectManager' declares the selector 'loader'
[[RKObjectManager sharedManager]
loadObjectsAtResourcePath:#"/springs/?apikey=xxx"
usingBlock:^(RKObjectLoader *loader) {
loader.onDidLoadObjects = ^(NSArray *objects){
springs = objects;
// These 2 lines have errors that need to be fixed
// Use of undeclared identifier 'loader'
[loader.mappingProvider setMapping:[Spring mapping] forKeyPath:#"springs"];
loader.onDidLoadResponse = ^(RKResponse *response){
}
I see the GitHub help page for this, but I can't figure this out with just that on my own. Really appreciate it!
Update
Ok I think I understand how to replace the first line, just by using
[RKObjectManager.sharedManager getObjectsAtPath:#"/springs/?apikey=xxx" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)...
But I'm still not sure what to do with the loader.onDidLoadObjects = ^(NSArray *objects) part!
The 0.1x code you show downloads the response data and then calls your callback to ask what to do with it, so you supply the mapping in the callback.
0.2x doesn't work like that. It takes all of the mappings as configuration before you make the request, and the mappings are associated with response descriptors (so RestKit can search all the config options and apply everything that matches).
So, you need to take your configuration and apply it to the RKObjectManager before you actually 'get' any content from the server.
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'm currently using the RKErrorMessage class to map an error message from my server like so:
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:#"message" toKeyPath:#"errorMessage"]];
RKResponseDescriptor *errorResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping
pathPattern:nil
keyPath:#"error"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
But now I would like to map an additional parameter from my server, for instance the error title (or anything else for that matter). My JSON looks like this:
{
"error":{
"title":"This is the error title"
"message":"This is the error message",
}
}
The idea is that I can directly map some of these error messages to UIAlertViews if something ever goes wrong when my app is accessing the server. This can be done already with only the error message but adding a title seems to make it fit well with UIAlertView.
From looking at the RKErrorMessage class I see that it only has two parameters: errorMessage and userInfo. How would I go about doing this?
UPDATE: The reason I'm trying to do this with RKErrorMessage is that it automatically maps to the NSError passed into the failure block in the RestKit get/post/put/etc methods... which is pretty nice.
[objectManager getObjectsAtPath:#"/api/somethingsomething/" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
Here's a way to do what you originally asked (not sure why you accepted the answer that didn't actually answer your question):
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"userInfo"]];
RKResponseDescriptor *errorResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping
pathPattern:nil
keyPath:#"error"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
The key difference from your sample code is this line:
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"userInfo"]];
Then, from the failure block callback, you can access the userInfo dictionary like:
NSString *message = [[[[[error userInfo] objectForKey:RKObjectMapperErrorObjectsKey] objectAtIndex:0] userInfo] objectForKey:#"message"];
NSString *title = [[[[[error userInfo] objectForKey:RKObjectMapperErrorObjectsKey] objectAtIndex:0] userInfo] objectForKey:#"title"];
RestKit will automatically map all attributes of the JSON's "error" dictionary to the RKErrorMessage's userInfo dictionary.
Why are you using RKErrorMessage ???
The RKErrorMessage is a simple class used for representing error
messages returned by a remote backend system with which the client
application is communicating.
I think the best way is to create the custom object to map your paramteres.
#interface MyCustomErrorObject : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *message;
#end