I am using AFNetworking, (AFHTTPRequestOperation) to make calls to the network and get the data back. I need to make use of the code in a for loop (enumeration of cards) to check for each card and get the data back, if the operation is successful, I get the information about the cards and if it fails, I should get an alert (using an alert view). The problem is I am getting multiple alerts if it fails (because it's inside a for loop and there can be a number of cards). How can I just show one alert only when it fails to connect to the network?
I know the operation is async, but can't get this to work.
Code below:-
- (void)verifyMobileDeviceStatus
{
[self fetchRequest];
[self.contentsArray enumerateObjectsUsingBlock:^(GCards *gCard, NSUInteger idx, BOOL * stop) {
NSURL *baseURL = nil;
baseURL = [NSURL URLWithString:BASE_URL_STRING];
NSString *soapBody = [NSString stringWithFormat:#"<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?><soapenv:Envelope xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header/><soapenv:Body><VerifyMobileDeviceStatus xmlns=\"http://tempuri.org/\"><Request><AuthToken>%#</AuthToken></Request></VerifyMobileDeviceStatus></soapenv:Body></soapenv:Envelope>", [gCard valueForKey:#"authToken"]];
NSLog(#" auth token =%#", [gCard valueForKey:#"authToken"]);
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:baseURL];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapBody length]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[soapBody dataUsingEncoding:NSUTF8StringEncoding]];
[request addValue: msgLength forHTTPHeaderField:#"Content-Length"];
[request addValue:#"http://tempuri.org/VerifyMobileDeviceStatus" forHTTPHeaderField:#"SOAPAction"];
[request addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", operation.responseString);
NSString *xmlString = [operation responseString];
[parser setGCard:gCard];
[parser parseXML:xmlString];
if([gCard.merchantStatus isEqualToString:MERCHANT_STATUS_ACTIVE])
{
gCard.isPremiumAccount = [NSNumber numberWithInt:1];
}
else
{
gCard.isPremiumAccount = [NSNumber numberWithInt:0];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[parser.delegate verifyDeviceStatusParserDidFailWithError:#"Error"];
NSLog(#"error: %#", [error userInfo]);
}];
[operation start];
}];
}
- (void)verifyDeviceStatusParserDidFailWithError:(NSString *)error
{
NSString *errorString = [NSString stringWithFormat:#"Error =%#", [error description]];
NSLog(#"Error parsing XML: %#", errorString);
BlockAlertView* alert = [BlockAlertView alertWithTitle:#"Connection Failed" message:#"Connection to web service Failed. Please try again."];
[alert addButtonWithTitle:NSLocalizedString(#"OK", nil) block:^{ }];
[alert show];
[activityIndicator stopAnimating];
self.navigationController.view.userInteractionEnabled = YES;
}
It's showing the alert multiple times, if it fails and I need to show it only once.
Any help would be appreciated.
You need to make the alert view a property of the class, so.
1 - Declare a property (alert) of type BlockAlertView in the class that make the multiple requests (let's call it RequesterClass). This property will reference an unique alert view, which will be displayed only once.
2 - Put this 2 lines in the init method of the RequesterClass
_alert = [BlockAlertView alertWithTitle:#"Connection Failed" message:#"Connection to web service Failed. Please try again."];
[_alert addButtonWithTitle:NSLocalizedString(#"OK", nil) block:^{ }];
3 - Modify the verifyDeviceStatusParserDidFailWithError: as follows:
- (void)verifyDeviceStatusParserDidFailWithError:(NSString *)error
{
NSString *errorString = [NSString stringWithFormat:#"Error =%#", [error description]];
NSLog(#"Error parsing XML: %#", errorString);
if(!alert.visible)
{
[alert show];
}
[activityIndicator stopAnimating];
self.navigationController.view.userInteractionEnabled = YES;
}
Hope it helps!
Related
I have an implementation of a PUT request in Objective C. The request is successful when executed externally from iOS / Objective C using Postman but returns an error (status code 500) when called within Objective C. As far as I can see, the implementation mirrors the way the call is set up in Postman. Here is the call I am trying to mirror using Objective C:
And here is my implementation in Objective C:
- (void)callUnregisterForPushNotifications:(NSString *)accessToken
pushToken:(NSString *)pushToken
completionBlock:(void (^)(NSMutableArray *resultsArray))completion{
NSLog(#"UNREGISTER FOR PUSH CALLED!");
NSLog(#"PUSH TOKEN %#", pushToken);
NSString *appendUrl = #"alerts/unregisterpush/";
NSLog(#"APPEND URL %#",appendUrl);
NSURL *unregisterUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", BaseURLString, appendUrl]];
NSLog(#"UNREGISTER URL: %#",unregisterUrl);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:unregisterUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[request setHTTPMethod:#"PUT"];
NSString *appendToken = [NSString stringWithFormat:#"Bearer %#", accessToken];
NSLog(#"TOKEN: %#",appendToken);
[request addValue:appendToken forHTTPHeaderField:#"Authorization"];
NSString *postString = [NSString stringWithFormat:#"Guid=%#",pushToken];
NSLog(#"POST STRING: %#",postString);
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"REQUEST %#",request);
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"UNREGISTER PUSH NOTIFICATIONS RESPONSE: %#", response);
NSLog(#"UNREGISTER PUSH NOTIFICATIONS ERROR: %#", error);
NSLog(#"UNREGISTER PUSH NOTIFICATIONS DATA: %#", data);
NSData *_data = data;// ... whatever
NSMutableString *_string = [NSMutableString stringWithString:#""];
for (int i = 0; i < _data.length; i++) {
unsigned char _byte;
[_data getBytes:&_byte range:NSMakeRange(i, 1)];
if (_byte >= 32 && _byte < 127) {
[_string appendFormat:#"%c", _byte];
} else {
[_string appendFormat:#"[%d]", _byte];
}
}
NSLog(#"UNREGISTER PUSH RESPONSE: %#", _string);
id obj= [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!obj) {
//NSLog(#"REGISTER PUSH NOTIFICATIONS ERROR: %#", error);
} else {
//NSLog(#"REGISTER PUSH NOTIFICATIONS DATA: %#", obj);
if(completion) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
completion((NSMutableArray*)obj);
}
//self.accessToken = [obj valueForKey:#"access_token"];
//NSLog(#"ACCESS TOKEN: %#",self.accessToken);
}
}];
}
Any input / help would be greatly appreciated, thanks in advance!
This line:
NSString *postString = [NSString stringWithFormat:#"Guid=%#",pushToken];
Doesn't match what you are showing in Postman.
Missing { } around the object
Missing " " around the field and value
The Accept and Content-type header are missing
I want to get address from latitude and longitude with GoogleGEO CODING (EX URL = http://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&sensor=true_or_false )
So I want to get JSON from that page by AFNetworking.
It's my code below :
IBAction)reLocation:(UIButton*)sender
{
if(sender.tag==1)
{
NSArray *gpsValue = [self getGPS];
float lat = [[gpsValue objectAtIndex:0] floatValue];
float lon = [[gpsValue objectAtIndex:1] floatValue];
NSString *string = [NSString stringWithFormat:#"%#%#,%#&sensor=true_or_false",GEOCODING_URL,[NSString stringWithFormat:#"%f", lat],[NSString stringWithFormat:#"%f",lon]]; // NSString *str = [NSString stringWithFormat:#"%f", myFloat];
NSLog(string);
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"AFNetworking success");
NSDictionary *location = (NSDictionary *)responseObject;
// 3
self.title = #"JSON Retrieved";
//[self.tableView reloadData];
NSLog(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"AFNetworking failure");
switch (operation.response.statusCode) {
case 400:
// Do stuff
NSLog(#"error 400");
break;
default:
NSLog([NSString stringWithFormat:#"%ld",(long)operation.response.statusCode]);
break;
}
// 4
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}];
// 5
[operation start];
}}
But when I click my button, always afnetworking fails and log shows 0 for status code.
I also got url log that i put in
i already checked that url is not problem (it shows json in working order)
I debug with simulator!
Is there something I miss ?
I have an app which does an async post to a server. Then it decodes the json and returns the message from the server. I put a few debugging log entries in my code, so I know that the response from the server, as well as the decoding of the json are instantaneous. The problem is that after the json is decoded, the async task runs for about 6 seconds before it calls the next event (Showing the popup dialog).
- (IBAction)register:(id)sender {
[self startPost]; // Starts spinner animation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doPost]; // performs post
});
}
-(void)doPost
{
#try {
NSString *post =[[NSString alloc] initWithFormat:#"request=register&platform=ios&email=%#&password=%#",self.email.text,self.password.text];
//NSLog(#"PostData: %#",post);
NSURL *url=[NSURL URLWithString:#"https://site.com/api.php"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//NSLog(#"Response code: %d", [response statusCode]);
if ([response statusCode] >=200 && [response statusCode] <300)
{
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
//NSLog(#"Response ==> %#", responseData);
NSData *responseDataNew = [responseData dataUsingEncoding:NSUTF8StringEncoding];
NSError* error = nil;
NSDictionary *myDictionary = [NSJSONSerialization JSONObjectWithData:responseDataNew options:NSJSONReadingMutableContainers error:&error];
if ( error ){
[self alertStatus:#"Unknown response code from server" :#"Whoops!"];
NSLog(#"Response ==> %#", responseData);
[self postDone];
}else{
if ([myDictionary[#"error"] isEqualToNumber:(#1)])
{
NSLog(#"ERROR DETECTED");
[self alertStatus:myDictionary[#"message"]:#"Whoops!"];
[self postDone];
}
else
{
[self alertSuccess];
[self postDone];
}
}
} else {
if (error) NSLog(#"Error: %#", error);
[self alertStatus:#"Connection Failed" :#"Whoops!"];
[self postDone];
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Registration Failed." :#"Whoops!"];
[self postDone];
}
}
-(void)startPost
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
self.email.enabled = false;
self.password.enabled = false;
self.confirm.enabled = false;
self.cancelButton.enabled = false;
}
- (void) alertStatus:(NSString *)msg :(NSString *)title
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
message:msg
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[alertView setTag:0];
[alertView show];
}
- (void) alertSuccess
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Success!"
message:#"You have been successfully registered."
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[alertView setTag:1];
[alertView show];
}
-(void)postDone
{
self.registerButton.hidden = false;
self.spinner.hidden = true;
self.loadingText.hidden = true;
//[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.email.enabled = true;
self.password.enabled = true;
self.confirm.enabled = true;
self.cancelButton.enabled = true;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{if (alertView.tag == 1)
{
[self dismissViewControllerAnimated:YES completion:nil];
}}
The alertStatus and alertSuccess functions just pop up a message box briefly.
When I run the code, I purposefully enter bad information so the log says "ERROR DETECTED". The problem is that it takes another 6 seconds before anything happens after that.
After you have called:
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
and obtained the data, you should switch back to the main thread to use it. This is because all UI updates must be done on the main thread.
So, all that code after you get the data should be moved to a new method and called as:
dispatch_async(dispatch_get_main_queue(), ^{
[self handleData:urlData withResponse:response error:error];
}
And you should also put the exception catch code inside dispatch_async(dispatch_get_main_queue(), ^{ because you try to update the UI there too...
In my code I am using an UIActivityIndicatorView on an UIAlertView. It is working fine but my problem is it is not showing up on correct time. I mean to say when the device get data from web service after that this loading indicator is appearing in the end and its not rite thing I think because I want it to be appear when the web service is sending or receiving data.
I need help as I am new to iOS app development. If there is any other easy way to do this thing then suggest me.
I hope my question is clear, my problem is according to this code the loading indicator is appearing after i get reply from web service but i want to run this indicator as the user will press update button and web service should be called after that. Tell me where i am wrong.
Here is the code I am using
-(IBAction)update:(id)sender
{
av=[[UIAlertView alloc] initWithTitle:#"Updating Image..." message:#"" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
ActInd=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[ActInd startAnimating];
[ActInd setFrame:CGRectMake(125, 60, 37, 37)];
[av addSubview:ActInd];
[av show];
{
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
int gid=[defaults integerForKey:#"gid"];
NSString *gameid=[NSString stringWithFormat:#"%i", gid];
NSLog(#"%#",gameid);
img=mainImage.image;
NSData *imgdata=UIImagePNGRepresentation(img);
NSString *imgstring=[imgdata base64EncodedString];
NSLog(#"%#",imgstring);
NSString *escapedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)imgstring,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8);
NSLog(#"escapedString: %#",escapedString);
#try
{
NSString *post =[[NSString alloc] initWithFormat:#"gid=%#&image=%#",gameid,escapedString];
NSLog(#"%#",post);
NSURL *url=[NSURL URLWithString:#"http://mywebspace/updategameimage.php"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"Response code: %d", [response statusCode]);
if ([response statusCode] >=200 && [response statusCode] <300) {
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
SBJsonParser *jsonParser = [SBJsonParser new];
NSDictionary *jsonData = (NSDictionary *) [jsonParser objectWithString:responseData error:nil];
NSLog(#"%#",jsonData);
NSInteger type = [(NSNumber *)[jsonData objectForKey:#"type"] integerValue];
NSLog(#"%d",type);
if (type==1) {
[self alertStatus:#"You can Keep on Drawing" :#"Sketch Updated"];
}
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Unable to connect with game." :#"Connection Failed!"];
}
}
[av dismissWithClickedButtonIndex:0 animated:YES];
[av release]; av=nil;
}
UI updates are done on main thread. You have started activity indicator on main thread.It's fine.
Now, you are making synchronous network call on main thread. It should be asynchronous. Here until you will receive the response from network call, your main thread will remain busy and your UI will not be updated.
To update the UI, you can either make the network call asynchronous or you can start the activity indicator in a separate function and then delay the call of network activity by performselector:afterdelay method.
You can use GCD, Raywenderlich Tutorial
-(IBAction)update:(id)sender
{
/*
Setup indicator and show it
*/
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
/*
Do network call
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
Update UI
*/
});
});
}
I am using performSelector to call URLRequest every couple of second with different timetstamp. However, data processing may take longer than the time I have defined.
[self performSelector:#selector(process) withObject:nil afterDelay:1.6];
below part shows the method is called
-(void)process
{
timestamp=[NSString stringWithFormat:#"%1.f",progressValue];
NSString *contour=#"&bandschema=4";
NSString *url6=[NSString stringWithFormat:#"http://contour.php? callback=contourData%#&type=json×tamp=%#%#",timestamp,timestamp,contour];
NSURL *url1=[NSURL URLWithString:url6];
__weak ASIHTTPRequest *request1 = [ASIHTTPRequest requestWithURL:url1];
[request1 setCompletionBlock:^{
responseString = [request1 responseString];
[self plotPoint:self.responseString];
}];
[request1 setFailedBlock:^{
NSError *error=[request1 error];
NSLog(#"Error: %#", error.localizedDescription);
}];
[request1 startAsynchronous];
}
this part is start point of analyzing data.
-(void)plotPoint:(NSString *)request
{
NSArray *polygonArray = [[dict objectForKey:#"data"]valueForKey:#"polygon"];
NSArray *valleyPolygonArray = [[dict objectForKey:#"valley"]valueForKey:#"polygon"];
CLLocationCoordinate2D *coords;
}
However sometimes time interval is not enough to get new data especially when internet connection is not good.
Could you guide me please? How could I handle the problem? What is the optimal solution?
Why dont you call process after you get the response as follows,
-(void)process
{
timestamp=[NSString stringWithFormat:#"%1.f",progressValue];
NSString *contour=#"&bandschema=4";
NSString *url6=[NSString stringWithFormat:#"http://contour.php? callback=contourData%#&type=json×tamp=%#%#",timestamp,timestamp,contour];
NSURL *url1=[NSURL URLWithString:url6];
__weak ASIHTTPRequest *request1 = [ASIHTTPRequest requestWithURL:url1];
[request1 setCompletionBlock:^{
responseString = [request1 responseString];
[self plotPoint:self.responseString];
if (somecondition)//based on some condition to break the chain when needed
[self process];
}];
[request1 setFailedBlock:^{
NSError *error=[request1 error];
NSLog(#"Error: %#", error.localizedDescription);
if (somecondition)//based on some condition to break the chain when needed
[self process];
}];
[request1 startAsynchronous];
}
This way you can keep 1.6 as the exact time interval after getting a response to creating a new request.