Restkit cannot read valid json? - ios

am just starting off with restkit for ios. I have a very simple json response
{
"result": [
{
"userid": "5964",
"name": "Your Name"
}
]
}
This is a valid json (tested against jsonlint)
However, when i try to use this with RKClient, it doe'snt see it as json. This is my code
-(void) request:(RKRequest *)request didLoadResponse:(RKResponse *)response{
if([request isGET]){
if([response isJSON]){
NSLog(#"JSON Response from Server %#", [response bodyAsString] );
}else{
NSLog(#"Non JSON Response from Server %#", [response bodyAsString] );
}
}
}
It always logs "Non JSON Reponse...." but the json is perfectly valid. The same thing happens when i use RKObjectMapping; the error will be "Unable to find parser for MIME Type text/xml.
Any help is highly appreciated.
Thanks

If for any reason you can't change the server headers and returned Mime type to xml or json, you can still add mapping for mime type 'text/html' to be processed as json.
Just add :
[[RKParserRegistry sharedRegistry] setParserClass:[RKJSONParserJSONKit class] forMIMEType:#"text/html"];
And don't forget to add the related header file :
#import <RestKit/RKJSONParserJSONKit.h>
Works for me.
Have a look at https://github.com/RestKit/RestKit/wiki/Using-a-Custom-MIME-Type

Serveur response header should be "application/json", and not "text/xml". ;p

I was a bit dumb, but things got solved once i added
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json');
to the server script.

Related

Accessing Online Slim API from iphone & IOS application

I have simple API developed in Slim, which is stored in Online Server. It works fine when I check it from browser, but when I retrieve it from Iphone app, it show me the following error:
PeopleAlsoAsk[12607:2219389] Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in (200-299), got 500" UserInfo={NSLocalizedRecoverySuggestion="this is index pagpe, Specific questions are retrieved successfully", NSErrorFailingURLKey=http://upvc.pk/test2/public/, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest: 0x608000001060> { URL: http://upvc.pk/test2/public/ }, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x60800003d6c0> { URL: http://upvc.pk/test2/public/ } { status code: 500, headers {
"Accept-Ranges" = bytes;
Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 81;
Date = "Wed, 26 Apr 2017 12:34:31 GMT";
Server = LiteSpeed;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/7.0.17";
} }, NSLocalizedDescription=Expected status code in (200-299), got 500}
2017-04-26 17:34:31.462 PeopleAlsoAsk[12607:2219389] Error function called
I tried a lot, but all in vain. If any one faced this problem, or know about this, then please guide me in this. Thanks in advance.
My code is given below
public/index.php
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../vendor/autoload.php';
$app = new \Slim\App;
//Questions Routes
require '../src/routes/questions.php';
$app->run();
routes/questions.php
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
$app = new \Slim\App;
//Get All Questions
$app->get('/api/questions', function(Request $request, Response $response){
$questions = "All questions are retrieved successfully";
echo json_encode($questions);
});
// Get specific Questions
$app->get('/api/questions/{app_id}', function(Request $request, Response $response){
$questions = "Specific questions are retrieved successfully";
echo json_encode($questions);
});
You should put your iOS code here.
From your Slim, I would place:
$app->status($status_code);
In your response code, you can add this function to give all client response:
function echoResponse($status_code, $response) {
$app = \Slim\Slim::getInstance();
// Http response code
$app->status($status_code);
// setting response content type to json
$app->contentType('application/json');
echo json_encode($response);
}
Usage very simple:
echoResponse(200, "your response");
From iOS point of view, hard to say something without the code.

AWS API Gateway exported IOS SDK no error response

