AFHTTPSessionManager - get unserialized/raw response body (NSData?) - ios

I've subclassed AFHTTPSessionManager according to the recommended best practice for iOS 8 (in place of AFHTTPOperationManager, which I was using before).
I can grab the NSHTTPURLResponse from the task (except that has no body, only headers), and the callback returns the serialized responseObject which is fine.
Sometimes I need to log the response as a string or display it in a text field - there doesn't appear to be a way to do this natively using SessionManager? OperationManager allowed you to reference the raw response as an NSString:
operation.responseString;
I suppose I could stringify the serialized requestObject, but that seems like a lot of unnecessary overhead, and won't help if the response object is invalid JSON.
Here's my subclassed singleton:
#implementation MyAFHTTPSessionManager
+ (instancetype)sharedManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
And then to make a simple GET (which I've added to a block method), I can do:
[[MyAFHTTPSessionManager sharedManager] GET:_url parameters:queryParams success:^(NSURLSessionDataTask *task, id responseObject) {
completion(YES, task, responseObject, nil);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
completion(NO, task, nil, error);
}];

You can accomplish this by creating a custom response serializer that records the data and serializes the response using the standard response serializer, combining both the raw data and parsed object into a custom, compound response object.
#interface ResponseWithRawData : NSObject
#property (nonatomic, retain) NSData *data;
#property (nonatomic, retain) id object;
#end
#interface ResponseSerializerWithRawData : NSObject <AFURLResponseSerialization>
- (instancetype)initWithForwardingSerializer:(id<AFURLResponseSerialization>)forwardingSerializer;
#end
...
#implementation ResponseWithRawData
#end
#interface ResponseSerializerWithRawData ()
#property (nonatomic, retain) forwardingSerializer;
#end
#implementation ResponseSerializerWithRawData
- (instancetype)initWithForwardingSerializer:(id<AFURLResponseSerialization>)forwardingSerializer {
self = [super init];
if (self) {
self.forwardingSerializer = forwardingSerializer;
}
return self;
}
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error {
id object = [self.forwardingSerializer responseObjectForResponse:response data:data error:error];
// TODO: could just log the data here and then return object; so that none of the request handlers have to change
if (*error) {
// TODO: Create a new NSError object and add the data to the "userInfo"
// TODO: OR ignore the error and return the response object with the raw data only
return nil;
} else {
ResponseWithRawData *response = [[ResponseWithRawData alloc] init];
response.data = data;
response.object = object;
return response;
}
}
#end
Then set this serializer on your session manager:
#implementation MyAFHTTPSessionManager
+ (instancetype)sharedManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
instance.responseSerializer = [[ResponseSerializerWithRawData alloc] initWithForwardingSerializer:instance.responseSerializer];
});
return instance;
}
Now in your completion handler you will get an instance of ResponseWithRawData:
[[MyAFHTTPSessionManager sharedManager] GET:_url parameters:queryParams success:^(NSURLSessionDataTask *task, id responseObject) {
ResponseWithRawData *responseWithRawData = responseObject;
NSLog(#"raw data: %#", responseWithRawData.data);
// If UTF8 NSLog(#"raw data: %#", [[NSString alloc] initWithData:responseWithRawData.data encoding:NSUTF8StringEncoding]);
// TODO: do something with parsed object
} failure:^(NSURLSessionDataTask *task, NSError *error) {
}];
I just whipped this up without compiling/testing, so I will leave it to you to debug and fill in the gaps.

You can access the “data” object directly from AFNetworking by using the “AFNetworkingOperationFailingURLResponseDataErrorKey” key so there is no need for subclassing the AFJSONResponseSerializer. You can the serialize the data into a readable dictionary. Here is some sample code to get JSON Data :
NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
NSDictionary *serializedData = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];
Here is code to Get Status code in Failur block
NSHTTPURLResponse* r = (NSHTTPURLResponse*)task.response;
NSLog( #"success: %d", r.statusCode );

Related

Objective C - How to return local variable in this code?

I have the following code that works. It successfully displays myName in the NSLog ...
NSURL *apiString = [NSURL URLWithString:#"http://testapi.com/url"];
[XMLConverter convertXMLURL:apiString completion:^(BOOL success, NSDictionary *dictionary, NSError *error)
{
if (success)
{
NSString *myName = dictionary[#"profile"][#"real_name"];
NSLog(#"%# is my name", myName);
}
}];
I have the following code for the method convertXMLURL which is in XMLConverter.m which I imported. It does a nice job of converting my XML to NSDictionary. That is what I want ...
+ (void)convertXMLURL:(NSURL *)url completion:(OutputBlock)completion
{
///Wrapper for -initWithContentsOfURL: method of NSXMLParser
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[[XMLConverter new] parser:parser completion:completion];
});
}
The problem I have is *dictionary is a local variable. I need to use it elsewhere in the code. How can I return it?
Simply create a #property and assign dictionary to it inside your completion handler.
For example:
Class Foo
#interface Foo : NSObject
#property (strong, nonatomic) NSDictionary* dictionary;
#end
Completion handler
NSURL *apiString = [NSURL URLWithString:#"http://testapi.com/url"];
[XMLConverter convertXMLURL:apiString completion:^(BOOL success, NSDictionary *dictionary, NSError *error)
{
if (success)
{
NSString *myName = dictionary[#"profile"][#"real_name"];
NSLog(#"%# is my name", myName);
myInstanceOfFoo.dictionary = dictionary;
}
}];
EDIT: If the completion handler is not inside a member of the Foo class the dictionary property must be declared in the header file (.h). Otherwise you can declare it in the implementation file (.m).
Based on the comment by carlodurso

AFHTTPSessionManager get error response from server [duplicate]

I've been using AFNetworking 2.0 in my app.
I've noticed that if my web-service returns a 500 status code I do not get the body of the response.
Here is an example of my php code
try
{
$conn = new PDO( "sqlsrv:server=$serverName;Database = $database", $uid, $pwd);
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
return $conn;
}
catch( PDOException $e )
{
$response->status(500);
echo( "Connection Error: " . $e->getMessage() );
}
If I use a simple rest client this is an example of a response body.
Connection Error: SQLSTATE[08001]: [Microsoft][SQL Server Native Client 11.0]SQL Server Network Interfaces: Error Locating Server/Instance Specified [xFFFFFFFF].
However this seems to be the only response I can get from AFNetworking
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x15e58fa0 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
This is the part of my objective-c code that does this.
...} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#",error.description);
}];
Is there a way I can get the response body?
Edit: More code for clarification
Below is part of my subclass of AFHTTPSessionManager
#implementation MSMAMobileAPIClient
+ (MSMAMobileAPIClient *)sharedClient {
static MSMAMobileAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[MSMAMobileAPIClient alloc] initWithDefaultURL];
});
return _sharedClient;
}
- (id)initWithDefaultURL {
return [self initWithBaseURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://%#/mamobile/index.php/" ,[[NSUserDefaults standardUserDefaults] stringForKey:#"serviceIPAddress"]]]];
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
self.responseSerializer = [AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:#[[AFJSONResponseSerializer serializer], [AFHTTPResponseSerializer serializer]]];
return self;
}
I tried setting the response serializer to a AFCompoundResponseSerializer but it didn't seem to make a difference
Below is an example of a subclass that I call the Librarian.
-(void)searchForItemWithString:(NSString *)searchString withCompletionBlock:(arrayBlock)block {
self.inventorySearchBlock = block;
NSDictionary *parameters = #{#"query": searchString};
[[MSMAMobileAPIClient sharedClient] GET:#"inventory/search" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
if (!responseObject) {
NSLog(#"Error parsing JSON");
} else {
//do stuff with the json dictionary that's returned..
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#",error.description);
}];
}
UPDATE: I have created a github repository to contain the latest code I am using. All changes will be posted there. https://github.com/Hackmodford/HMFJSONResponseSerializerWithData
The answer comes from this issue on github.
https://github.com/AFNetworking/AFNetworking/issues/1397
gfiumara is the dev who came up with this. I have only slightly modified his subclass of AFJSONResponseSerializer to include an actual string instead of the NSData
//MSJSONResponseSerializerWithData.h
#import "AFURLResponseSerialization.h"
/// NSError userInfo key that will contain response data
static NSString * const JSONResponseSerializerWithDataKey = #"JSONResponseSerializerWithDataKey";
#interface MSJSONResponseSerializerWithData : AFJSONResponseSerializer
#end
// MSJSONResponseSerializerWithData.m
#import "MSJSONResponseSerializerWithData.h"
#implementation MSJSONResponseSerializerWithData
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (*error != nil) {
NSMutableDictionary *userInfo = [(*error).userInfo mutableCopy];
userInfo[JSONResponseSerializerWithDataKey] = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *newError = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:userInfo];
(*error) = newError;
}
return (nil);
}
return ([super responseObjectForResponse:response data:data error:error]);
}
#end
Here is an example of how I use it in the failure block.
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#",[error.userInfo objectForKey:#"JSONResponseSerializerWithDataKey"]);
}];
You need to use AFCompoundSerializer to tell the AFNetworking framework how to process all of the possible responses it could receive. By default it will only try to map JSON. A compound serializer will work through the serializers until it finds one that doesn't raise an error.
You want to use:
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers
on AFCompoundResponseSerializer (in AFURLResponseSerialization.h).
You need to pass an array of serializers that can handle the response. One of the serializers in the array should be an instance of AFHTTPResponseSerializer to handle your error responses.
If you include my category in your project, it's as simple as the following:
[mySessionManager POST:#"some-api" parameters:params success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
...
} failure:^(NSURLSessionDataTask *task, NSError *error) {
id responseObject = error.userInfo[kErrorResponseObjectKey];
... do something with the response ...
}];
Here's the code for my category. It swizzles AFURLSessionManager to inject a shim into the completion handler. The shim puts the response into the NSError's userInfo.
https://gist.github.com/chrishulbert/35ecbec4b37d36b0d608

