Crash ScenarioI am using AFNetworking for GET and POST requests and I am calling GET and POST methods on MAIN QUEUE and when the response comes,I update the UI.Now,before the response comes from API I am pushing onto another ViewController,and that's when the crash occurs.The message says:bad_accessPossible SolutionShould I be calling that method on some background queue so that I Can update that on MAIN QUEUE.Is it correct? Here is the code:
-(void)getDataFromUrl:(NSString *)url withRequestName:(NSString *)requestName withMessege:(NSMutableDictionary *)message
{
Reachability* googleReach = [Reachability reachabilityWithHostName:#"www.google.com"];
if(googleReach.currentReachabilityStatus!=0)
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(#"output :%#", responseObject);
arrayParsedJson = (NSMutableArray * )responseObject;
[self.delegate dataReceivedFromService:arrayParsedJson withRequestName:requestName];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[self.delegate dataReceivedErrorService:error withRequestName:requestName withMsg:error.description];
}];
}
else
{
//[TSMessage showNotificationInViewController:views title:Title_Alert subtitle:Service_Alert type:TSMessageNotificationTypeError];
}
}
So when I get response in the success block,I call my delegate methods you can see.But if I have navigated to some other viewController before the response comes in block, it crashes.
Related
I have followed up the BrainTree tutorial for objective-c and ended up the following implementation. I wonder, how could I able to store user credit card information such as Uber or AirBnb. Everytime, user clicks on make a payment, and displays the credit card information entry viewcontroller.
By the way, transaction happens succesfully, and I could able to see charges on my BrainTree sandbox account.
- (IBAction)placeOrderBtnClicked:(id)sender {
[self showDropIn: TOKEN];
}
- (void)showDropIn:(NSString *)clientTokenOrTokenizationKey {
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientTokenOrTokenizationKey request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else if (result.cancelled) {
NSLog(#"CANCELLED");
[self dismissViewControllerAnimated:YES completion:NULL];
} else {
[self postNonceToServer:result.paymentMethod.nonce];
}
}];
[self presentViewController:dropIn animated:YES completion:nil];
}
- (void)postNonceToServer:(NSString *)paymentMethodNonce {
self.manager = [AFHTTPSessionManager manager];
NSDictionary *params = #{#"amount" : #"44", #"payment_method_nonce" : paymentMethodNonce};
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager POST:URLString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull operation, id _Nonnull responseObject) {
NSLog (#"transaction is succesfull");
} failure:^(NSURLSessionDataTask * _Nullable operation, NSError * _Nonnull error) {
}];
}
// the following method never gets called!!!
- (void)fetchExistingPaymentMethod:(NSString *)clientToken {
[BTDropInResult fetchDropInResultForAuthorization:clientToken handler:^(BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else {
// Use the BTDropInResult properties to update your UI
NSLog(#"Payment method :%#", result.paymentMethod);
NSLog(#"Payment Description :%#", result.paymentDescription);
NSLog(#"Payment option type :%ld", (long)result.paymentOptionType);
}
}];
}
UPDATE: I want to see the following highlighted section
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
Do you mean that you want the payment form to display the stored payments or are you asking how to store the payments? In order to have the Drop-in display previously stored payment methods, you need to pass the customer_id into the ClientToken.generate() call on your server-side. If you are looking to save a payment method, then again, this would happen on your server-side call, as you would have to pass the nonce from client to server and use that nonce in a PaymentMethod.create() call.
I,m trying to upload video using afnetworking and my code is below in response it return nil url please let me know if you want any other detail . i don,t know where is my mistake.
{
// add hud to show sending image
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
NSString *format;
format=#"video/mp4";
myDict1 = #{#"user_id”:””,
#"timezone”:””,
#"friend_id”:””,
#"message_type”:””,
#"message":"",
#"language_id”:””
};
AFHTTPSessionManager *manager=[AFHTTPSessionManager manager];
NSString* webService=[NSString stringWithFormat:#"%#/send_messages",WEB_SERVICE_URL_BETA];
[manager POST:webService parameters:myDict1 constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData)
{
[formData appendPartWithFileData:picturedata name:#"file" fileName:#"testvideo.mov" mimeType:#"video/quickTime"];
} progress:nil success:^(NSURLSessionDataTask * Nonnull task, id Nullable responseObject) {
if ([[NSString stringWithFormat:#"%#",[responseObject valueForKey: #"status"] ] isEqualToString:#"1"])
{
}
else
{
}
}
failure:^(NSURLSessionDataTask _Nullable task, NSError _Nonnull error) {
NSLog(#"Failure %#",error);
}];
}
Since I am new to IOS and AFNetworking 3,0 is new, I don't know how to retrieve data from AFHTTPSessionManager.
I have to following message and I want to return the result
- (NSString *) makeServiceCall;
{
NSString *response = #"";
#try {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager
POST:self.url.absoluteString
parameters:self.parameters
progress:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Success: %#", responseObject);}
failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error: %#", error);
}];
[AFHTTPSessionManager manager].securityPolicy.allowInvalidCertificates = YES;
}
#catch (NSException *exception) {
NSLog(#"%#", exception.reason);
}
}
The method AFHTTPSessionManager POST:parameters:progress:success:failure: is an asynchronous method.
What you are trying to do is return a string from the method calling it. This will not work as the method will finish before the download has started.
You need to call this with a completion block something like this...
- (void)getStringWithCompletionHandler:(void (^)(id))completion {
NSLog(#"Method started");
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager
POST:self.url.absoluteString
parameters:self.parameters
progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(#"Download underway");
}
success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Download successful");
completion(responseObject);
}
failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error");
}];
// trying to return a string here won't work because the download hasn't finished yet.
// You can see the order of things happening by adding logs...
NSLog(#"Method finished");
}
The order of the logs in this code will be...
Method started
Method finished
Download underway
Download successful
As you can see, trying to return at the end of the method won't work because the download won't have completed yet.
I have a scenario where I need to quiet refresh auth token (relogin) again if it expired when I accessing other API but I'm having a hard time thinking how to code this without creating redundant codes for every APIs even though the flow is similar.
When user has expired auth token > call paid API A (return 401 unauthorised) > relogin again > call paid API A (run successfully)
I'm having difficult in wrapping my mind to call paid API A the second time with less code and not falling into infinite loop trap. Is there any method useful for this case like NSNotification center?
Note: I need to use API in this format from AFNetworkinglogin
- (NSURLSessionDataTask *)getApiA:(CallbackBlock)block{
CallbackBlock _block = [block copy];
NSString *urlString = [[NSURL URLWithString:GET_API_A_URL relativeToURL:[NSURL URLWithString:HOME_URL]] absoluteString];
return [self GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = (NSDictionary *)responseObject;
BLOCK_SAFE_RUN(block, response, nil, task);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if([self unauthorizedAccess:task]){ //401
***//call Login once again > run getApiA again***
}else if ([self forbiddenAccess:task]){ //403
}
BLOCK_SAFE_RUN(block, nil, error, task);
}];
}
If i get it right you could split it into 2 methods. And pass a bool for trying again. e.g.:
- (NSURLSessionDataTask *)getApiA:(id)block {
NSString *urlString = [[NSURL URLWithString:GET_API_A_URL relativeToURL:[NSURL URLWithString:HOME_URL]] absoluteString];
return [self doApiACallWithURL:urlString firstTry:YES completion:block];
}
- (NSURLSessionDataTask *)doApiACallWithURL:(NSString *)url firstTry:(BOOL)first completion:(CallbackBlock)completion {
__weak typeof(self) wself = self;
return [self GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = (NSDictionary *)responseObject;
BLOCK_SAFE_RUN(block, response, nil, task);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([wself unauthorizedAccess:task]) { //401
if (first) {
[wself doApiACallWithURL:url firstTry:NO completion:completion];
}
} else if ([wself forbiddenAccess:task]) { //403
}
BLOCK_SAFE_RUN(block, nil, error, task);
}];
}
and use a weak self for blocks is in most cases a good idea.
I am trying out afnetworking 2.0 and just trying to figure out how to cancel specific tasks.
The old way would be to use something like
[self cancelAllHTTPOperationsWithMethod:#"POST" path:#"user/receipts"]
but I dont see anything like this in 2.0
I created a sub class of AFHTTPSessionManager which gives me access to the array of pending tasks and I can cancel them directly but I dont know how to identify 1 task from another so I can cancel only specific tasks.
Task does have an taskidentifier but this doesnt appear to be what I need.
NSString *path = [NSString stringWithFormat:#"user/receipts"];
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:[prefs valueForKey:#"uuid"] password:self.store.authToken];
[self GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
completionBlock(responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
errorBlock(error);
}];
now if i wanted to cancel this request only how would I approach this?
You can store the task in a variable so you can access it later:
NSURLSessionDataTask* task = [self GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
completionBlock(responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
errorBlock(error);
}];
Then simply cancel it with [task cancel].
Another way would be to save the task ID of the task and later ask the URL session for its tasks and identify the task you wish to cancel:
// save task ID
_savedTaskID = task.taskIdentifier;
// cancel specific task
for (NSURLSessionDataTask* task in [self dataTasks]) {
if (task.taskIdentifier == _savedTaskID) {
[task cancel];
}
}
No need to save it, here is my implementation, use your subclass of AFURLSessionManager for cancelling specific request:
- (void)cancelAllHTTPOperationsWithPath:(NSString *)path
{
AFURLSessionManager * yourSessionManager = [self getSessionManager];
[[yourSessionManager session] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
[self cancelTasksInArray:dataTasks withPath:path];
[self cancelTasksInArray:uploadTasks withPath:path];
[self cancelTasksInArray:downloadTasks withPath:path];
}];
}
- (void)cancelTasksInArray:(NSArray *)tasksArray withPath:(NSString *)path
{
for (NSURLSessionTask *task in tasksArray) {
NSRange range = [[[[task currentRequest]URL] absoluteString] rangeOfString:path];
if (range.location != NSNotFound) {
[task cancel];
}
}
}
you can do the following
NSArray *operations = [[[MyClient sharedClient] operationQueue] operations];
if(operations && operations.count > 0){
for (NSOperation *operation in operations) {
if([operation isKindOfClass:[AFHTTPRequestOperation class]]){
AFHTTPRequestOperation *httpOperation = (AFHTTPRequestOperation *)operation;
NSLog(#"%#", [[httpOperation request] URL]);
//--- if this is your request then cancel it --> [httpOperation cancel];
}
}
}
Where MyClient is a child of AFHTTPClient and the function sharedClient is a static function which returns a singleton instance of MyClient