RestKit - GETting a JSON array from a Rails app - ios

I want to get this JSON data with my iOS app: https://sleepy-journey-2871.herokuapp.com/users.json .... RestKit tries to get these users from the url, but it returns 0 objects and says "No mappable representations were found at the key paths searched. No response descriptors match the response loaded."
PLEASE help me figure out what I'm missing or doing wrong! I've been battling this for weeks.
I have Xcode 5.0.2, I successfully installed RestKit with Cocoapods and a Podfile that looks like this:
platform :ios, '6.0'
pod 'RestKit', '~> 0.22.0'
pod 'RestKit/Testing'
pod 'RestKit/Search'
My AppDelegate.m file is below (the didFinishLaunchingWithOptions method):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
//Set base url
NSString *baseUrl = #"https://sleepy-journey-2871.herokuapp.com";
//initialize the the http client with baseUrl
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseUrl]];
//initialize the RKObjectManager with our http client
RKObjectManager *manager = [[RKObjectManager alloc] initWithHTTPClient:httpClient];
//add text/plain as a JSON content type to properly parse errors
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/plain"];
//register JSONRequestOperation to parse JSON in requests
[manager.HTTPClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
//state that we are accepting JSON content type
[manager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
//configure so that we want the outgoing objects to be serialized into JSON
manager.requestSerializationMIMEType = RKMIMETypeJSON;
//set the shared instance of the object manager, so that we can easily re-use it later
[RKObjectManager setSharedManager:manager];
return YES;
}
I have this code in the viewDidLoad method of the view controller that first loads when the app is launched:
- (void)viewDidLoad
{
[super viewDidLoad];
RKObjectManager *manager = [RKObjectManager sharedManager];
[manager getObjectsAtPath:#"/users"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
NSLog(#"Loaded databases: %#", [mappingResult array]);
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#", [error localizedDescription]);
}];
// Do any additional setup after loading the view, typically from a nib.
}

You have to:
Create a User class.
Create a mapping for User class.
Create a response descriptor.
Pseudocode could look something like:
#interface User : NSObject
#property (nonatomic, copy) NSNumber *userID;
#property (nonatomic, copy) NSString *name;
...
#end
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[User class]];
[mapping addAttributeMappingsFromDictionary:#{
#"name": #"name",
#"id": #"userID"
...
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodAny
pathPattern:nil
keyPath:nil
statusCodes:nil];
...
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSLog(#"The public timeline Tweets: %#", [result array]);
} failure:nil];
[operation start];
Take a look at examples here.

Related

Restkit basic url response

