RestKit: confused as to how to setup a POST - objective-c

I have managed to GET objects and to POST a new object to the server, but the POST generates an error on the iPhone.
Here is my setup:
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMappingFoo pathPattern:#"/foos" keyPath:#"foos" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; // Works well for GET
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[entityMappingFoo inverseMapping] objectClass:[Foo class] rootKeyPath:#"foo"]; // Client --> Server works
The problem is (I think) that:
when I GET, the back-end expects the JSON to have a plural key: {"foos":[...]} (which makes sense since there might be several objects)
when I POST one object, RestKit expects the answer from the back-end to be singular: {"foo":...}. Yet, since it uses the same responseDescriptor as for GET, it gets a plural and it is lost.
If I replace keyPath:#"foos" by keyPath:#"foo" in responseDescriptor my POST works... but not my GET.
How do I reconcile the two?

You're right. Basically you just need to create multiple response descriptors to cover the different cases. You can use the same mapping in each, but RestKit needs to know what to look for when processing a response and your 2 cases are different.

Related

Which request to use to fetch data from database based on some data sent?

I am using django-rest-framework's genericAPIViews
I want to send some data from the front end to the backend and depending upon the data sent Django should query a model and return some data to the frontend. The data sent is protected data and thus can't be attached in the URL so, GET request can't be used. I am not manipulating the database, just querying it and returning a response (a typical GET use case).
Now in DRF's genericAPIViews, I can't find a view which does this:
As can be seen from Tom Christie's GitHub page only 2 views have a post handler:
CreateAPIView: return self.create()
ListCreateAPIView: return self.create()
As can be seen both these views have post methods which create entries in the database which I don't want. Is there a built-in class which does my job or should I use generics.GenericAPIView and write my own post handler?
Currently I am using generic.View which has post(self, request, *args, **kwargs)
I think you have a few options to choose from. One way is to use a ModelViewSet which could be quite useful because of how it nicely handles the communication between views, serializers and models. Here is a link to django-rest-framework ModelViewSet docs.
These are the actions that it provides by default (since it inherits from GenericAPIView):
.list(), .retrieve(), .create(), .update(), .partial_update(), .destroy().
If you don't want all of them you could specify which methods you want by doing the following:
class ModelViewSet(views.ModelViewSet):
queryset = App.objects.all()
serializer_class = AppSerializer
http_method_names = ['get', 'post', 'head']
Note: http_method_names seems to be working from Django >= 1.8
Source: Disable a method in a ViewSet, django-rest-framework

afnetworking request parameter sequence

I am trying to produce a request using afnetworking in objective c, however, it seems like the hardware that I am trying to connect to only applies requests when the parameters of the request are in a specific order. So I am wondering if there is a way to make the request so that the parameters are in a specific order. (As just doing it normally seems to jumble the sequence of the params up)
Here's my code:
NSDictionary *params = #{
#"param1" : #"bla",
#"param2" : #"bla2",
#"param3" : #"bla3"
};
[requestManager GET:#"somewhere" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
DLog(#"Success!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DLog(#"Fail: %#", error);
}];
It actually goes to success every time, its just that the request I had applied would be practically ignored.
The actual request body becomes something like "param3=bla3&param1=bla1&param2=bla2 etc which would be ignored as it seems.
You can't do that using the request manager in the way you currently are.
Instead, you would need to create the parameter list yourself, and then create a request from that. Then you could use AFN to handle the request transmission and response.
Note that the server shouldn't require a specific order and that this should be changed if possible. Note also that the dictionary of parameters has no order (even though you add the keys in a set order).
Keeping the order of the parameters have a great impact on server performance. This sounds silly at first, but just think about GET requests which contain the query string as part of the URL. Web servers can cache the response for the given URL. If you mess with the order of the parameters, the cache won't be as effective as it could be.
The case is even worse if you call an API from different platforms (iOS, Android, Web) and they all reorder the params, which means that the same content will be found on 3 different cache keys.
Keeping the order is a performance issue at the first place.

Restkit with bare json response objects