AFHTTPSessionManager with multiple requests in rapid succession (AFNetworking 2.0)

i am new to iOS programming, still learning.
EDIT: !!!!!! Everything in my code works. My question is about the delegation pattern i use,
if i am generating problems in the background that i have no idea of, or if there is a better way to handle my situation in AFNetworking...
I have created an API for my app by subclassing AFHTTPSessionManager.
My API creates a singleton and returns it and supplies public functions for various requests. And those functions create parameter lists, and make GET requests on the server like this:
- (void)getCharacterListForKeyID:(NSString *)keyID vCode:(NSString *)vCode sender:(id)delegate
{
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[#"keyID"] = keyID;
parameters[#"vCode"] = vCode;
[self GET:#"account/Characters.xml.aspx" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
self.xmlWholeData = [NSMutableDictionary dictionary];
self.errorDictionary = [NSMutableDictionary dictionary];
NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
[XMLParser setShouldProcessNamespaces:YES];
XMLParser.delegate = self;
[XMLParser parse];
if ([delegate respondsToSelector:#selector(EVEAPIHTTPClient:didHTTPRequestWithResult:)]) {
[delegate EVEAPIHTTPClient:self didHTTPRequestWithResult:self.xmlWholeData];
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([delegate respondsToSelector:#selector(EVEAPIHTTPClient:didFailWithError:)]) {
[delegate EVEAPIHTTPClient:self didFailWithError:error];
}
}];
}
I was using a normal protocol/delegate method earlier. But once i make calls this API more than once like this: (IT WAS LIKE THIS:)
EVEAPIHTTPClient *client = [EVEAPIHTTPClient sharedEVEAPIHTTPClient];
client.delegate = self;
[client getCharacterListForKeyID:self.keyID vCode:self.vCode];
Previous call's delegate was being overwritten by next. So i changed to above style. Passing sender as an argument in the function:
EVEAPIHTTPClient *client = [EVEAPIHTTPClient sharedEVEAPIHTTPClient];
[client getCharacterListForKeyID:self.keyID vCode:self.vCode sender:self];
And i pass this sender to GET request's success and failure blocks.
What i wonder is : "Is this a good programming practice ?". Passing objects to blocks like this should be avoided if possible ? Is there any other more elegant way in AFHTTPSessionManager to handle this type of work (making same GET request over and over with different parameters and returning results to the respective request owners) more elegantly ?
Delegation pattern falters when it comes to simplicity and asynchronous request processing. You should be using blocks, here's an example
Your server class:
static NSString *const kNews = #"user_news/"; // somewhere above the #implementation
- (NSURLSessionDataTask *)newsWithPage:(NSNumber *)page
lastNewsID:(NSNumber *)lastNewsID
completion:(void (^)(NSString *errMsg, NSArray *news, NSNumber *nextPage))completionBlock {
return [self GET:kNews
parameters:#{#"page" : page,
#"news_id" : lastNewsID
}
success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *news = nil;
NSNumber *nextPage = nil;
NSString *errors = [self errors:responseObject[#"errors"]]; // process errors
if ([responseObject[#"status"] boolValue]) {
news = responseObject[#"news"];
nextPage = responseObject[#"next_page"];
[self assignToken];
}
completionBlock(errors, news, nextPage);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSString *errors = [self errors:error];
completionBlock(errors, nil, nil);
}];
}
The caller
- (void)dealloc {
[_task cancel]; // you don't want this task to execute if user suddenly removes your controller from the navigation controller's stack
}
- (void)requestNews {
typeof(self) __weak wself = self; // to avoid the retain cycle
self.task = [[GSGServer sharedInstance] newsWithPage:self.page
lastNewsID:self.lastNewsID
completion:^(NSString *errMsg, NSArray *news, NSNumber *nextPage) {
if (errMsg) {
[GSGAppDelegate alertQuick:errMsg]; // shortcut for posting UIAlertView, uses errMsg for message and "Error" as a title
return;
}
[wself.news addObjectsFromArray:news];
wself.lastNewsID = [wself.news firstObject][#"id"];
wself.page = nextPage;
[wself.tableView reloadData];
}];
}

AFNetworking 500 response body

I've been using AFNetworking 2.0 in my app.
I've noticed that if my web-service returns a 500 status code I do not get the body of the response.
Here is an example of my php code
try
{
$conn = new PDO( "sqlsrv:server=$serverName;Database = $database", $uid, $pwd);
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
return $conn;
}
catch( PDOException $e )
{
$response->status(500);
echo( "Connection Error: " . $e->getMessage() );
}
If I use a simple rest client this is an example of a response body.
Connection Error: SQLSTATE[08001]: [Microsoft][SQL Server Native Client 11.0]SQL Server Network Interfaces: Error Locating Server/Instance Specified [xFFFFFFFF].
However this seems to be the only response I can get from AFNetworking
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x15e58fa0 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
This is the part of my objective-c code that does this.
...} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#",error.description);
}];
Is there a way I can get the response body?
Edit: More code for clarification
Below is part of my subclass of AFHTTPSessionManager
#implementation MSMAMobileAPIClient
+ (MSMAMobileAPIClient *)sharedClient {
static MSMAMobileAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[MSMAMobileAPIClient alloc] initWithDefaultURL];
});
return _sharedClient;
}
- (id)initWithDefaultURL {
return [self initWithBaseURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://%#/mamobile/index.php/" ,[[NSUserDefaults standardUserDefaults] stringForKey:#"serviceIPAddress"]]]];
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
self.responseSerializer = [AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:#[[AFJSONResponseSerializer serializer], [AFHTTPResponseSerializer serializer]]];
return self;
}
I tried setting the response serializer to a AFCompoundResponseSerializer but it didn't seem to make a difference
Below is an example of a subclass that I call the Librarian.
-(void)searchForItemWithString:(NSString *)searchString withCompletionBlock:(arrayBlock)block {
self.inventorySearchBlock = block;
NSDictionary *parameters = #{#"query": searchString};
[[MSMAMobileAPIClient sharedClient] GET:#"inventory/search" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
if (!responseObject) {
NSLog(#"Error parsing JSON");
} else {
//do stuff with the json dictionary that's returned..
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#",error.description);
}];
}
UPDATE: I have created a github repository to contain the latest code I am using. All changes will be posted there. https://github.com/Hackmodford/HMFJSONResponseSerializerWithData
The answer comes from this issue on github.
https://github.com/AFNetworking/AFNetworking/issues/1397
gfiumara is the dev who came up with this. I have only slightly modified his subclass of AFJSONResponseSerializer to include an actual string instead of the NSData
//MSJSONResponseSerializerWithData.h
#import "AFURLResponseSerialization.h"
/// NSError userInfo key that will contain response data
static NSString * const JSONResponseSerializerWithDataKey = #"JSONResponseSerializerWithDataKey";
#interface MSJSONResponseSerializerWithData : AFJSONResponseSerializer
#end
// MSJSONResponseSerializerWithData.m
#import "MSJSONResponseSerializerWithData.h"
#implementation MSJSONResponseSerializerWithData
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (*error != nil) {
NSMutableDictionary *userInfo = [(*error).userInfo mutableCopy];
userInfo[JSONResponseSerializerWithDataKey] = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *newError = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:userInfo];
(*error) = newError;
}
return (nil);
}
return ([super responseObjectForResponse:response data:data error:error]);
}
#end
Here is an example of how I use it in the failure block.
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#",[error.userInfo objectForKey:#"JSONResponseSerializerWithDataKey"]);
}];
You need to use AFCompoundSerializer to tell the AFNetworking framework how to process all of the possible responses it could receive. By default it will only try to map JSON. A compound serializer will work through the serializers until it finds one that doesn't raise an error.
You want to use:
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers
on AFCompoundResponseSerializer (in AFURLResponseSerialization.h).
You need to pass an array of serializers that can handle the response. One of the serializers in the array should be an instance of AFHTTPResponseSerializer to handle your error responses.
If you include my category in your project, it's as simple as the following:
[mySessionManager POST:#"some-api" parameters:params success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
...
} failure:^(NSURLSessionDataTask *task, NSError *error) {
id responseObject = error.userInfo[kErrorResponseObjectKey];
... do something with the response ...
}];
Here's the code for my category. It swizzles AFURLSessionManager to inject a shim into the completion handler. The shim puts the response into the NSError's userInfo.
https://gist.github.com/chrishulbert/35ecbec4b37d36b0d608