I am using the exported API Gateway IOS SDK. The API was originally imported to the API Gateway via Swagger as a template, but since modified with the AWS portal to map back to the response model and class within the IOS SDK. All 200 requests are returned with no issue. I am using pod 'AWSAPIGateway', '= 2.4.1'
The issue i am having is nothing else other then 200 response is handled and/or received by the IOS SDK. I know it is sent because Cloudwatch shows the correct mapping, but nothing is returned in the IOS App.
At first i thought maybe i was conflicting with the API Gateway's own response codes so i switched to 403 which wasn't on it list. But that didn't work. I am also not getting 500 response errors.
Here is the calling function:
client.apiRequestPut(apiRequestVar).continueWithSuccessBlock {
(task: AWSTask!) -> AnyObject! in
print(task)
if ((task.error) != nil) {
print(task.error)
return nil;
}
if( (task.result != nil)) {
print(task.result)
}
}
return nil
}
And the AWS SDK Generated Client Function:
- (AWSTask *)apiRequestPut:(APINewRequest *)body {
NSDictionary *headerParameters = #{
#"Content-Type": #"application/json",
#"Accept": #"application/json",
};
NSDictionary *queryParameters = #{
};
NSDictionary *pathParameters = #{
};
return [self invokeHTTPRequest:#"PUT"
URLString:#"/requestVariable"
pathParameters:pathParameters
queryParameters:queryParameters
headerParameters:headerParameters
body:body
responseClass:[APIModel class]];
}
}
Here is the Error Model:
#implementation APIErrorModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"success": #"success",
#"theFunction": #"theFunction",
#"action": #"action",
#"timeStamp": #"timeStamp",
#"error": #"error",
#"data": #"data"
};
}
+ (NSValueTransformer *)timeStampJSONTransformer {
return [AWSMTLValueTransformer reversibleTransformerWithForwardBlock:^id(NSString *dateString) {
return [NSDate aws_dateFromString:dateString format:AWSDateISO8601DateFormat1];
} reverseBlock:^id(NSDate *date) {
return [date aws_stringValue:AWSDateISO8601DateFormat1];
}];
}
Here is the mapping in the API Gateway for 400 response code:
#set($inputRoot = $input.path('$'))
{
"success" : $input.json('$.success'),
"theFunction" : "foo",
"action" : "foo",
"timeStamp" : "foo",
"error" : "foo",
"data" : $input.json('$.message')
}
Here is the resulting Cloudwatch log entry:
Endpoint response body before transformations:
{
"success": false,
"message": "{\"message\":\"The conditional request failed\",\"code\":\"ConditionalCheckFailedException\",\"time\":\"2016-05- 12T20:16:03.165Z\",\"statusCode\":400,\"retryable\":false,\"retryDelay\":0} "
}
Endpoint response headers: {content-length=217, Connection=keep-alive, content-type=application/json; charset=utf-8, cache-control=no-cache, Date=Thu, 12 May 2016 20:16:03 GMT
}
Method response body after transformations:
{
"success": false,
"theFunction": "foo",
"action": "foo",
"timeStamp": "foo",
"error": "foo",
"data": "{\"message\":\"The conditional request failed\",\"code\":\"ConditionalCheckFailedException\",\"time\":\"2016-05- 12T20:16:03.165Z\",\"statusCode\":400,\"retryable\":false,\"retryDelay\":0} "
}
Method response headers: {Content-Type=application/json}
Successfully completed execution
Method completed with status: 400
But then i get nothing received back in the iOS app?
Any help to what i am missing would be much appreciated!
- continueWithSuccessBlock: is used when you want to extract only the result from the task object. The task object in - continueWithSuccessBlock: always has the nil error property. If you want to receive errors, you need to use - continueWithBlock: instead.
See the Bolts documentation for more details.

Convenient way to print raw HTTP request in Swift or Objective-C?