I'm fairly new to Restkit but so far its worked pretty well for me using version 0.20.3 for most of my networking needs.
Im consuming a json based API written in c# using WCF webhttp bindings, it is worth mentioning at this point that I have absolutely no control of this API and cannot change the format of the returned json and I need to work with what I have.
The problem is that when the API returns a simple type like int, double or string as the response the json response is completely bare as below..
string response
"hello world"
int response
2342524
Both of these example responses have a content type of application/json
Ive tried to consume an API endpoint with restkit that gets a count of customer orders by the customer number.
The code for the request is as follows and Im expecting an NSNumber as the response but its generating an error as its a raw unwrapped type and I cant provide a mapping for this.
[manager getObject:nil
path:#"/service/http/CountOrders?CustomerId=324534413"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
RKLogInfo(#"order count: %#",mappingResult);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Operation failed with error: %#", error);
}];
And the error I'm getting back is
restkit.network:RKResponseMapperOperation.m:317 Failed to parse response data: Loaded an unprocessable response (200) with content type 'application/json'
restkit.network:RKObjectRequestOperation.m:213 GET 'http://CUSTOMERDOMAIN/service/http/CountOrders?CustomerId=324534413'
(200 OK / 0 objects)
[request=0.2263s mapping=0.0000s total=0.2386s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1017 "Loaded an unprocessable response (200) with content type 'application/json'"
UserInfo=0x8e1a660 {NSErrorFailingURLKey=http://CUSTOMERDOMAIN/service/http/CountOrders?CustomerId=324534413, NSUnderlyingError=0x8e1b1a0
"The operation couldn’t be completed. (Cocoa error 3840.)",
NSLocalizedDescription=Loaded an unprocessable response (200) with content type 'application/json'}
hj
Is there any way of parsing the the response to an NSNumber to cover this edge case?
Im thinking a custom deserialization handler might be the way to go if thats at all possible but as I said I'm new to restkit and am basically looking for a push in the right direction.
A custom serializer could work, but a simple approach is probably to skip RestKit for the requests with 'simple' responses and use the underlying AFNetworking classes instead. Then you don't need to worry about serialization and you can just quickly coerce the response value.

Get the MappingResult firstObject value within the Persistent Storage to avoid "Illegal attempt to establish relationships" errors

I am fetching an object from the persistentStoreManagedObjectContext and showing some of its value to my user within a UIView. That object, let's call it Book has an Author relationship. Whenever I am about to display that book, I check if the author is set or not. If it's not set, I do:
[[RKObjectManager sharedManager] getObjectsAtPath:[NSString stringWithFormat:#"/api/rest/userprofiles/%#/",_currentBook.authorID] parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {..}
Within the success callback, I want to do a:
_currentBook.author = mappingResult.firstObject;
if (![_currentBook.managedObjectContext saveToPersistentStore:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
However, I can't do _currentBook.author = mappingResult.firstObject; because I get:
Illegal attempt to establish a relationship between objects in different contexts
However, I know that this newly fetched Author is saved within the persistentStoreCoordinator, because I configured my RestKit to do so (or it's by default, I can't remember). So I don't want to create that object AGAIN, I would just want to get it's value within my currentContext which is persistentStoreManagedObjectContext. Do I have to use a NSFetchedResultsController just for that?
Edit
I already have a connection in the book to connect to the author, via the authorID, which I send back at the same time as getting the Book object from my server. However, it might be the case that the Author Object is not fetched yet, hence I keep that ID.
[bookMapping addConnectionForRelationship:#"author" connectedBy:#{ #"authorID": #"identifier" }];
However, even after that author is fetched, book.author is set to null, unless I REFETCH once again in the persistent storage.
You should get RestKit to connect the relationship using foreign key mapping based on the author id. That way all of the updates are made at the same time, in the same context, and saved before the mapping result is returned.

"onDownloadProgressChanged:" not being called (MKNetworkKit)

I'm working on a project that requires downloading a list of users from a server —JSON data created from a PHP script that reads a MySQL database— and I would like to inform the user of the progress of the request but onDownloadProgressChanged: never gets called when sending a GET request through operationWithPath:params:httpMethod:ssl: and I don't know if that is an intended behavior or not.
MKNetworkOperation *op = [self operationWithPath:kSPGetUserListPath params:nil httpMethod:#"GET" ssl:YES];
Should onDownloadProgressChanged: be called when I send a GET request with operationWithPath:params:httpMethod:ssl: or is it only called when downloading a file using addDownloadStream:?
Whenever I send a POST request with a file attached through addData: method of MKNetworkOperation the onUploadProgressChanged: method get called accordingly.
Thank you!!!
I had the same problem because missed something like the following MKNetworkEngine initializing in the main class:
self.sampleDownloader = [[ExampleDownloader alloc] initWithHostName:nil customHeaderFields:nil];