Im using AFNetworking to retrieve items from a basic rails server in a simple iOS project.
When I make the request in the simulator, everything works smoothly. However, when I make the same request while running the project on my device, I'm getting a frustrating error.
I understand that I cannot connect to localhost directly from my device and therefore need to use my ip address, which I am doing. This is the weird part: when I make a request to the server, I can see in my terminal that the server was hit and is returning a 200 response. However, the request is failing (on the client end) with the error message: 'The request timed out.'
Information and code:
My rails server is extremely basic. I've essentially generated a new project, set up a simple model called 'items' that has a single column -- a string -- for the item's content. I have the routes set up to only respond to JSON requests and the index method on the items_controller just returns the results of Item.all in json form.
Here are my routes:
TestingServer::Application.routes.draw do
scope :format => true, :constraints => { :format => 'json' } do
resources :items, :only => [:index]
end
end
and here is my items_controller.rb
class ItemsController < ApplicationController
def index
@items = Item.all
render :status => 200, :json => @items
end
end
As for the iOS project, here is my AFHTTPClient subclass header:
#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"
@interface PNAPIClient : AFHTTPClient
+ (PNAPIClient *)sharedClient;
@end
and here is its implementation:
#import "PNAPIClient.h"
#import "AFJSONRequestOperation.h"
static NSString * const kPNAPIClientBaseURLString = @"http://<ip address>:9292/";
@implementation PNAPIClient
+ (PNAPIClient *)sharedClient {
static PNAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[PNAPIClient alloc] initWithBaseURL:[NSURL URLWithString:kPNAPIClientBaseURLString]];
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:@"Accept" value:@"application/json"];
return self;
}
@end
And finally, here is the request that is failing:
- (IBAction)testRequest:(id)sender {
[[PNAPIClient sharedClient] getPath:@"/items.json" parameters:nil
success:^(AFHTTPRequestOperation *operation, id JSON) {
NSLog(@"success: %@", JSON);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"failure %@", error.localizedDescription);
}];
}
One last comment: I tried using a different URL (one that is online from another example) and it worked fine. This makes me suspect that their is either an issue with my Rails server, or that there is an issue with my device connecting to it locally. Like I said, I am able to do everything from the simulator just fine, and can see that I am hitting the server from my device.
Update 1
It appears that the failure block on the -getPath:parameters:success:failure: is being called no matter what the server response is. That is, if the server throws a 422 response with a json representation of the error, I am able to get the error message on my device. However, if the server returns a 200 response with some other json object, the failure block is still thrown... with no errors of course.
AFJSONRequestOperation will call the failure block:
In all of these cases, the
error
variable is set. In fact, it's the presence of anerror
that causes the failure block to be called (see[AFJSONRequestOperation -setCompletionBlockWithSuccess:failure:]
).If your log isn't outputting anything, try logging
error
instead oferror.localizedDescription
.Anyway, it sounds like your server is returning HTTP 200 with an invalid JSON object. You can set a breakpoint in the failure block and then type
po operation.responseString
in the debugger to inspect.