I am learning Rest-kit. i am trying to parse this url https://api.coursera.org/api/catalog.v1/courses?fields=language,shortDescription
I have created one Courses class.
#interface Courses : NSObject
#property (nonatomic, strong) NSString *name;
#end
and in viewcontroller i have wrote below code
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureRestKit];
[self loadCourses];
}
- (void)configureRestKit
{
// https://api.coursera.org/api/catalog.v1/courses?fields=language,shortDescription
// initialize AFNetworking HTTPClient
NSURL *baseURL = [NSURL URLWithString:#"https://api.coursera.org"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
// initialize RestKit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
// setup object mappings
RKObjectMapping *courseMapping = [RKObjectMapping mappingForClass:[Courses class]];
[courseMapping addAttributeMappingsFromDictionary:#{
#"name":#"name"
}];
// register mappings with the provider using a response descriptor
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:courseMapping method:RKRequestMethodGET pathPattern:#"/api/catalog.v1/courses?fields=language,shortDescription" keyPath:#"elements" statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
}
- (void)loadCourses
{
[[RKObjectManager sharedManager] getObjectsAtPath:#"/api/catalog.v1/courses?fields=language,shortDescription" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#", mappingResult.array);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"What do you mean by 'there is no Courses?': %#", error);
}];
}
ERROR :_Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded."
I am not getting response in success block. Please correct me. Thanks in advance
Don't add your query parameters to the path pattern on the response descriptor, and set them as parameters on the request:
pathPattern:#"/api/catalog.v1/courses"
generally I would also say that /api/catalog.v1/ should be part of the base URL so you would have:
NSURL *baseURL = [NSURL URLWithString:#"https://api.coursera.org/api/catalog.v1/"];
...
... pathPattern:#"courses" ...
...
... getObjectsAtPath:#"courses" parameters:#{ #"fields" : #"language,shortDescription" } ...

How use RestKit 2.X in iOS?

I'm new using RestKit, but I can't understand at all how it works...
please, can somewhere explain me it??
My Json file is:
{
"colors":
{
"red":"#f00",
"green":"#0f0",
"blue":"#00f",
"cyan":"#0ff",
"magenta":"#f0f",
"yellow":"#ff0",
"black":"#000"
}
}
and the path where i'm hosting this file is: http://186.36.181.116/tesis/file.json
The code that I'm trying in my ViewDidLoad method is:
- (void)viewDidLoad
{
[super viewDidLoad];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[colores class]];
[mapping addAttributeMappingsFromArray:#[#"colors"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:#"/tesis/:coloresID" keyPath:#"colors" statusCodes:statusCodes];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://186.36.181.116/tesis/file.json"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
colores *colores = [result firstObject];
NSLog(#"Mapped the article: %#", colores);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
[operation start];
}
My Class "colores" is as follow:
#import <Foundation/Foundation.h>
#interface colores : NSObject{}
#property (weak, nonatomic) IBOutlet NSString *colores;
#end
Thank you so much in advance!!
you can find a detailed tutorial here and full source code github.
In order to properly map responses to JSON, we must do the following things:
*Create an instance of RKEntityMapping for each entity in our managed object model
*Add mappings between JSON response keys and object properties
*Add mappings between embedded JSON objects and relationships
*Create response descriptors with the mappings
*Optional: Create request descriptors with the mappings if you plan to PUT or POST

this class is not key value coding-compliant for the key Text using RestKit v0.20.0

For 2 days now, I've been trying to find out why I'm getting the error using iOS 6.1.3 with Xcode 4.6.2 and RestKit 0.20.0:
"...this class is not key value coding-compliant for the key Text."
The strange part is that I can receive (GET) the JSON object fine. The error happens when I create my sample SignalMessage object and then try to PUT it back to the server.
The JSON is as follows:
{"Text":"New Message","HasMessage":"true"}
The SignalMessage object looks like this:
#import <Foundation/Foundation.h>
#interface SignalMessage : NSObject {
}
#property (nonatomic, copy) NSString *signalText;
#property (nonatomic, retain) NSNumber *isHasMessage;
#end
And the implementation like this:
#import "SignalMessage.h"
#implementation SignalMessage
#synthesize isHasMessage, signalText;
#end
My correctly working getMessage function looks like this:
- (IBAction)getMessage:(id)sender;
{
NSLog(#"%#", #"Getting message... ");
NSURL *url = [NSURL URLWithString:#"http://ec2-54-243-148-145.compute-1.amazonaws.com/TabletPractice/api/signal?clientIdentifier=2"];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:url];
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[SignalMessage class]];
[responseMapping addAttributeMappingsFromDictionary:#{#"Text":#"signalText", #"HasMessage": #"isHasMessage"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[manager addResponseDescriptor:responseDescriptor];
[manager getObject:nil path:#"" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
NSArray *theresults = [result array];
for (SignalMessage *item in theresults) {
self.txtMessage.text = item.signalText;
[self hideControls];
}
} failure:^(RKObjectRequestOperation * operation, NSError * error)
{
NSLog (#"Server WS call failure: operation: %# \n\nerror: %#", operation, error);
}];
}
And here is the sendClicked message that gives me grieve:
- (IBAction)btnSendClicked:(id)sender;
{
if ([txtMessage.text length] < 1)
return;
NSURL *url = [NSURL URLWithString:#"http://ec2-54-243-148-145.compute-1.amazonaws.com/TabletPractice/api/signal?clientIdentifier=2"];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:url];
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[requestMapping addAttributeMappingsFromDictionary:#{#"Text":#"signalText", #"HasMessage": #"isHasMessage"}];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping
objectClass:[SignalMessage class]
rootKeyPath:#""];
[manager addRequestDescriptor:requestDescriptor];
SignalMessage *newMessage = [[SignalMessage alloc] init];
newMessage.signalText = #"Test Message";
BOOL isMsg = TRUE;
NSNumber *boolAsNumber = [NSNumber numberWithBool:isMsg];
newMessage.isHasMessage = boolAsNumber;
[manager putObject:newMessage path:#"" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSLog(#"We object mapped the response with the following result: %#", result);
} failure:^(RKObjectRequestOperation * operation, NSError * error)
{
NSLog (#"Server WS call failure: operation: %# \n\nerror: %#", operation, error);
}];
[self hideControls];
}
At this point, I'm at a loss.
Please add a inverse mapping to your RKRequestDescriptor in your btnSendClicked method like below:
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor
requestDescriptorWithMapping:[requestMapping inverseMapping]
objectClass:[SignalMessage class]
rootKeyPath:#""];

Json Mapping using Restkit v0.20

i am able to send request correctly but after receiving response i get
response.body ={"status":"success","loginToken":"xxxxyyyzzz"}
but with an error saying
It Failed: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors
match the response loaded." UserInfo=0x2b55b0 {NSErrorFailingURLStringKey=http://192.168.1.71
/firstyii/index.php?r=site/login, NSLocalizedFailureReason=A 200 response was loaded from the
URL 'http://192.168.1.71/firstyii/index.php?r=site/login', which failed to match all (1)
response descriptors:
<RKResponseDescriptor: 0x2a5860 baseURL=http://192.168.1.71 pathPattern=/firstyii
/index.php?r=site/login statusCodes=200-299> failed to match: response path '/firstyii
/index.php?r=site/login' did not match the path pattern '/firstyii/index.php?r=site/login'.,
NSLocalizedDescription=No response descriptors match the response loaded., keyPath=null,
NSErrorFailingURLKey=http://192.168.1.71/firstyii/index.php?r=site/login,
NSUnderlyingError=0x2b6070 "No mappable object representations were found at the key paths
searched."}
LoginResponse.h
#interface LoginResponse : NSObject
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *loginToken;
+(RKObjectMapping*)defineLoginResponseMapping;
#end
LoginResponse.m
#import "LoginResponse.h"
#implementation LoginResponse
#synthesize loginToken;
#synthesize status;
+(RKObjectMapping*)defineLoginResponseMapping {
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[LoginResponse class]];
[mapping addAttributeMappingsFromDictionary:#{
#"status": #"status",
#"loginToken": #"loginToken",
}];
return mapping;
}
#end
Code in LoginManager.m
-(void)LoginWithUserName:(NSString *)username password:(NSString*)password {
LoginRequest *dataObject = [[LoginRequest alloc] init];
[dataObject setUsername:username];
[dataObject setPassword:password];
NSURL *baseURL = [NSURL URLWithString:#"http://192.168.1.71"];
AFHTTPClient * client = [AFHTTPClient clientWithBaseURL:baseURL];
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class]
forMIMEType:#"application/json"];
RKObjectMapping *requestMapping = [[LoginRequest defineLoginRequestMapping] inverseMapping];
[objectManager addRequestDescriptor: [RKRequestDescriptor
requestDescriptorWithMapping:requestMapping objectClass:[LoginRequest class] rootKeyPath:nil
]];
// what to print
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("Restkit/Network", RKLogLevelDebug);
RKObjectMapping *responseMapping = [LoginResponse defineLoginResponseMapping];
[objectManager addResponseDescriptor:[RKResponseDescriptor
responseDescriptorWithMapping:responseMapping pathPattern:#"/firstyii/index.php?r=site/login"
keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
[objectManager setRequestSerializationMIMEType: RKMIMETypeJSON];
[objectManager postObject:dataObject path:#"/firstyii/index.php?r=site/login"
parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"It Worked: %#", [mappingResult array]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"It Failed: %#", error);
}];
}
i think that the code used above is to map response with array ,
so i need code to map simple json like this one response.body={"status":"success","loginToken":"logingngngnnggngngae"}
Such responses are to be mapped using pathpattern , non keyvalue mapping mentioned at https://github.com/RestKit/RestKit/wiki/Object-mapping.
I was using pathPattern having "?" in it , hence it was not matching properly.
,also path pattern shouldnot have "/" at beginning.
the above code works fine if i change the pathPattern to pathPattern:#"site/login"
,and postObject to postObject:#"site/login"
and ofcourse changed the baseurl = #"http://json.xxxxxxx.xxxxx:8179/jsonresponse/index.php" so that there are not special charaters in it.

writing an API class for RestKit framework

I switched from AFnetworking to RestKit. In AFnetworking had an API class. The API.h class contained the following.
#import <UIKit/UIKit.h>
typedef void (^JSONResponseBlock)(NSDictionary* json);
#interface API : NSObject
//the authorized user
#property (strong, nonatomic) NSDictionary* user;
+(API*)sharedInstance;
//check whether there's an authorized user
//send an API command to the server
-(void)loginCommand:(NSMutableDictionary*)params onCompletion:(JSONResponseBlock)completionBlock;
And my API.m class looks like this.
+(API *)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^ {
sharedInstance = [[self alloc]initWithBaseURL:[NSURL URLWithString:kAPIHost]];
});
return sharedInstance;
}
#pragma mark - init
//intialize the API class with the destination host name
-(API *)init
{
//call super init
self = [super init];
if (self != nil){
//initialize the object
user = nil;
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
-(void)loginCommand:(NSMutableDictionary *)params onCompletion:(JSONResponseBlock)completionBlock{
NSLog(#"%#%#",kAPIHost,kAPILogin);
NSMutableURLRequest *apiRequest = [self multipartFormRequestWithMethod:#"POST" path:kAPILogin parameters:params constructingBodyWithBlock:^(id <AFMultipartFormData>formData){
//TODO: attach file if needed
}];
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
//success!
NSLog(#"SUCCESSSS!");
completionBlock(responseObject);
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
//Failure
NSLog(#"FAILUREE!");
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:#"error"]);
}];
[operation start];
}
Like you can see I only instantiate it once and put all my methods in over here. In my view controller I only need to call this method with a parameter-dictionary. Then I could read the whole JSON file.
Now with restKit I do this all on viewController level. I want to split it up like I did by AFNetworking. This is what I do in RestKit, At the moment is this all on viewController level.
//let AFNetworking manage the activity indicator
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
// Initialize HTTPClient
NSURL *baseURL = [NSURL URLWithString:#"http://virtuele-receptie.preview.sanmax.be"];
AFHTTPClient* client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
//we want to work with JSON-Data
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
// Initialize RestKit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
//Do mapping
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dataMapping
pathPattern:nil
keyPath:#"data"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
NSDictionary *dict = [[NSDictionary alloc]initWithObjectsAndKeys:_txtLogin.text,#"email",_txtPass.text,#"pwd", nil];
[objectManager getObject:nil path:#"/nl/webservice/company-user/login/apikey/key12345678" parameters:dict
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//Success
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
//Failure
}];
So far with RestKit I haven't seen a huge need for an API class like you might create with other HTTP frameworks. RestKit has its own HTTP client (actually, just AFNetworking's client), so there's no need to have a class for your HTTP client, and I've found that each time I use the RKObjectManager I generally want access to the method parameters and block callbacks within each view controller. In other words, I don't want to run RestKit networking code in an API class, because I would essentially have to wrap the entire call in a method that can be accessed in the view controller (success block, failure block, etc).
In essence, RestKit's design lightens the networking code so much that in my experience with it - 3 or 4 apps now - I've yet to see enough reason write an API class like you describe.

Resources