Restkit mapping and inserting data works fine, but I need to add custom values to the database (not from JSON)
RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:entityName inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:dict];
if (uniqKey != nil) {
entityMapping.identificationAttributes = #[ uniqKey ];
}
// Set MIME Type to JSON
manager.requestSerializationMIMEType = RKMIMETypeJSON;
// register mappings with the provider using a response descriptor
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:entityMapping
method:RKRequestMethodPOST
pathPattern:path
keyPath:rootKeyPath
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[manager addResponseDescriptor:responseDescriptor];
[manager postObject:nil path:path parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
if (mappingResult.array.count != 0) {
NSDictionary *data = mappingResult.array[0];
NSLog(#"data: %#", data);
}else{
NSLog(#"Unable to fetch data from: %#", path);
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Error response': %#", error);
}];
Other than NSPredict and filtering the data, is is possible to insert values (like string) manually while mapping?
You can modify the objects from the mapping result in the completion block, but then you need to explicitly save the context and other observers of the context will have received a save notification. This is the super simple approach.
Alternatively you could override willSave or use NSManagedObjectContextWillSaveNotification (the latter being the better option) to trigger your custom logic. Your changes would then be made inline with the RestKit changes and would be automatically saved.
Solved the issue using
[[mappingResult set] setValue:value forKey:key];
in manager on success block.
Related
I'm using restkit on IOS. I'm having trouble parsing a simple json object.
I have a simple json object returned:
{"_id"=>"537c235189d50fabcc000009",
"about"=>"nice",
"headline"=>"looking",
"access_token"=>
"$2a$10$oZ4IiaVBxHhc1qGeBoZv1uYonBM3Qb5Y010rTkUOynDZIGdGagqJy"}
My setup:
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureRestKit];
[self loadVenues];
}
- (void)loadVenues
{
NSString *clientToken = SNAPTOKEN;
NSDictionary *profile = #{#"os": #"iOS",
#"device_token": #"first_token",
#"password":#"password",
#"email":#"email",
#"headline":#"hello world"};
NSDictionary *queryParams = #{#"token" : clientToken,
#"profile" : profile};
[[RKObjectManager sharedManager] postObject: nil
path: #"/profiles/new"
parameters:queryParams
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"log%#", mappingResult );
_profile = mappingResult.array;
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"What do you mean by 'there is no coffee?': %#", error);
}];
}
- (void)configureRestKit
{
// initialize AFNetworking HTTPClient
NSURL *baseURL = [NSURL URLWithString:#"https://airimg.com"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
// initialize RestKit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
// setup object mappings
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[Profile class]];
[venueMapping addAttributeMappingsFromDictionary:#{ #"_id": #"about", #"access_token": #"headline" }];
// register mappings with the provider using a response descriptor
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:venueMapping
method:RKRequestMethodPOST
pathPattern:#"/profiles/new"
keyPath:#"response"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
}
I have the following error:
NSLocalizedFailureReason=The mapping operation was unable to find any nested object representations at the key paths searched: response
The representation inputted to the mapper was found to contain nested object representations at the following key paths: _id, about, access_token, headline
This likely indicates that you have misconfigured the key paths for your mappings., NSLocalizedDescription=No mappable object representations were found at the key paths searched., keyPath=null}
The main problem is that your response descriptor uses a key path of response and your JSON doesn't include that key at the top level. So, set the key path to 'nil'.
Also, your view controller should store the reference to the objectManager and use it again later. Currently you use [RKObjectManager sharedManager] and that returns the first object manager that was ever created so as soon as you have multiple view controllers each creating their own object manager you will have issues.
Of course, the view controllers shouldn't necessarily be creating individual object managers...
I try to save to Core Data NSManagedObject which I got from server. But I don't know any idea how to save object got from [mappingResult firstObject] in success block to Core Data. How can I do this? Should I use RKObjectManager's postObject or RKManagedRequestOperation? Should I do [managedObjectContext insertNewObjectForEntityForName:#""] before this?I can't find any instructions in official docs for this case and need some help.
**EDIT: **I initialise RKManagedRequestOperation like this:
RKResponseDescriptor* responseDescriptor =[RKResponseDescriptor
responseDescriptorWithMapping:[UserMapping mappingForUser]
method:RKRequestMethodPOST
pathPattern:kUserEndpoint
keyPath:#"profile"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKRequestDescriptor* requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[UserMapping mappingForUserProfileModel]
objectClass:[User class] rootKeyPath:#"profile" method:RKRequestMethodPOST];
[[RKObjectManager sharedManager] addRequestDescriptor:requestDescriptor];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
userObject = [User new];
NSDictionary* userParameters = #{ #"user_id" : [User sharedUser].userId};
[[RKObjectManager sharedManager] appropriateObjectRequestOperationWithObject:resumeObject method:RKRequestMethodPOST
path:kUserEndpoint
parameters:userParameters];
RKManagedObjectRequestOperation* managedRequest = [[RKManagedObjectRequestOperation alloc]
initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#", kService, kUserEndpoint]]]
responseDescriptors:#[responseDescriptor]];
managedRequest.managedObjectContext = _managedObjectContext;
[managedRequest setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"MAPPING = %#", [mappingResult firstObject]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
[[RKObjectManager sharedManager] enqueueObjectRequestOperation:managedRequest];
EDIT2: RestKit doesn't save mapped data to CoreData. But userObject.title saves perfectly:
userObject = [_managedObjectContext insertNewObjectForEntityForName:#"User"];
userObject.title = #"USER_NAME";
NSDictionary* userParameters = #{ #"user_id" : [User sharedUser].userId};
[[RKObjectManager sharedManager] postObject:userObject path:kUserEndpoint parameters:userParameters
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
} failure:^(RKObjectRequestOperation *operation, NSError *error) { }];
You need to take a step back as your suggestions are shooting in the dark.
If you configure your object manager with a managed object store and create response descriptors with entity mappings then when you receive data as a result of requests this will be converted into managed objects. These objects will automatically be saved to the core data store before the success block is called.
Any other objects you want to create can be created as usual and you need to explicitly save the context.
Sending requests with RestKit doesn't itself change the store contents, only the response results in changes.
See this tutorial. There is an example of using RestKit with object management and Code Data
https://github.com/alexanderedge/RestKitTutorial1
I try to post an object Foobar (the class has two attributes: string foo and string bar) using the method postObject of the class RKObjectManager.
Server-side, I have a WCF service that receive the POST method
public void PostFoobar(Foobar foobar) { ... }
All the connection works. The problem is that the object foobar received is always NULL. It seems ResKit does not POST my object as an encapsulation of the two arguments, but post two string independently.
I mean, when I tryed to implement the following method (server-side) :
public void PostFoobar(string foo, string bar) { ... }
and the two parameters was not null ! it has worked !
But I would prefer to recover the serialized object obviously...
My question is :
How am I suppose to configure my POST request to recover an entire Foobar object on the server side, and not every attributes independently ?
My code
Here is my code to send the POST request
NSError *error = nil;
NSManagedObjectModel *managedObjectModel = #"myObjecModel";
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Configure the object manager
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://192.168.1.10/rest"]];
objectManager.managedObjectStore = managedObjectStore;
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[RKObjectManager setSharedManager:objectManager];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/plain"];
RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:#"Foobar" inManagedObjectStore:managedObjectStore];
[postMapping addAttributeMappingsFromDictionary:#{
#"foo" : #"strFoo", // server side:foo, iOS side: strFoo
#"bar" : #"strBar" // server side:bar, iOS side: strBar
}];
RKRequestDescriptor * requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[postMapping inverseMapping] objectClass:[Foobar class] rootKeyPath:nil method:RKRequestMethodPOST];
[objectManager addRequestDescriptor:requestDescriptor];
// POST to create
RKManagedObjectStore *objectStore = [[RKObjectManager sharedManager] managedObjectStore];
Foobar *foobar = [NSEntityDescription insertNewObjectForEntityForName:#"Foobar" inManagedObjectContext:objectStore.mainQueueManagedObjectContext];
foobar.strFoo = #"foo ipad";
foobar.strBar = #"bar ipad";
#try {
[[RKObjectManager sharedManager] postObject:foobar path:#"foobar" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error.localizedDescription);
}];
}
#catch (NSException *exception) {
NSLog(#"error - %#", exception);
}
One more thing :
I have tested my web service with the chrome client "Simle REST client" and it works with the data :
{ "foo": "foo from chrome", "bar" : "bar from chrome" }
Edit - Frame capture with Wireshark
It seems to be good.. I don't understant.
You're calling with nil:
[[RKObjectManager sharedManager] postObject:nil path:#"foobar" parameters:nil ...
So RestKit is given nothing to serialise and no parameters.
If that's a typo, turn on trace logging for mapping and look at what it's doing. Also, get Charles or a similar tool and check exactly what data is being sent over the network.
I am attempting to upgrade my current RestKit to 0.20.3, and in one case I need to get the result back as a dictionary, not mapped to any object.
Unfortunately, the code below results in an essentially empty RKMappingResult object, despite the fact that a correct dictionary does exist deeper in RestKit (I checked).
[self.objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[RKObjectMapping mappingForClass:[NSMutableDictionary class]]
method:RKRequestMethodGET
pathPattern:#"permission.json"
keyPath:nil
statusCodes:nil]];
[self loadObject:[[NSMutableDictionary alloc] init]
method:RKRequestMethodGET
path:#"permission.json"
params:nil
success:^(RKObjectRequestOperation *op, RKMappingResult *result) {
if (success)
success([result dictionary]);
}
failure:^(RKObjectRequestOperation *op, NSError *error) {
if (failure)
failure(error);
}];
Load object calls the following:
RKObjectRequestOperation *const operation =
[self operationForObject:object
method:method
path:path
params:params
success:success
failure:failure];
[self.objectManager enqueueObjectRequestOperation:operation];
return operation;
Can anyone give me some direction on how I might convince RestKit to give me back a dictionary?
This is indeed one of the correct ways to create the mapping instance for a dictionary result:
[RKObjectMapping mappingForClass:[NSMutableDictionary class]]
But you still need to set the key names that should be mapped or RestKit will just do no mapping.
I'm trying to send an object to the server (PUT request) without a mapping because I already have everything I need from the server. The callback directly goes to failure, even though the server has sucessfully created the object.
Here is an example of what I am doing:
- (void) PUTsuccess:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
{
RKObjectManager *objectManager = [YSManager objectManager];
[objectManager putObject:self path:[kPutPath stringByAppendingString:self.id] parameters:kAPIDefaultParameters success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"operation.HTTPRequestOperation.request.HTTPBody: %#", [[NSString alloc] initWithData:operation.HTTPRequestOperation.request.HTTPBody encoding:NSUTF8StringEncoding]);
if (success) {
success(operation, mappingResult);
}
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure! Error: %#", error.localizedDescription);
if (failure) {
failure(operation, error);
}
}];
}
The server response is 201 created and I can find back my object in the server so that's all good, but still, the callback directly fires failure because it's trying to map my object, the error in the failure callback is:
Error: No response descriptors match the response loaded.
Thanks a lot, any suggestion will be appreciated!
Update 1
Added dictionary mapping now:
RKObjectMapping *idMapping = [RKObjectMapping requestMapping];
[idMapping addAttributeMappingsFromArray:#[]];
RKResponseDescriptor *responseDescriptorID = [RKResponseDescriptor responseDescriptorWithMapping:idMapping
pathPattern:[kRequestPath stringByAppendingString:#":code"]
keyPath:#"objects"
statusCodes:statusCodes];
Define a response descriptor simply to map the response (or part of it) to an NSDictionary.