I do want to test my Service that calls a method that uses AFNetworking 3.x.
Service:
+ (AnyPromise *)allRepositoriesfetchRepositoriesByLanguage:(NSString *)language forPage:(int)page {
return [[APIClient sharedClient] fetchRepositoriesByLanguage:language forPage:page].then(^(NSDictionary *response) {
NSValueTransformer *transformer = [MTLJSONAdapter arrayTransformerWithModelClass:[RepositoriesModel class]];
NSArray *repositories = [transformer transformedValue:response[#"items"]];
return repositories;
});
}
Client:
#pragma mark - fetchRepositoriesByLanguage
- (AnyPromise *)fetchRepositoriesByLanguage:(NSString *)language forPage:(int)page {
NSString *urlString = [NSString stringWithFormat:#"search/repositories?q=language:%#&sort=stars&page=%d", language, page];
return [self fetchWithURLString:urlString].then(^(NSDictionary *response){
return response;
});
}
- (AnyPromise *)fetchWithURLString:(NSString *)stringURL {
return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter _Nonnull adapter) {
NSURL *URL = [NSURL URLWithString:stringURL];
[[APIClient sharedClient] GET:URL.absoluteString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
//NSLog(#"JSON: %#", responseObject);
NSError *error;
adapter(responseObject,error);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}];
}
UnitTest:
it(#"should fetchRepositoriesByLanguage not be nil", ^{
id mockHTTPClient = [OCMockObject partialMockForObject:[APIClient sharedClient]];
[[[mockHTTPClient expect] andDo:^(NSInvocation *invocation) {
// we define the sucess block:
void (^thenBlock)(NSDictionary *response) = nil;
// Using NSInvocation, we get access to the concrete block function
// that has been passed in by the actual test
// the arguments for the actual method start with 2 (see NSInvocation doc)
[invocation getArgument:&thenBlock atIndex:1];
// now we invoke the successBlock with some "JSON"...:
thenBlock([NSDictionary dictionaryWithObjectsAndKeys:#"Bom Dia", #"greetings", nil]); //here I got error
}] fetchRepositoriesByLanguage:[OCMArg any] forPage:1];
[mockHTTPClient fetchRepositoriesByLanguage:#"Java" forPage:1].then(^(NSDictionary *response) {
expect(response).toNot.beNil();
});
});
But I always got an error on thenBlock, an EXC_BAD_ACCESS.
Related
When I call this method with nil, the app crashes, but I want to know how to write it with nullable.
CRASH
[KPTaxnoteApiSaveHandler saveEntryWithUuid:uuid completion:nil];
OK
[KPTaxnoteApiSaveHandler saveEntryWithUuid:uuid completion:^(NSError *error) {}];
This is the code.
+ (void)saveEntryWithUuid:(NSString *)uuid completion:(void (^ __nullable)(NSError * _Nullable error))completion {
NSLog(#"saveEntryWithUuid");
Entry *entry = [Entry MR_findFirstByAttribute:#"uuid" withValue:uuid];
NSDictionary *params = #{#"entry[uuid]":entry.uuid};
[KPTaxnoteApiSaveHandler postWithUrl:kApiUrlStringForEntry params:params completion:^(NSError *error) {
if (!error) {
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Entry *entry = [Entry MR_findFirstByAttribute:#"uuid" withValue:uuid inContext:localContext];
entry.needSave = #NO;
}];
}
completion(error);
}];
+ (void)postWithUrl:(NSString *)urlStr params:(NSDictionary *)params completion:(nullable void (^)(NSError *_Nullable error))completion {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:urlStr parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
completion(nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completion(error);
}];
Where is the crash happening? My first guess is you need to do something like this:
if (completion) {
completion(nil); // Or completion(error);
}
This will handle the case where the completion is nil.
I am making a simple GET request using AFNetworking 2, but I am getting a NSURLErrorDomain error.
I created a manager class which subclasses AFHTTPRequestOperationManager and creates a singleton instance so that I can use a shared manager.
+ (id)manager {
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
- (id)init {
NSURL *baseURL = [ZSSAuthentication baseURL];
self = [super initWithBaseURL:baseURL];
if (self) {
[self setRequestSerializer:[AFJSONRequestSerializer serializer]];
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:[ZSSAuthentication username] password:[ZSSAuthentication password]];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
}
return self;
}
- (void)getData:(NSString *)pubID parameters:(NSDictionary *)parameters completion:(void (^)(NSDictionary *results))completion failure:(void (^)(NSError *error))failure {
NSString *url = [NSString stringWithFormat:#"data/all/%#", pubID];
[self GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Check to see if there are errors
ZSSError *error = [self errorForAPICall:responseObject status:[operation.response statusCode]];
if (error) {
[self logMessage:error.localizedDescription];
failure(error);
return;
}
NSDictionary *data = [responseObject objectForKey:#"data"];
completion(data);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
}
Then, in my viewController's viewDidLoad method I make a call to that method:
[[ZSSManager manager] getData:self.pubID parameters:nil completion:^(NSDictionary *results) {
self.items = results;
[self dataWillReload];
NSLog(#"%#", results);
[self.tableView reloadData];
} failure:^(NSError *error) {
NSLog(#"Error: %# %li", error, (long)error.code);
}];
Then I get this error:
Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)" UserInfo=0x7ff952306610 {NSErrorFailingURLKey=http://test.mysite.com/v1/data/all/5}
The strange thing is, on a previous viewController, I make a different call to the manager, and it completes and returns data correctly. But, when I make this second call, I get the error. AND, if I move that getData call out of the viewDidLoad method, and invoke it with a button press, it DOES WORK. What the heck?
What could be causing this?
I am having problems in saving a response coming from a POST request.
Based on AFnetworking documentation and NSScreencast tutorial I created my own subclass of AFHTTPRequestOperationManager, but I am not sure why the response is not saved.
How do I know, the response is not saved?
Because there is an error:(null) message in console and the my method does not perform segue.
I know that I am getting the values, because of the breakpoint that I put NSURLSessionDataTask
But I do not know why the values are not saved and I am getting an error message. I appreciate any help.
The APIClient/Manager
AuthAPIManager.h
#import "AFHTTPSessionManager.h"
#interface AuthAPIManager : AFHTTPSessionManager
+(AuthAPIManager *)sharedManager;
-(NSURLSessionDataTask *)initializeLogin:(NSString *)username completion:(void(^)(NSDictionary *results, NSError *error))completion;
//for login
#property (nonatomic,readonly,retain)NSString *StoreIdentifierForVendor;
#property(nonatomic,copy)NSString *devicetype;
#end
AuthAPIManager.m
#import "AuthAPIManager.h"
#import "LoginInfo.h"
#import "CredentialStore.h"
static AuthAPIManager *sharedManager =nil;
static dispatch_once_t onceToken;
#implementation AuthAPIManager
+(AuthAPIManager *)sharedManager
{
dispatch_once(&onceToken, ^{
sharedManager = [[AuthAPIManager alloc] initWithBaseURL:[NSURL URLWithString:BASE_URL]];
sharedManager.responseSerializer=[AFJSONResponseSerializer serializer];
sharedManager.requestSerializer = [AFJSONRequestSerializer serializer];
});
return sharedManager;
}
-(id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
}
return self;
}
-(NSURLSessionDataTask *)initializeLogin:(NSString *)username completion:(void (^)(NSDictionary *, NSError *))completion
{
_devicetype = #"ios";
_StoreIdentifierForVendor = [[[UIDevice currentDevice]identifierForVendor]UUIDString];
id loginParameters =#{#"AccountId":username,
#"DeviceType":_devicetype
};
NSURLSessionDataTask *task =[self POST:#"/Accn" parameters:loginParameters
success:^(NSURLSessionDataTask *task, id responseObject)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
if (httpResponse.statusCode == 200) {
LoginInfo *loginInfo =[[LoginInfo alloc]initWithDictionary:responseObject];
CredentialStore *credStore =[CredentialStore sharedStore];
credStore.loginInfo =loginInfo;
completion(responseObject,nil);
loginInfo = responseObject;
} else {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, nil);
});
NSLog(#"Received: %#", responseObject);
NSLog(#"Received HTTP %d", httpResponse.statusCode);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
}];
return task;
}
#end
And this is how I am calling my method in my view controller
- (IBAction)login:(id)sender
{
[_usernameTextField resignFirstResponder];
[SVProgressHUD show];
NSURLSessionDataTask *task = [[AuthAPIManager sharedManager] initializeLogin:self.usernameTextField.text completion:^(NSDictionary *results, NSError *error)
{
if (results) {
LoginInfo *loginInfo = [[LoginInfo alloc]initWithDictionary:results];
CredentialStore *credStore =[ CredentialStore sharedStore];
credStore.loginInfo =loginInfo;
[self performSegueWithIdentifier:#"welcomeViewSegue" sender:self];
}
else
{
NSLog(#"there is an error:%#",error);
}
}];
[SVProgressHUD dismiss];
}
Your success block is being called, but your status code is not 200.
You are calling your completion block like this:
completion(nil, nil);
So this code:
if (results) {
[snip]
}
else
{
NSLog(#"there is an error:%#",error);
}
is passed a nil results object.
Set a breakpoint on if (httpResponse.statusCode == 200) and inspect httpResponse in your debugger to see why it's not what you expect. (You may get a different success code, such as 204.)
Call your completion block with results instead of nil.
I'm using AFNetworking, AFOAuth1Client and AFLinkedInOAuth1Client to get the OAuth token from LinkedIn's API. This is all working well.
When I make a call using getPath to v1/people/~ I am receiving a 401, consistently.
If I push all the same values from my code into the LinkedIn console the generated link gives me the basic profile I am after.
What is causing the 401? I have a feeling it is either AFNetworking or my configuration of it.
Also, do you have any suggestions on how to diagnose the underlying issue?
Code below
+ (JJLinkedInClient *)sharedInstance {
DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
return [[self alloc] init];
});
}
- (id)init {
if ( (self = [super init]) ) {
_client = [[AFLinkedInOAuth1Client alloc] initWithBaseURL:[NSURL URLWithString:kJJLinkedInAPIBaseURLString]
key:#"XXXXXXXX"
secret:#"YYYYYYYY"];
// [_client registerHTTPOperationClass:[AFJSONRequestOperation class]];
[_client registerHTTPOperationClass:[AFHTTPRequestOperation class]];
// [_client setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
- (void)authorize:(void(^)())success {
__block JJLinkedInClient *weakSelf = self;
[self.client authorizeUsingOAuthWithRequestTokenPath:#"uas/oauth/requestToken"
userAuthorizationPath:#"uas/oauth/authorize"
callbackURL:[NSURL URLWithString:#"XXXXXXX://linkedin-auth-success"]
accessTokenPath:#"uas/oauth/accessToken"
accessMethod:#"POST"
success:^(AFOAuth1Token *accessToken) {
NSLog(#"Success: %#", accessToken);
[weakSelf getProfile];
success();
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
- (void)getProfile {
[self.client getPath:#"v1/people/~"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
}
The problem is in the AFPercentEscapedQueryStringPairMemberFromStringWithEncoding function, inside AFOAuth1Client. It needs to not escape the tilde, and it needs to escape the comma.
Since this is a static function though, I don't think I can override it in AFLinkedInOAuth1Client. I'll follow up with #mattt and see what he says. For now you can change it to this, to get it working:
static NSString * AFPercentEscapedQueryStringPairMemberFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
static NSString * const kAFCharactersToBeEscaped = #":/?&=;+!##$(),";
static NSString * const kAFCharactersToLeaveUnescaped = #"[].~";
// static NSString * const kAFCharactersToBeEscaped = #":/?&=;+!##$()~";
// static NSString * const kAFCharactersToLeaveUnescaped = #"[].";
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescaped, (__bridge CFStringRef)kAFCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding));
}
There are few similar questions to mine with answers,but could not find a way to use that solutions on my code. I would be glad, if anyone can help me on code. How to retry timeout request?
while block for pagination purpose:
while(mult < (int)totalCount) {
AFHTTPRequestOperation *opr = [self getRequestForAllRecordsOfClass:className updatedAfterDate:mostRecentUpdatedDate withinPage:page+1];
[pagedOperations addObject:opr];
mult = mult + PAGINATION_SIZE;
page = page + 1;
}
[[SDAFParseAPIClient sharedClient] enqueueBatchOfHTTPRequestOperations:operations progressBlock:^(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations) {
NSLog(#"totalNumberOfOperations: %u numberOfCompletedOperations: %u",totalNumberOfOperations,numberOfCompletedOperations);
} completionBlock:^(NSArray *operations) {
...
}];
and building request operation
-(AFHTTPRequestOperation *)getRequestForAllRecordsOfClass:(NSString *)className updatedAfterDate:(NSDate *)mostRecentDate withinPage:(int)page {
NSMutableURLRequest *request = [ZurmoHelper GETRequestForAllRecordsOfClass:className updatedAfterDate:mostRecentDate inPage:page];
AFHTTPRequestOperation *operation = [[SDAFParseAPIClient sharedClient] HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"RESPONSE pagination!!! %#:", className);
NSError *error = nil; //error in parsing json
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
if (!error) {
NSLog(#"json dict paged in : %d",page );
id dataObject = [jsonDict objectForKey:#"data"];
NSArray *itemsObject = [dataObject objectForKey:#"items"];
[self addItems:itemsObject className:className];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Request for class %# failed with error: %#", className, error);
if (error.code == -1001) {
NSLog(#"for class %# page: %d,",className,page);
//need to retry this operation???
}
}];
return operation;
}