How to use Restkit mapping - restkit

I am using the development branch to avoid starting with the old as the new is rolling out. Unfortunately, I cannot figure out how to do the basics (I am new to RestKit).
The basic steps I am trying are to accomplish start with calling "/auth/login/" with 2 parameters and retrieve a json document back. Any help would be greatly appreciated!
UPDATE 1: am I asking the wrong question? what am I missing? do people just no use Restkit for projects?
UPDATE 2: what should I look for when getting this error? I have a class mapping and a path pattern, but I really don't get what I should be doing.
Code=1001 "Unable to find any mappings for the given content"
I just found this updated readme file at https://github.com/RestKit/RestKit/blob/development/README.md
UPDATE 3: I have tried a variety of ways to do a simple call/respond/success call but nothing. I can see in the output the call is successful, but RestKit always complains it cannot map the content. I really do not get it. The resulting json is mostly as follows:
{
"email" : "me#here.com",
"fullname" : "Full Name"
}
and no matter what I try, I cannot get RestKit to figure that out. Help? Anyone?
UPDATE 4: I changed the payload to be the following, plus I changed the descriptor statement, but there was no change in results. The call succeeds, RestKit fails for error 1001. It still says my keyPath=null. What am I missing?
{
"whoami" : {
"email" : "me#here.com",
"fullname" : "Full Name"
}
}
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:mymap
pathPattern:#"/auth/login/"
keyPath:#"whoami"
statusCodes:statusCodes]];

