How AFNetworking to join request timeout and network impassability verification? - timeout

I use AFNetworking + SVProgressHUD post to request data. SVProgressHUD will always display if the Network has a problem or very slow and internet links have been cut.
How AFNetworking to judge the network problem? Or request timeout?

I'm not sure what the implementation is for SVProgressHUD but I'm using a similar one called MBProgressHUD in my current application. Hopefully this advice still applies.
If I understand your question correctly, you're wondering how to dismiss your progressHUD if the AFNetworking call fails? If that's your question, then all you need to do is dismiss your HUD in your failure block as shown below.
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[navigationController.view addSubview:HUD];
HUD.dimBackground = YES;
[HUD show:YES];
//Set up the request
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"example.com"]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
[request setHTTPMethod:#"POST"];
//Set up the operation
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Hide activity indicator after success
[HUD show:NO];
[HUD hide:YES];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Hide activity indicator after failure
[HUD show:NO];
[HUD hide:YES];
}];
// Fire off the operation
[operation start];

Related

What is the proper way to display a loading indicator while getting data from a URL

I'm somewhat new to objective-c, i'm developing a news iOS application, the app gets all its contents using JSON parsing from a url, i'm using AFNetworking for that and this is the method that i made:
- (void)getContents
{
NSString *urlString = #"http://some-url-that-has-json-output/";
urlString = [urlString stringByAppendingString:self.articleId];
NSLog(#"The call url is: %#",urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking asynchronous url request
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"The JSON data is: %#", responseObject);
jsonContents = [responseObject objectForKey:#"article"];
[self LoadStructure];
} failure:nil];
[operation start];
}
Now the data loads fine with this method.
My Question: How to display a loading indicator (could be a GIF) while getting the data ? and is this method above is the proper or best way to get data from a url ?
You can use default loading indicator of iOS(UIActivityIndicator). You should start animating it before completion block and should hide inside success and failure block.
You should create a method using indicator as class variable:
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.hidesWhenStopped = YES;
indicator.frame = CGRectMake(35, 15, 30, 30);
[self.view addSubview:indicator];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking asynchronous url request
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[indicator startAnimating];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"The JSON data is: %#", responseObject);
// to stop:
[indicator stopAnimating];
jsonContents = [responseObject objectForKey:#"article"];
[self LoadStructure];
} failure::^(AFHTTPRequestOperation *operation, NSError *error){
[indicator stopAnimating];
}];
[operation start];
Drag and drop UIActivityIndicatorView into your XIB view and connect it with IBOutlet.
.h file
#property(nonatomic, strong)IBOutlet UIActivityIndicatorView * activityIndicator;
Add UIActivity indicator view into your view. show it before
NSString *urlString = #"http://some-url-that-has-json-output/";
using:[self.activityIndicator startAnimating];
and stop it inside completion block and failure block
using: [self.activityIndicator stopAnimating];

Multiple photo background image upload and memory

