Unable to retrieve data with RestKit because class is not key value coding compliant - restkit

I am writing a simple Multiple choice question for iOS.
I want to create an API so that I can retrieve the questions and store the answers of the users. I use django and tastypie for the backend.
I use this function to load the quesion in my app :
- (void)loadQuestion
{
RKObjectMapping* questionMapping = [RKObjectMapping mappingForClass:[Question class]];
[questionMapping addAttributeMappingsFromDictionary:#{
#"question": #"question",
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:questionMapping pathPattern:nil keyPath:#"question" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
NSURL *URL = [NSURL URLWithString:#"http://127.0.0.1:8000/api/v1/question/2/?format=json"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSLog(#"request : %#", request);
RKObjectRequestOperation *objectRequestOperation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[objectRequestOperation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
RKLogInfo(#"Load collection of Articles: %#", mappingResult.array);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Operation failed with error: %#", error);
}];
[objectRequestOperation start];
}
Here is my question.h :
#import <Foundation/Foundation.h>
#interface Question : NSObject
#property (strong) NSString *question;
#end
And my question.m
#implementation Question
#synthesize question = _question;
#end
The JSON is the following :
{
"chapter": "/api/v1/chapter/2/",
"id": 2, "
pub_date": "2013-04-25T19:23:42.930097",
"question": "Quelle est la capitale de ce pays : Emirats Arabes Unis ?",
"resource_uri": "/api/v1/question/2/"
}
And the tastypie API is :
from tastypie.resources import ModelResource
from tastypie import fields
from .models import Question, Chapter
class ChapterResource(ModelResource):
class Meta:
queryset = Chapter.objects.all()
resource_name = 'chapter'
class QuestionResource(ModelResource):
chapter = fields.ForeignKey(ChapterResource, 'chapter')
class Meta:
queryset = Question.objects.all()
resource_name = 'question'
The error i get is :
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x76b3030> valueForUndefinedKey:]: this class is not key value coding-compliant for the key question.'
I read https://github.com/RestKit/RestKit/wiki/Object-Mapping but did not find what I am doing wrong.
What is wrong with my class ?

Try turning on trace logging to get more details about what's happening with the mapping:
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
That said, I'm not sure it's a problem with the mapping. The exception is thrown while trying to read the question from a string object. RestKit shouldn't be doing that based on your use case. So, you need to add a breakpoint on all exceptions so you can see the full stack trace and work out what's going on: Xcode adding an exception breakpoint.
Looking at your JSON and mapping spec it seems ok.

Related

How to add CSRF token into http header on AFHTTPRequestOperationManager?

I created a test application (using scaffold) at heroku and I built an iOS client (using AFNetworking 2) to this heroku application. I was trying to delete records from heroku using iOS app and It didn't work. I received 422 status error from server.
Looking at heroku logs I figure out that server is claiming for CSRF token. So I tried to do that with this code on my iOS client:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer new];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json", nil];
[manager DELETE:contact.url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Response: %#", [operation description]) ;
if (block) {
block(error);
}
NSLog(#"Error: %#", error);
}];
It didn't work.
How can I add CSRF token into http header on AFHTTPRequestOperationManager ?
Two things here:
If your server is complaining about a lack of a CSRF token, then forging one isn't going to be the correct solution. See this Stack Overflow answer for more information. While you're just starting to get everything up-and-running, you can temporarily disable this by commenting out the protect_from_forgery call in your API controller.
AFHTTPRequestOperationManager is initialized with an AFJSONResponseSerializer already, so you don't need to set that yourself. You can add a default X-XSRF-TOKEN (or whatever header by doing [manager.requestSerializer setValue:#"..." forHTTPHeaderField:#"..."];
With AFNetworking2 you customize http headers in request serializer. So you need to subclass the one you currently work with and add this logic there.
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(NSDictionary *)parameters
error:(NSError *__autoreleasing *)error {
NSMutableDictionary *modifiedParams = [parameters mutableCopy];
modifiedParams[#"your-header-name"] = #"you-header-value";
NSMutableURLRequest *res = [super requestBySerializingRequest:request
withParameters:modifiedParams
error:error];
return res;
}

Using FaceBook SDK why do I get 'NSInvalidArgumentException', reason: '-[__NSDictionaryM length]: unrecognized selector sent to instance

This code gives this error...why ?
'NSInvalidArgumentException', reason: '-[__NSDictionaryM length]: unrecognized selector sent to instance
NSMutableDictionary *info = [NSMutableDictionary dictionaryWithObjectsAndKeys:longitude,#"longitude",latitude,#"latitude",nil];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys: info, #"venue", nil];
NSString *graphPath = [NSString stringWithFormat:#"/%#", (ID_OF_EVENT)];
FBRequest *updateEvent = [FBRequest requestWithGraphPath:graphPath parameters:params HTTPMethod:POST];
[updateEvent startWithCompletionHandler: ^(FBRequestConnection *connection,
NSDictionary* result,
NSError *error) {
I am trying to set the venue longitude and latitude, but no matter what I try I keep getting the run time error. What am I missing ? I thought "venue" is a dictionary under the main event and has the valid keys "longitude" and "latitude". Have one my parameters got to be an Array and not a dictionary ?
Actually I suspect these elements are non writable and I probably have to add the address at #"location" to see the details appear.
Regards

Ignoring SSL certificate errors with NSURLConnection

I'm using [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)] to pull data from a web service, but the web server has a self-issued certificate, causing this error to appear:
Error displayed using:
NSAlert *a = [NSAlert alertWithError:error];
[a runModal];
Is there any way to ignore this error and continue anyway?
Following the instructions in the linked question, I defined a dummy interface for NSURLConnection:
#interface NSURLRequest (DummyInterface)
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
#end
And called the method before creating the request:
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
This suppressed the error about the certificate being invalid.
This may be rejected by Apple. Use the proper implementation of NSConnectiondatadelegate:
see a way around it:
Stackoverflow response to similar question

How to tell RestKit which mapping to use when using sendObject

I'm new to RestKit so this question may be dumb: When using [objectManager sendObject:...], how do I tell RestKit which mapping it should use for the result? More specific: I am sending GET and POST data to a server which in return responds with a JSON encoded message:
{"purchaseresult":{"status":"ok","errormsg":""}}
The Objective-C code I use looks like this:
RKObjectMapping *purchaseResultMapping = [RKObjectMapping mappingForClass:[PurchaseResult class]];
[purchaseResultMapping mapKeyPathsToAttributes:#"status", #"status", #"errormsg", #"errorMessage",nil];
[objectManager.mappingProvider setMapping:purchaseResultMapping forKeyPath:#"purchaseresult"];
[[RKObjectManager sharedManager].mappingProvider setErrorMapping:purchaseResultMapping];
[objectManager sendObject:queryParams toResourcePath:#"/purchase/post" usingBlock:^(RKObjectLoader* loader) {
loader.method = RKRequestMethodPOST;
loader.resourcePath = #"/purchase/post";
loader.params = queryParams;
loader.objectMapping = purchaseResultMapping;
}];
This returns an RestKit error:
restkit.network:RKObjectLoader.m:216 Encountered errors during mapping: Expected an object mapping for class of type '__NSDictionaryI', provider returned one for 'PurchaseResult'
Any ideas what I am doing wrong here?
Thanks
Christian
The object that you're sending is a NSDictionary so RestKit is looking for an object mapping for an NSDictionary but you are providing a PurchaseResult mapping. If you send an instance of PurchaseResult or provide your mapping for the NSDictionary class then this error should be avoided.
An alternative to this would be as follows:
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/purchase/post" usingBlock:^(RKObjectLoader *loader) {
loader.delegate = **Your loader delegate here**;
loader.method = RKRequestMethodPOST;
loader.params = queryParams;
}];
Just make sure you've defined your mapping for the objects coming back in and you should be good.

Restkit: migrating to 0.20

I am trying to migrate to RestKit 0.20-pre2.
Currently I managed to migrate my mapping (at least the compiler does not complain anymore), but I have problems in creating requests (previously I used the RKObjectLoader which does not exist anymore.
My previous code is the following:
- (RKObjectLoader*)objectLoaderWithResourcePath: (NSString*)resourcePath
method: (RKRequestMethod)httpMethod
parameters: (NSDictionary*)parameters
mappableClass: (Class)objectClass
{
RKObjectMapping *mapping = [self.objectManager.mappingProvider objectMappingForClass:objectClass];
NSString *path = resourcePath;
if (httpMethod == RKRequestMethodGET) {
path = [resourcePath stringByAppendingQueryParameters:parameters];
}
RKObjectLoader *request = [self.objectManager loaderWithResourcePath:path];
request.method = httpMethod;
request.delegate = self;
request.objectMapping = mapping;
if (httpMethod != RKRequestMethodGET) {
request.params = parameters;
}
return request;
}
I used the above method to create a generic request, and then send it either synchronously or asynchronously.
Now... I saw the new method getObjectsAtPath: parameters: success: failure:, but.. I need the same for the POST (and I don't have any object to post... it is simply the server which accept a POST request for the login..)
Any help?
Thank you
I had the same problem as you and i received a great answer here:
Trying to make a POST request with RestKit and map the response to Core Data
Basically,
This is what you need:
NSDictionary *dictionary = #{ #"firstParam": #(12345), #"secondParam": #"whatever"};
NSMutableURLRequest *request = [objectManager requestWithObject:nil method:RKRequestMethodPOST path:#"/whatever" parameters:parameters];
RKObjectRequestOperation *operation = [objectManager objectRequestOperationWithRequest:request ^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSLog(#"Loading mapping result: %#", result);
} failure:nil];
There is an example here in README section Managed Object Request that will help you:
https://github.com/RestKit/RestKit
You can use AFNetworking directly using the RK HTTPClient subclass, something like this:
[[RKObjectManager sharedManager].HTTPClient postPath:#"/auth" parameters:params success:^(AFHTTPRequestOperation *operation, id JSON)
{
// Success
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
// Error
}];
Since RestKit v0.20.x, RK now use AFNetworking under the hood instead of RKClient, so you can refer directly to the AFNetworking docs:
http://afnetworking.github.com/AFNetworking/Classes/AFHTTPClient.html#//api/name/postPath:parameters:success:failure:
Edit
In my project, for the auth, I simply created an NSObject named User, with a singleton, and managed the mapping myself. I (personally) didn't need to have my auth user in my core data stack. If you need to use the RK Core data mapping capabilities, take a look at RKObjectManager with the postObject:path:parameters:success:failure: method.

Resources