Issue with retrieving JSON with AFNetworking - json

I am trying to retrieve some JSON data with AFNetworking.
My server component (PHP/ZendFramework) delivers the following response:
>curl -i http://test.local:8080/public/appapi/testaction/format/json
HTTP/1.1 200 OK
Date: Mon, 06 Feb 2012 10:37:20 GMT
Server: Apache
X-Powered-By: PHP/5.3.8
Content-Length: 35
Content-Type: application/json
{"entries":"test","abcxxx":"test2"}
I try to use the following code to retrieve this json data:
NSString *baseurl = #"http://test.local:8080";
NSString *path = #"/public/appapi/testaction/format/json";
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseurl]];
[client registerHTTPOperationClass:[AFJSONRequestOperation class]];
[client setAuthorizationHeaderWithUsername:#"myusername" password:#"mypassword"];
[client getPath:path parameters:nil success:^(__unused AFHTTPRequestOperation *operation, id JSON) {
//NSLog(#"sjson: %#", [JSON valueForKeyPath:#"entries"]);
NSLog(#"sjson: %#", JSON);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error Code: %i - %#",[error code], [error localizedDescription]);
}];
When running in the simulator the console shows the following output:
2012-02-06 11:22:39.800 App[3642:15803] sjson: <6a736f6e 466c6963 6b724170 69287b22 6d657468 6f64223a 7b225f63 6f6e7465 6e74223a 22666c69 636b722e 74657374 2e656368 6f227d2c 2022666f 726d6174 223a7b22 5f636f6e 74656e74 223a226a 736f6e22 7d2c2022 6170695f 6b657922 3a7b225f 636f6e74 656e7422 3a223336 32306666 32343265 37323336 34633135 32373031 31353438 62393335 3066227d 2c202273 74617422 3a226f6b 227d29>
Any hint on what I am doing wrong would be greatly appreciated :) I tried accessing the Twitter API and that works perfectly fine...

This is confusing, and something I've been trying to figure out how to properly document / design better, but you need to set your Accept header for the request to application/json.
Registering AFJSONRequestOperation says "any request that comes through, I'll look at and handle if it is a JSON request". The way you hint that a request is JSON is to either set the appropriate Accept header, or add .json as the extension. Right now, the request is going through the list of operation classes, but it only catches on AFHTTPRequestOperation, which has a response object type of NSData (what you're seeing in the log).
Sorry about the confusion!

Here are the 3 lines I had to add to get my app to send and receive JSON:
httpClient.parameterEncoding = AFJSONParameterEncoding;
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];

if you're using a custom accept header, like i am, AFNetworking won't recognize that you're requesting json, like mattt explained. one way around this is to simply convert the data into a json object in your completion block:
^(AFHTTPRequestOperation *operation, id JSON)
{
id payload = [NSJSONSerialization JSONObjectWithData:JSON options:NSJSONWritingPrettyPrinted error:nil];
NSLog(#"Fetched: %#", payload);
}

I had the same issue but after I added the Accept header something like in #Tylerc230 answer, the request start to fail because received format "text/html" wasn't in expected content type, so I added #"text/html" in acceptableContentTypes function of AFJSONRequestOperation.m .

Related

iOS NSURLSession Get Request JSON always showing outdated data

I have the following doubts about the JSON data returned from using both "GET" versus "POST" request. In the following URL JSON DATA, the data is not always updated based on the server changes (eg: database). For example, if I delete all the suggestion records from the database when I had 3 previously, it still returns 3 suggestion records in my JSON response body when I call dataTaskWithRequest.
However, if I change to POST, then the JSON response body will always be updated with the actual records from the server. In my server code (Using CakePHP), I did not check for post or get data. Actually, it was intended to be a GET method, but for some reason, only POST method seems to always fetch the most up to date data from JSON as opposed to GET.
Below is my code from my iOS client, but I'm not too sure if its very useful. I was wondering if there is a cache issue for GET request as opposed to POST request? However, I tried disabling cache for NSURLSessionConfig but it had no impact.
config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
The code base is below:
NSString *requestString = [NSString stringWithFormat:#"%#/v/%#.json", hostName, apptIDHash];
NSURL *url = [NSURL URLWithString:requestString];
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
if (!error) {
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
NSError *jsonError;
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
[self printJSONOutputFromDictionary:jsonObject];
if (!jsonError) {
block(jsonObject, nil);
}
else{
block(nil, jsonError);
}
}
else{
NSError *statusError = [self createServerUnavailableNSError:httpResp];
block(nil, statusError);
}
}
else{
block(nil, error);
}
}];
[dataTask resume];
In the above code fragment, the JSON body is always showing outdated data.
I really want to understand the issue, and would really appreciate if anyone could explain this issue for me.
Try adding the following request header:
[req addRequestHeader:#"Cache-Control" value:#"no-cache"];
I encountered the same problem as you and adding the above code solved the problem for me.
Taken from ASIHTTPRequest seems to cache JSON data always

RestKit with my JSON data is not working

I'm trying to understand the restkit 0.22. I got some tutorials from different blogs and youtubes. I ended up with mixed up code.
Could anyone please help me with this, I really need it to work for my project.
I created Core Data Model with entities Songs.xcdatamodeld
I have a json that comes from my mySQL db:
[{"SongID":"1","SongTitle":"Song1","PerformerName":"Performer1","SongURL":"http://mysite/mysongs/1.mp3","PerformerPic":"PerfPic1.png"},
{"SongID":"2","SongTitle":"Song2","PerformerName":"Performer2","SongURL":"http://mysite/mysongs/2.mp3","PerformerPic":"PerfPic2.png"},
{"SongID":"3","SongTitle":"Song3","PerformerName":"Performer3","SongURL":"http://mysite/mysongs/3.mp3","PerformerPic":"PerfPic3.png"}]
in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:http://mysite]];
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Songs" ofType:#"momd"]];
//Initialize managed object store
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL ] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
[RKObjectManager sharedManager].requestSerializationMIMEType = RKMIMETypeJSON;
[RKMIMETypeSerialization registeredMIMETypes];
[objectManager setAcceptHeaderWithMIMEType:#"application/json"];
RKEntityMapping* mapping = [RKEntityMapping mappingForEntityForName:#"Songs"
inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore];
mapping.identificationAttributes = #[#"songID"];
[mapping addAttributeMappingsFromDictionary:#{#"id" : #"SongID",
#"songTitle" : #"SongTitle",
#"performerName" : #"PerformerName",
#"songURL" : #"SongURL",
#"performerPic" : #"PerformerPic"}];
return YES;
}
in TableView Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
[RKObjectManager.sharedManager getObjectsAtPath:#"/api.php"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
self.Songs = [mappingResult array];
NSLog(#"It Worked: %#", self.Songs);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"It Failed: %#", error);
}];
}
This is the error I'm getting:
GET 'http://mysite/api.php' (200 OK / 0 objects) [request=3.5566s mapping=0.0000s
total=3.5627s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001
"No response descriptors match the response loaded." UserInfo=0xb5920b0
{NSErrorFailingURLStringKey=http://mysite/api.php, NSLocalizedFailureReason=A 200
response was loaded from the URL 'http://mysite/api.php', which failed to match all (0)
response descriptors:, NSLocalizedDescription=No response descriptors match the response
loaded., keyPath=null, NSErrorFailingURLKey=http://mysite/api.php,
NSUnderlyingError=0xb5921b0 "No mappable object representations were found at the key
paths searched."}
First, you don't seem to be creating the managed object contexts as part of your setup code. This will likely cause you issues after you fix your 'main' issue:
Your main issue is quite clearly described in the error message:
which failed to match all (0) response descriptors
I.e. You haven't created any response descriptors.
The Object-mapping guide walks you through the mapping and descriptor creation process (and includes lots more details to boot).
Start with something like:
RKResponseDescriptor *rd = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
then add the mapping to the object manager.
(ensure you're calling createManagedObjectContexts somewhere).

I'm making an IOS application, how do I make the application send me the UDID of the device its on?

Im currently developing and IOS application, For security purposes I would like to know how I can make the app send the device "UDID" to a server.
So basically I need to know how to make the device "fetch" the udid and then take the udid and send it to a server as a "request".
If the UDID is "registered" in the MYSQL database, then the server will send back a confirmation.
Besides finding out how to get the udid, I may need additional help setting up the mysql database :$
Thanks!
You can get the UUID of an iOS device using: CFUUIDRef udid = CFUUIDCreate(NULL);
NSString *udidString = (NSString *) CFUUIDCreateString(NULL, udid);
(Apple dont like you to use the UDID).
As far as posting it to a server, I suggest using a JSON post method, and recording the success. A good JSON library is SBJson which can be found here. Youll need to create a HTTP post, get the response data, and use SBJson library to parse the response.
EDIT: OR instead of SBJson, as Carbonic acid kindly pointed out, you can use NSJSONSerialization. Also, as pointed out by Naz Mir, new UUID method used.
Edit:
[[[UIDevice currentDevice] identifierForVendor] UUIDString] is not deprecated as I stated. Please go through the links below for information.
Getting UDID as stated above NSString *uuididentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; is deprecated and Apple no longer allows it. If your aim is to uniquely identify a device you can use SecureUDID or OpenUDID
I have used OpenUDID sometime back in one of our apps and using it is as simple as -
#import "OpenUDID.h"
[OpenUDID setOptOut:NO];
self.openUDID = [OpenUDID value];
Once you have the required value sending it to the server is trivial. You can use iOS networking library like AFNetworking to send and receive data. For example,
#import "AFHTTPRequestOperation.h"
NSURL *url = [NSURL URLWithString:#"Your sever URL"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *postString = [NSString stringWithFormat:#"&UDID=%#", self.
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPMethod:#"POST"];
AFHTTPRequestOperation *httpOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *httpOperation, id responseObject) {
//handle server response here
NSLog(#"%#", [httpOperation responseString]); //this contains the servers response
}failure:^(AFHTTPRequestOperation *httpOperation, NSError *error) {
//handle server errors here
NSLog(#"error: %#", [httpOperation error]);
}];

Whats wrong with simple html NSString?

Hey guys I have a simple html NSString, it works fine on the browser but does not load on the iPhone. I am using a simple: [self.w loadHTMLString:errorString baseURL:nil]. What am I missing?
NSString* errorString = [NSString stringWithFormat:
#"<html><body> <iframe src=\"http://64.183.101.142:81/pda.htm\"width=\"500\" height=\"500\"></iframe></body></html>"];
Yo need to first load your request or HTML on webview and Capture this request using a UIWebViewDelegate method with following protocol
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//capture request => request
//From here you have the NSURLRequest object
//You can extract the headers of the request to NSDictionary which will contain //the
//authentication cookie details, token, etc. using the following method of NSURLRequest
//- (NSDictionary *)allHTTPHeaderFields
}
Source Source link
If it requires authentication you will need to modify your string depending on the type of request.
if it's a get it will have a "?" at the and the parameters something like this:
http://xx.xx.xx.xx:81/pda.htm?user="userX"&passwd="something" if it's a post method you will pass your data within the body of your request. [NSURLRequest setHTTPBody:];
I hope this helps.

AFNetworking + JSON + progress download

I'm using AFNetworking and like it very much.
I need to get JSON data from my server and it's OK, it works perfectly.
I added the setDownloadProgressBlock but I think it can't work with JSON download: maybe it's not possible to get the estimated amount of bytes to download.
My code:
NSMutableURLRequest *request = [[VinocelaHTTPClient sharedClient] requestWithMethod:#"GET" path:#"ws/webapp/services/pull" parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
{
}];
[operation setDownloadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
NSLog(#"Get %d of %d bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation start];
And my result :
Get 27129 of -1 bytes
Get 127481 of -1 bytes
Get 176699 of -1 bytes
So, I think AFNetworking can't estimate the real size to download when downloading JSON data contrary to a zip file or an image ?
From perusing the source, it seems that the progress callback is just passed the expectedContentLength property of the cached, internal NSHTTPURLResponse object. So, if for some reason your server isn't correctly sending the Content-Length header, and/or is doing chunked transfer encoding, that value is unknown, and the value NSURLResponseUnknownLength is returned (which happens to be defined as -1).
Try inspecting the headers returned by an HTTP request outside the context of your app. If you get a Content-Length header with a sane value, the problem likely lies in AFNetworking itself. If it is absent, the problem lies with the server. I've never seen an HTTP server send a JSON response using chunked transfer encoding (most of the time the content size should be relatively small and known at the time the headers are sent), but it's within spec for it to do so.