Is there a convenient way to view or print a NSMutableURLRequest as a raw HTTP request -- i.e. how it gets sent over the wire? Perhaps when the request is actually made? I'm specifically not trying to extract specific components of the request and manually compose them together. FWIW I'm loading it in a webview with WKWebView.loadRequest(r).
By raw request, I mean as it is sent over the wire -- something like:
POST /target/command HTTP/1.0
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12D508
Content-Type: application/json
Content-Length: 38914
Custom-Header: custom-value
{"param": "value"}
The reason I'm asking is because my server seems to receive an empty request body for a particular request, while it receives request bodies on others. Would be very useful if I could debug this like I debug on my server -- by printing the raw request. I understand I could use proxy tools like Charles, but I'm hoping there's a more convenient answer.
I generally just print out all outgoing requests as a copy/pasteable cURL command with a category like this, you can probably reformat it if you don't need the functionality.
#implementation NSURLRequest (cURLRepresentation)
- (NSString *)escapeQuotesInString:(NSString *)string {
NSParameterAssert(string.length);
return [string stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
}
- (NSString *)cURLRepresentation {
NSMutableString *curlString = [NSMutableString stringWithFormat:#"-------\ncurl -k -X %# --dump-header -", self.HTTPMethod];
for (NSString *key in self.allHTTPHeaderFields.allKeys) {
NSString *headerKey = [self escapeQuotesInString: key];
NSString *headerValue = [self escapeQuotesInString: self.allHTTPHeaderFields[key] ];
[curlString appendFormat:#" -H \"%#: %#\"", headerKey, headerValue];
}
NSString *bodyDataString = [[NSString alloc] initWithData:self.HTTPBody encoding:NSUTF8StringEncoding];
if (bodyDataString.length) {
bodyDataString = [self escapeQuotesInString: bodyDataString];
[curlString appendFormat:#" -d \"%#\"", bodyDataString];
}
[curlString appendFormat:#" \"%#\"", self.URL.absoluteString];
[curlString appendFormat:#"\n-------"];
return curlString;
}
#end
Used RequestBin (requestb.in) instead to make sure I wasn't going crazy... Turns out there's a bug in WKWebView.loadRequest(r) where r.HTTPBody will not get passed in the request body even when non-nil. Sigh.

RestKit - Expected Content Type application/json

I am experiencing the weirdest error while using RestKit. Here's the localized description of the error -
Expected content type {(
"application/json",
"text/html",
"text/plain",
"application/x-www-form-urlencoded"
)}, got {application/json}
As you can see, I am expecting "application/json" as a content type, and the server is returning application/json. I have no idea why I am getting this error!
The detailed error is here --
2015-02-19 11:24:43.234 MyProject[62944:10754151] E restkit.network:RKObjectRequestOperation.m:213 POST 'https://ipaddress/api/v1.0/user/verify' (200 OK / 0 objects) [request=0.1408s mapping=0.0000s total=0.1428s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1016 "Expected content type {(
"application/json",
"text/html",
"text/plain",
"application/x-www-form-urlencoded"
)}, got {application/json" UserInfo=0x7f9c49db4930 {NSLocalizedRecoverySuggestion={"user_id":"3749f17f647c4ee1a9ee9d75c5f49f7e","domain":"domain","device_id":"2751029764","access_token":"589134b447974320b2d494a491e8226a","access_token_expiration":2181237828314,"refresh_token":"cb06c038be7b4f7c930de1fcb3f2017a"}, NSErrorFailingURLKey=https://ipaddress/api/v1.0/user/verify, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest: 0x7f9c49def0c0> { URL: https://ipaddress/api/v1.0/user/verify }, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x7f9c49f567a0> { URL: https://ipaddress/api/v1.0/user/verify } { status code: 200, headers {
"Baseline-Request-Id" = 30f91b50;
Connection = "keep-alive";
"Content-Length" = 230;
"Content-Type" = "{application/json, q=1000}";
Date = "Thu, 19 Feb 2015 19:23:48 GMT";
Server = "nginx/1.6.2";
} }, NSLocalizedDescription=Expected content type {(
"application/json",
"text/html",
"text/plain",
"application/x-www-form-urlencoded"
)}, got {application/json}
Update --
After some debugging, I found out that the problem lies in the value of "Content-Type" I am getting in the response. As you can see in the example above, the value is "{application/json, q=1000}" instead of "application/json". RestKit, for some reason, is reading it as "{application/json" (omitting the stuff after the comma, and notice the starting brace) and hence not finding a match with the expected types.
To "solve" it in a dirty way, I added the following line (notice the "{" before "application/json")
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"{application/json"];
But we all know its not the right way. I don't know what is wrong here, is the server sending something incorrect? Or is RestKit supposed to read it differently?
I couldn't find the reason why the server was returning a "Wrong" content type. The developers of the server denied that it was doing something wrong. So I ended up using my dirty hack and added a "{" before "application/json".
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"{application/json"];

Automatic decompresion of gzipped json response data not working

From all I have read gzipped response data should be automatically decompressed by the NS URL loading system. I am using AFNetworking and a AFJSONResponseSerializer to handle requests to a custom backend. I have experimented with adding "Accept-Encoding" = gzip to my request headers, but it has had no effect. My response headers include:
"Content-Encoding" = gzip;
"Content-Length" = 179837;
"Content-Type" = "application/json";
I am making the request like this:
[[AFHTTPRequestOperationManager sharedClient]
GET:#"some/endpoint"
parameters:#{#"foo":#"bar"}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response data size %u", operation.responseData.length);
NSURL *outputURL = [[APP_DELEGATE applicationDocumentsDirectory] URLByAppendingPathComponent:#"data.gzip"];
[operation.responseData writeToURL:outputURL atomically:NO];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"ERROR [%d] [%#]", error.code, error.localizedDescription );
}];
When this runs the size of operation.responseData.length printed is what I expect. However, responseObject is nil when it should be an NSArray. I added the lines writing operation.responseData to test that the data received is what I expect. I can browse to data.gzip, uncompress it, and see my JSON.
It appears to me that the gzipped data received is not being automatically decompressed before my AFJSONResponseSerializer tries to handle it. Most of what I have found on Stackoverflow has either discussed how the decompression is automatic or suggested adding an "Accept-Encoding" = gzip header. Any suggestions would be appreciated.
Have you checked the output from the "custom backend" — use something like Charles Proxy which will let you see things like your response headers and raw content.
e.g. typical headers as shown in Charles:
HTTP/1.1 200 OK
Date Wed, 26 Nov 2014 22:25:33 GMT
Server Apache/2.2.24 (Unix) mod_hive/4.0 mod_ssl/2.2.24 OpenSSL/1.0.0-fips mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 mod_fcgid/2.3.6
X-Powered-By PHP/5.3.29
P3P CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
Cache-Control no-cache
Pragma no-cache
Set-Cookie 4d3750fca133ae462ca26ca5a1cc0104=c7da991b30f53342846fff5ddaf3ea08; path=/
Cache-Control max-age=3600
Expires Wed, 26 Nov 2014 23:25:33 GMT
Host-Header 192fc2e7e50945beb8231a492d6a8024
Keep-Alive timeout=5, max=100
Connection Keep-Alive
Transfer-Encoding chunked
Content-Type text/html; charset=utf-8

Resources