Maybe this can help..the request may look like this:
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:email forKey:#"email"];
[params setObject:fullname forKey:#"fullname"];
NSMutableURLRequest *rq = [manager requestWithObject:[User new] method:RKRequestMethodPOST path:#"http://url" parameters:params];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[User mapping] pathPattern:nil keyPath:nil statusCodes:nil];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:rq responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
//success
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
//error
}];
[operation start];
and mapping:
+ (RKObjectMapping *)mapping
{
RKObjectMapping *requestMapping = [RKObjectMapping mappingForClass:[self class]];
[requestMapping addAttributeMappingsFromDictionary:#{
#"ContactDetail": #"contactDetail",
#"IdUser": #"idUser",
#"Name": #"name",
#"Address": #"address",
#"UserSettings": #"userSettings"
}];
// if the obj have relationship
RKRelationshipMapping *rl = [RKRelationshipMapping relationshipMappingFromKeyPath:#"someKey" toKeyPath:#"toKey" withMapping:[Obj mapping]];
[requestMapping addPropertyMappingsFromArray:#[rl]];
return requestMapping;
}

Related

Can I use RestKit and Realm.io?

I want to use RestKit, but I already use Realm.io instead of CoreData.
Is it possible to use RestKit on top of Realm.io?
Sure you can. Once you get the object back from RestKit:
// GET a single Article from /articles/1234.json and map it into an object
// JSON looks like {"article": {"title": "My Article", "author": "Blake", "body": "Very cool!!"}}
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[Article class]];
[mapping addAttributeMappingsFromArray:#[#"title", #"author", #"body"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:#"/articles/:articleID" keyPath:#"article" statusCodes:statusCodes];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://restkit.org/articles/1234.json"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
Article *article = [result firstObject];
// I would put the Realm write here
NSLog(#"Mapped the article: %#", article);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
[operation start];
You will need to do two things:
Create your RealmArticle model (in this case) that inherits from RLMObject
Then you will just need to write to your realm
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[RealmArticle createInDefaultRealmWithObject:article];
[realm commitWriteTransaction];

Synchronous calling web API service for a generic method

I am using RestKit version 0.20.3 to make a generic method that is used in many other places. The problem is the returned value from that method is always nil because the "return location;" statement is executed BEFORE the Success call back function over the [objectManager getObjectsAtPath ...] method (see below codes).
I want the "return location;" statement must WAIT for the block variable "location" is filled with data from the Success call back function inside the [objectManager getObjectsAtPath ...] method. How can I do this?
Thank you for your help.
My generic method looks like:
-(KNSunGoogleLatitudeLongitudeGeometryLocation*)getSynchronouslyLatitudeLongitudeWithAddress:(NSString*)address
{
__block KNSunGoogleLatitudeLongitudeGeometryLocation* location = [[KNSunGoogleLatitudeLongitudeGeometryLocation alloc] init];
NSURL *baseURL = [NSURL URLWithString:#"http://maps.googleapis.com/maps/api"];
AFHTTPClient * client = [AFHTTPClient clientWithBaseURL:baseURL];
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
//1. KNSunGoogleLatitudeLongitudeGeometryLocation
RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[KNSunGoogleLatitudeLongitudeGeometryLocation class]];
[locationMapping addAttributeMappingsFromArray:#[#"lat", #"lng"]];
//2. KNSunGoogleLatitudeLongitudeGeometry
RKObjectMapping *geometryMapping = [RKObjectMapping mappingForClass:[KNSunGoogleLatitudeLongitudeGeometry class]];
//3. KNSunGoogleLatitudeLongitude
RKObjectMapping *latLongMapping = [RKObjectMapping mappingForClass:[KNSunGoogleLatitudeLongitude class]];
//4. property/relationship mapping
[geometryMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"location"
toKeyPath:#"location"
withMapping:locationMapping]];
[latLongMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"geometry"
toKeyPath:#"geometry"
withMapping:geometryMapping]];
// 6. response
RKResponseDescriptor * responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:latLongMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"results"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
// 7
[objectManager addResponseDescriptor:responseDescriptor];
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:address, #"address", #"false", #"sensor", nil];
// 6
[objectManager getObjectsAtPath:#"http://maps.googleapis.com/maps/api/geocode/json"
parameters:queryParams
success:^(RKObjectRequestOperation * operaton, RKMappingResult *mappingResult)
{
//-----------
NSArray* results = [mappingResult array];
KNSunGoogleLatitudeLongitude* result0 = [results objectAtIndex:0];
KNSunGoogleLatitudeLongitudeGeometry* geometry = result0.geometry;
location= geometry.location;
NSLog(#"lat=%#, long=%#", location.lat, location.lng);
}
failure:^(RKObjectRequestOperation * operaton, NSError * error)
{
NSLog (#"failure: operation: %# \n\nerror: %#", operaton, error);
}];
return location; // note: ALWAYS RETURNs nil
}
You need to change what you want because it's a bad design. You should not block the requestor while the request is in progress. Instead you should pass a block to your general method that is executed from the block you pass to RestKit. This allows you to properly respect the asynchronous nature of the request.
If you did want to proceed with blocking, you could use look at using a semaphore. But, you would need to manage this yourself. And you wouldn't be able to trigger the request on the main thread - ever. These are significant hurdles to general usage and will probably cause you issues in the future.

Restkit 0.20 basic operation

I am just getting started with RestKit and have arrived just as Rk 0.20 is going live and the documentation and demo's are a step behind. Most stuff on the web is for RK 0.10 and there are big changes in the 0.20 version.
I don't want to fall back to an earlier version when the new one will very soon be up and running.
I have a JSON resource at a URL "test.myserver.com" that returns a simple datagram -
{
"id_user": "4401",
"datalocation": "4401",
"country": "Great-Britain",
"data": "testdata",
"login": "Fred Bloggs",
"password": "579c0cb0ed2dc25db121283f7a98cc71",
"accessLevel": "2",
"timestamp": "1012",
"datahash": "2749da29f20ce7a85092323f193adee8"
}
I am pretty sure I have the Mappings etc sorted but my service requires authentication so I need to pass a user name and password in the request to the server.
I have got this so far
NSURL *url = [NSURL URLWithString:#"http://test.myserver.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
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];
Which appears to contact the server but inevitably logs the following error
restkit.network:RKObjectRequestOperation.m:296 Object request failed: Underlying HTTP request operation failed with error: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200-299), got 401" UserInfo=0x7884030 {NSLocalizedRecoverySuggestion={
"error": {
"code": 401,
"message": "Unauthorized: Authentication required"
}
}, AFNetworkingOperationFailingURLRequestErrorKey=http://elancovision.umfundi.com>, NSErrorFailingURLKey=http://elancovision.umfundi.com, NSLocalizedDescription=Expected status code in (200-299), got 401, AFNetworkingOperationFailingURLResponseErrorKey=}
The question of course is how I add the user name and password into the request.
Sorry for the noob question!
With basic HTTP authentication, username and password should be inserted into the HTTP request authorization header field for each request.
First, I suggest you to use RKObjectManager to centralize configuration for requests and mappings. http://restkit.org/api/latest/Classes/RKObjectManager.html
RKObjectManager can store network parameters (through AFNetworking Library), then build appropriate http query based on username/password, paths, objectmapping.
Adapting your example, it would give something like :
NSURL* url = [[NSURL alloc]initWithString:#"http://test.myserver.com"];
RKObjectManager* objectManager = [RKObjectManager managerWithBaseURL:url];
[objectManager.HTTPClient setAuthorizationHeaderWithUsername:#"username" password:#"password"];
//NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLRequest *request = [objectManager requestWithObject:nil method:RKRequestMethodGET path:#"/yourAPI/yourmethod" parameters:nil];
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];
If authentication works, having a look at the RESTKit wiki should give you the next hints to build correct mappings : https://github.com/RestKit/RestKit/wiki/Object-mapping
My solution here:
// Build a RestKit manager object to look after the restful stuff
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://test.myserver.com"]];;
// Hash the GUI input password string and pass the username in plain text
NSString *md5PW = [umfundiCommon md5:passwordField.text];
[manager.HTTPClient setAuthorizationHeaderWithUsername:userField.text password:md5PW];
RKObjectMapping *WebResponse = [RKObjectMapping mappingForClass:[WSObject class]];
[WebResponse addAttributeMappingsFromDictionary:#{#"id_user":#"id_user", #"datalocation": #"datalocation", #"country":#"country", #"data": #"data", #"login": #"login", #"password": #"password", #"accessLevel": #"accessLevel", #"timestamp": #"timestamp", #"datahash": #"datahash"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:WebResponse pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
// Add the above response descriptor to the manager
[manager addResponseDescriptor:responseDescriptor];
// the getObject makes the call using the stuff assembled into the manager Object and drops into either the success or the failure routines.
[manager getObject:nil path:#"" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
NSLog (#"Server WS call success:");
NSArray *theresults = [result array];
for (WSObject *item in theresults) {
NSLog(#"datahash=%#",item.datahash);
NSLog(#"user_id=%#",item.id_user);
}
} failure:^(RKObjectRequestOperation * operation, NSError * error)
{
NSLog (#"Server WS call failure: operation: %# \n\nerror: %#", operation, error);
}];
........

TWRequest code works but very slow to show?

I'm working with TWrequest to display my twitter lists in a tableview. The following code works. The problem is it is very slow to update the table. I am NSlogging the request response (which happens very quickly), I am also looping through each list and adding the list 'name' to an array (which again, happens very quickly <1s). But for some inexplicable reason, the table takes roughly a further 4 seconds or so to update.
Why is this taking so long for the table to reload? The problem is not parsing the response (because I can see with nslog this happens pretty quick), it's taking a long time to display in the table? Help very much appreciated!
-(IBAction)getLists{
// First, we need to obtain the account instance for the user's Twitter account
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request permission from the user to access the available Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to the account.");
}
else {
// Grab the available accounts
twitterAccounts = [store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
// Now make an authenticated request to our endpoint
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
//[params setObject:#"1" forKey:#"include_entities"];
// The endpoint that we wish to call
NSURL *url = [NSURL URLWithString:#"http://api.twitter.com/1.1/lists/list.json"];
// Build the request with our parameter
TWRequest *request = [[TWRequest alloc] initWithURL:url parameters:params requestMethod:TWRequestMethodGET];
// Attach the account object to this request
[request setAccount:account];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!responseData) {
// inspect the contents of error
NSLog(#"error = %#", error);
}
else {
NSError *jsonError;
NSArray *timeline = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&jsonError];
if (timeline) {
// at this point, we have an object that we can parse
NSLog(#"timeline = %#", timeline);
for (NSDictionary *element in timeline) {
NSString *listName = [element valueForKey:#"name"];
[listsArray addObject:listName];
}
[listsTable reloadData];
}
else {
// inspect the contents of jsonError
NSLog(#"jsonerror = %#", jsonError);
}
}
}];
}
}
}];
}
Sorry, just came across this post. If you haven't found a solution yet, hopefully this will help.
I believe that performRequestWithHandler can be called on any thread, so UI changes should be dispatched to the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
});
Or in the case of reloading table data you can use:
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];

Core Data fetchedResultsController errors 'A fetch request must have an entity' entityForName returns nil

Hi I set up my own coredata app, or I tried...
First I created the xdatamodel and generated the Modelclasses, after this I implemented all the function of core-data in AppDelegate which I found in a generated project. Finally I copied the fetchedResultsController in my TableViewController.
fetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ParameterGroup" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
First I checked if the managedObjectsController is != nil, it has a address
Then I copied the EntityName from my xdatamodel in entityForName,
but NSEntityDescricption entity is nil.
And if I just create a new object the exception says, that the entity doesn't exist
Do I have to connect the xdatamodel to my project?
Hope you can help me
Thanks a lot!!!
The most common cause of this problem is simply misspelling the entity name wrong in the code such that it doesn't match the entity name in the data model.
Copy and paste the entity name from the model to the code and see if that fixes the problem.
The simplest way to solve this, given that you haven't done a lot coding on non-core-data parts, is probably to create a new project where you check the box for "Use Core Data". If you're going to use a Navigation Bar, choose this as your template. If I recall correctly, this will generate a table view with all functions needed. You'll have to modify the datamodel (generated).
Remark that you'll have to delete the app from the Simulator if it is installed and you change the datamodel (otherwise the generated data will not be consistent with the datamodel and the app will crash)

Resources