Testing a AFNetworking API with Kiwi

I'm writing some tests for a networking API with kiwi.
Here is the relevant code:
//////////////////////////////////////////////////////
MyAPI.h
//////////////////////////////////////////////////////
#protocol MyAPIDelegate<NSObject>
-(void) onMyMethodCall:(id)response;
-(void) onFail:(NSError*)error message:(NSString*)message;
#end
#interface MyAPI : NSObject
#property (nonatomic, weak) id<MyAPIDelegate> delegate;
-(void) myMethodCall;
#end
//////////////////////////////////////////////////////
MyAPI.m
//////////////////////////////////////////////////////
#synthesize delegate
-(void) myMethodCall
{
NSDictionary *params = [Dictionary dictionaryWithObject:#"value1" forKey:#"param1"];
[self apiCallWithServerPath:#"logic/myMethodCall"
parameters:params
onSuccess:^(id response) {
[delegate onMyMethodCall];
}
onFailure:^(NSError *error, NSString* msg) {
[delegate onFail:error message:msg];
}];
}
-(void) apiCallWithServerPath:(NSString *)serverPath parameters:(NSDictionary *)parameters onSuccess:(void (^)(id))success onFailure:(void (^)(NSError *, NSString *))failure
{
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL urlWithString:#"http://www.myserver.com/"]];
[client setParameterEncoding:AFFormURLParameterEncoding];
[client postPath:serverPath
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
id response = [[JSONDecoder decoder] objectWithData:responseObject];
success(response);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"API call failed: error message = %#",[error localizedDescription]);
failure(error, [error localizedRecoverySuggestion]);
}];
}
//////////////////////////////////////////////////////
MyAPITest.m
//////////////////////////////////////////////////////
SPEC_BEGIN(MyAPITest)
describe(#"MyAPITest should", ^{
__block MyAPI *api;
__block MyAPI *delegateMock;
beforeEach(^{
delegateMock = [KWMock mockForProtocol:#protocol(MyAPIDelegate)];
api = [[MyAPI alloc] init];
api.delegate = delegateMock;
});
afterEach(^{
delegateMock = nil;
api = nil;
});
it(#"should go to the server and get an answer, dont care about the value atm", ^{
[[api should] receive:#selector(myMethodCall)];
KWCaptureSpy *spy = [delegateMock captureArgument:#selector(onMyMethodCall:) atIndex:0];
// I usually put a breakpoint here...
[api myMethodCall];
[[expectFutureValue(spy.argument) shouldEventually] beNonNil];
});
});
SPEC_END
This is a very simple test, that I'm building incrementally. Eventually I'd like to test the values inside spy.argument, but for now i'm only interested in making sure that it is not nil.
Running the test always fails with: [FAILED] Argument requested has yet to be captured.
Debugging doesn't work: When I try to put breakpoint in the test (where the comment says) its never steps into the method.
At the same time, if I put NSLog(s) inside MyAPI's myMethodCall, they are not printed on console.
Any help would be much appreciated.
/////////////////////// UPDATE //////////////////////////////////
Turns out that if I comment/remove this line (from the test):
[[api should] receive:#selector(myMethodCall)];
The test works. Any idea of why this line is causing the problem?

Resources