I'm trying to upload multiple photos in background mode using AFNetworking and I managed to make it work.
The main problem I'm facing now is memory, which, while uploading more than 10 photos, runs out terminating my app.
What I'm doing is just firing all the uploads simultaneously as I've read in some answers on StackOverflow.
Now that this problem arises I'm wondering if I'm doing something wrong with memory management or if a better strategy would be to serialize the uploads, start the first one and when it terminates start the upload of the next in the handleEventsForBackgroundURLSession method.
Before changing completely the upload design I would like to hear from someone if it's a good alternative, as the majority of answers I've seen regarding this matter state that one should fire all the requests together.
Thank you
Create NSOperationQueue and add all your upload image operation to that queue. This queue will manage your system memory. Please refer below sample code.
NSOperationQueue *myQueue = [[NSOperationQueue alloc]init];
NSURLRequest *request = [[AFHTTPRequestSerializer serializer]
multipartFormRequestWithMethod:#"POST"
URLString:apiPostPhoto(singleton.userId, #"icon")
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSString *filepath = [[CustomFunctions getFilesPath] stringByAppendingPathComponent:#"icon.png"];
[formData appendPartWithFileURL:[NSURL fileURLWithPath:filepath] name:#"uploadicon" error:nil];
} error:nil
];
AFHTTPRequestOperation *operationUploadOne = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"uploadlogo:%#",operation.responseString);
[[NSUserDefaults standardUserDefaults]setObject:operation.responseString forKey:KEY_LOGO_TIMESTAMP];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"uploadlogo:%#",[error description]);
}];
[operation addObserver:self forKeyPath:#"isFinished" options:NSKeyValueObservingOptionNew context:nil];
[myQueue addOperation:operationUploadOne];
AFHTTPRequestOperation *operationUploadTwo = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[myQueue addOperation: operationUploadTwo];
you can add more number of operation using this method.
[myQueue addOperation: operationUploadTwo];

AFNetworking and synchronous request to fulfil online registration process

I am using AFNetworking and I've read that synchronous responses are discouraged. yet I need to check whether a user already exist in the online database before the person can go to the next stage of the app. Yes, a typical registration process.
My code as it stands it returns NO because is asynchronous. I need to find a way to check for the success call and return YES or NO depending on this callback.
Could anyone point me in the right direction of how to write an app that waits for the success call so that I know that the user has not been set?
-(BOOL)doesTheUserExistAlreadyOnServer:(NSString *)parsedEmail
{
BOOL *methodResponse = NO;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://www.myurl.co.uk/"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST"
path:#"http://www.myurl.co.uk/igym.php"
parameters:#{#"myvar2":#"piggy"}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
// NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
if ([[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] isEqualToString:#"piggy"]) {
__block methodResponse = YES;
NSLog(#"%#",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
return (BOOL)methodResponse;
}
EDIT:
I solved the problem using the following logic.
The user clicks on the registration button. The main method does all the preliminary non-web checks, then calls the [self doesTheUserExistAlreadyOnServer:_email.text];
that method code is now
-(void)doesTheUserExistAlreadyOnServer:(NSString *)parsedEmail
{
if(![_spinner isAnimating])
{
[_spinner startAnimating];
}
__block RegistrationViewController* me = self;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://www.myurl.co.uk/"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST"
path:#"http://www.myurl.co.uk/igym.php"
parameters:#{#"myvar2":#"piggy"}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
if ([[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] isEqualToString:#"piggy"]) {
NSLog(#"%#",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
[me registrationPartTwo:YES];
} else
{
[me registrationPartTwo:NO];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
}
Then once that block/callback is successful it calls
-(void)registrationPartTwo:(BOOL)doesItExistOnServer
{
[_spinner stopAnimating];
NSString *emailAlreadyInUseMessage = [NSString stringWithFormat:#"This email is already in use"];
if (doesItExistOnServer)
{
self.screenMsg.text = emailAlreadyInUseMessage;
//here more code to send the user the the next step
}
}
basically i solved this using a 2method registration process dependant upon callback, dont know if thats the best or most efficient way. but thats the way i could solve it on my own.
Use a delegate callback to notify when the operation completes. If needed, before starting the operation, put up an UIActivityIndicatorView (the spinner) to prevent the user from interacting with the app.

IOS/AFNetworking: enqueue two JSON operations and then compare the returned NSArrays

For my app, I have to connect to two webservices that return JSON.
I first rolled my own networking code using GCD, but seeing how AFNetworking handles things, I decided to implement it. Most things went ok, but at some point I'm retrieving two arrays filled with objects. Those two arrays are then compared using a different method. Somehow the actual enqueueing is either delayed or not working, depending on the code I'm using.
When using:
NSArray *operations = [NSArray arrayWithObjects:operation, operation1, nil];
AFHTTPClient *client = [[AFHTTPClient alloc]init];
[client enqueueBatchOfHTTPRequestOperations:operations progressBlock:nil completionBlock:^(NSArray *operations) {
[self compareArrays:self];
}
it just hangs.
When using:
[operation start];
[operation1 start];
[operation waitUntilFinished];
[operation1 waitUntilFinished];
[self compareArrays:self];
at the end, it gets the arrays, but only compares after the UI has been formed.
EDIT:
I checked Dave's answer, and it looks really streamlined.
Would my app benefit from using the AFHTTPClient, or does this method (using AFJSONRequestOperation) offer the same features? I recall AFHTTPClient handling reachability on its own now (though you need to set it). I fiddled around a bit, and got this working:
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
WebServiceStore *wss = [WebServiceStore sharedWebServiceStore];
self.userData = wss.userData;
serviceURL = [NSString stringWithFormat: #"WEBSERVICE URL"];
NSString* zoekFruit = [NSString stringWithFormat:
#"%#?customer=%#&gebruiker=%#&password=%#&breedte=%#&hoogte=%#&diameter=%#",
serviceURL,
self.userData.Klant,
self.userData.Gebruiker,
self.userData.Wachtwoord,
breedte,
hoogte,
diameter];
NSURL *url = [NSURL URLWithString:[zoekFruit stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
id results = [JSON valueForKey:#"data"];
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//Initiate the BWBand
BWBand *band = [[BWBand alloc]init];
//Set the BWBand's properties with valueforKey (or so).
[getBandenArray addObject:band];
}];
NSLog(#"getBandenArray: %#",getBandenArray);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Error retrieving Banden" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
[alert show];
}];
[queue addOperation:operation];
The AFNetworking class will allow you to create a bunch of operations, then hand them all off to be processed. You add some code to occur on success/failure for each individual request and/or when all requests have been processed.
Here's an outline of what you might do. I'll leave the details, actual comparison, and error handling to your imagination. :)
-(void)fetchAllTheData {
NSMutableArray * operations = [NSMutableArray array];
for (NSString * url in self.urlsToFetchArray) {
[operations addObject:[self operationToFetchSomeJSON:url]];
}
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:[PREFS objectForKey:#"categoryUrlBase"]]];
[client enqueueBatchOfHTTPRequestOperations:operations
progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(#"Finished %d of %d", numberOfFinishedOperations, totalNumberOfOperations);
}
completionBlock:^(NSArray *operations) {
DLog(#"All operations finished");
[[NSNotificationCenter defaultCenter] postNotificationName:#"Compare Everything" object:nil];
}];
}
-(AFHTTPRequestOperation *)operationToFetchSomeJSON:(NSString*)whichOne {
NSURL * jsonURL = [NSURL URLWithString:whichOne];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:jsonURL];
[request setHTTPShouldHandleCookies:NO];
[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"My data = %#", operation.responseString); // or responseData
// Got the data; save it off.
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DLog(#"Error downloading: %# %#", whichOne, error);
}];
return operation;
}
Hope that helps.

ios - ASIHTTPRequest and HUD Loading View Start and Stop

I have successfully implemented the ASIHTTPRequest framework to download a file from a URL and it saves in my Document Directory - YIPPIE!!!
Now I would like to show a Progress of some sort, so that the user can what is going on.
With the UIWebViewDelegate there are the following methods - I was hoping to use something similar.
-(void)webViewDidStartLoad:(UIWebView *)webView
-(void)webViewDidFinishLoad:(UIWebView *)webView
Is there something similar to use with ASIHTTP??
Here is an example mixing MBProgressHUD and ASI.
The idea is you start your updating before the begin the request and you end the updating in either the completion or failed blocks.
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Updating…";
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
}];
[request setFailedBlock:^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSError *error = [request error];
}];
[request startAsynchronous];

Resources