MBProgressHud, AFnetworking and refreshing data - ios

I'm using AFnetworking to make a call to a server. While downloading I am using MBProgressHUD to show that data is being downloaded. So far everything works great. My issue is when I press the home button and then relaunch the app. I would like for the page to automatically refresh itself and for MBProgressHUD to display to the user that something is being downloaded. That I cannot do. I can download the data, but I cannot get the HUD part to work.
First, in the viewDidLoad Method I add this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidBecomeActiveNotificationAction)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Now in the method applicationDidBecomeActiveNotificationAction, I call [self downloadWebsites].
In the method downloadWebsites is where the bulk of the work is done: Here it is:
//show the hud
MBProgressHUD* progressHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
progressHUD.labelText = #"Loading";
progressHUD.mode = MBProgressHUDAnimationFade;
[self.list_websites getPath:[NSString stringWithFormat:#"%#?%#", #"websites", self.auth_header] parameters: nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//download data in the success block
//refresh the ui
[self.tableView reloadData];
[progressHud self.view animated:YES];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure block. log the error
NSLog([error description]);
}];
Why doesn't this work? I can get the data. But I can't get the progressHud to display. How do I fix this?

The Notification Runloop and the HTTP request runloop maybe not the same. So method that show a progress HUD maybe not called.

Related

AFNetworking + MBProgressHUD unable to update HUD status

In the viewDidLoad method of my viewController I wrote something like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
self.hud.labelText = #"Loading";
[serverData loadNewsData:self ];
}
Basically I show the HUD and start a method loadNewsData on another class, passing the viewController.
The method in the other class is like this:
-(void)loadNewsData:(LoadViewController*) loadViewController
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:[siteAddress stringByAppendingString:getNewsList] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
loadViewController.hud.detailsLabelText = #"updating news";
[NewsModel setNewsData:responseObject];
loadViewController.hud.detailsLabelText = #"Finished";
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
When the request of the AFHTTPRequestOperation starts I was expecting to see the sub menu in the HUD to change in "Updating news". But nothing happens. I just see Finished at the end of the process. The requests takes like 10sec so I was expecting to see "updating news", but nothing.
I try also to this:
dispatch_async(dispatch_get_main_queue(), ^{
loadViewController.hud.detailsLabelText = #"updating news"; });
Thinking I have to be in the UI queue to update some UI stuff, but it did not work either.
I'm a newbie probably I miss something.
thanks for any good advices.
Try to move loadViewController.hud.detailsLabelText = #"updating news"; before you start GET request. If you keep this inside the success block you won't see the text.
This link should help you out... NSURLConnection
It describes the NSURLConnection delegate method to use and how to use it for the exact situation your in. I understand that this isn't an AFNetworking solution but if your still looking for an AFNetworking solution, check out this method - (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block;.
Hope this gets you in the right track.

UI still delayed even using GCD

In my modal UI there is a "DONE" button linked with IBAction -done:, it will upload a text to (lets say Dropbox server). Its code looks like this
- (IBAction)done:(id)sender {
// must contain text in textview
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (![_textView.text isEqualToString:#""]) {
// check to see if we are adding a new note
if (!self.note) {
DBFile *newNote = [[DBFile alloc] init];
newNote.root = #"dropbox";
self.note = newNote;
}
_note.contents = _textView.text;
_note.path = _filename.text;
// - UPLOAD FILE TO DROPBOX - //
NSLog(#"Initializing URL...");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSURL *url = [Dropbox uploadURLForPath:self.note.path];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"PUT"];
NSData *noteContents = [self.note.contents dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Creating session task...");
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request
fromData:noteContents
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *resp = (NSHTTPURLResponse *) response;
if (!error && resp.statusCode == 200) {
NSLog(#"OK");
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate noteDetailsViewControllerDoneWithDetails:self];
});
} else {
NSLog(#"Status code: %d", resp.statusCode);
}
}];
[uploadTask resume];
});
} else {
UIAlertView *noTextAlert = [[UIAlertView alloc] initWithTitle:#"No text"
message:#"Need to enter text"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[noTextAlert show];
}
}
The delegate method noteDetailsViewControllerDoneWithDetails: of this class is look like this
-(void)noteDetailsViewControllerDoneWithDetails:(NoteDetailsViewController *)controller{
// refresh to get latest
[self dismissViewControllerAnimated:YES completion:nil];
[self notesOnDropbox];}
(notesOnDropbox is a time-consuming task). When DONE button is tapped, I expect this modal VC/UI to dismiss immediately and it fetches data on background (by notesOnDropbox method). However, when I try tapping DONE button, my UI stop responding for about seconds, after that the modal UI is dismissed. I cannot figure out where I misuse the GCD. Please help me.
if you want to dismiss your modal VC/UI immediately, just ask the delegate to dismiss,
like is:
- (IBAction)done:(id)sender {
[self.delegate noteDetailsViewControllerDoneWithDetails:self];
// ...
}
In your sample code,
you do the dismiss action after the upload task completed, but the upload task is asynchronous.
and you ask the delegate to dismiss use GCD dispatch_async, this is asynchronous task, too.
After all, you have to consider the what time to do upload, who to do upload task and what time to invoke notesOnDropbox.
First, if notesOnDropbox is a time-consuming task, then you should not be performing it on the main thread (as you are doing). If it is sufficiently time-consuming and you do it on the main thread, the WatchDog process will kill your app dead right before the user's eyes.
Second, there is no need to get off the main thread to do an upload. If you use NSURLSession correctly, it will be asynchronous.
Your code only calls noteDetailsViewControllerDoneWithDetails when the whole upload task is completed, because that's how you wrote your code. Actually, the situation seems worse. If the upload task has any kinds of problems, noteDetailsViewControllerDoneWithDetails will never be called.
You need to call noteDetailsViewControllerDoneWithDetails as soon as possible, and then think about what you are going to do when the upload fails - which might easily happen a long time later.

Logging round trip to server with RestKit 2

I am trying to figure out how long my round trip to the server is before any mapping occurs. I need to get at the RKObjectRequestOperation, but it is only available in the success and fail blocks.
I see that RestKit 2 does send a notification:
[[NSNotificationCenter defaultCenter] postNotificationName:RKObjectRequestOperationDidStartNotification object:weakSelf];
But there is no user info sent along.
Any ideas on how I can do this? I was thinking of an associated object onto the operation queue but that is causing crashes.
What I did:
self.op = self.objectManager.operationQueue.operations.lastObject;
objc_setAssociatedObject(self, &kRetrieverRequestOperationKey, self.op, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Added the above right after:
[self.objectManager getObjectsAtPath:resourcePath parameters:parmsDictionary success:^(RKObjectRequestOperation *requestOperation, RKMappingResult *mappingResult)
{
[weakSelf didLoadOperation:requestOperation result:mappingResult isFromCache:NO];
[weakSelf requestDidEnd:requestOperation];
} failure:^(RKObjectRequestOperation *requestOperation, NSError *error) {
[weakSelf requestOperation:requestOperation didFailWithError:error];
[weakSelf requestDidEnd:requestOperation];
}];
Then when RestKit posted its notification I was able to get at the RKObjectRequestOperation.
Not ideal, but seems to work.

Sending NSOperationQueue to UITableView as a DataSource

i have written code to downloading data from server using NSOperationQueue and NSOperation. and now i want to show progress on UserInterface. i used UITableView and used NSOpeartionQueue as a datasource in tableview delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[[Downloadmanager sharedInstance] downloadOperationQueue] count];
}
and bind NSOperation`s properties to UITableViewCell.
1) Is this a fisible solution to sending NSOperationQueue as a datasource to tableview delegate ?
2) How to implement notification to reload tableview when NSOperation's state changes?
Thanks.
I don't think it's the proper way of showing progress using NSOperationQueue as a datasource to tableview. You can use networking library like AFNetworking for downloading data and use setDownloadProgressBlock: method for showing progress. Refer this link for the code download progress.
It's easy to reload tableview when the download completes, just call [tableView reloadData] in completionblock.
Here is the code which shows image downloading using AFNetworking which you can easily change for data download.(refer this gist)
- (void)downloadMultiAFN {
// Basic Activity Indicator to indicate download
UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[loading startAnimating];
[self.imageView.superview addSubview:loading];
loading.center = self.imageView.center;
// Create a request from the url, make an AFImageRequestOperation initialized with that request
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.picUrl]];
AFImageRequestOperation *op = [[AFImageRequestOperation alloc] initWithRequest:request];
// Set a download progress block for the operation
[op setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
if ([op.request.URL.absoluteString isEqualToString:#"http://www.pleiade.org/images/hubble-m45_large.jpg"]) {
self.progressBar.progress = (float) totalBytesRead/totalBytesExpectedToRead;
} else self.progressBar2.progress = (float) totalBytesRead/totalBytesExpectedToRead;
}];
// Set a completion block for the operation
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
self.imageView.image = responseObject;
self.image = responseObject;
if ([op.request.URL.absoluteString isEqualToString:#"http://www.pleiade.org/images/hubble-m45_large.jpg"]) {
self.progressBar.progress = 0;
} else self.progressBar2.progress = 0;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {}];
// Start the image download operation
[op start];
// Remove the activity indicator
[loading stopAnimating];
[loading removeFromSuperview];
}
That is an interesting idea, but I don't think it's a good practice make such a "high coupling" - linking model so tightly to the view.
I'd approach it as - download the data on the background thread as you already do - with NSOperationQueue but save it to some kind of an object; say NSMutableArray that serves as the data source for the table view.
Every time a single operation ends (use completion handlers or KVO to get informed) - update the table view. The update can be done two ways - reloading or updating. I'll leave the choice up to you - you can read further discussion about that in this question.

Wait for all methods to finish before loading the view IOS

I have a UIViewController with a method which calls a web server, and I wish to wait until this method is finished before showing the UIViewController.
How can I do this?
If you call webserver via an http request, or something similar, you can set a custom delegate method for the didFinish event and show your viewController here.
Take a look at ASIHTTPRequest.
You can use grand central dispatch (GCD), heres a example:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Web Service Calls
dispatch_async(dispatch_get_main_queue(), ^{
//UI Stuff
});
});
You should request data before you show your view controller.
Then, using designated initialized, open view controller and show his data.
Example (with AFNetworking framework)
[[AFAppDotNetAPIClient sharedClient] getPath:#"stream/0/posts/stream/global" parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) {
NSLog(#"JSON: %#", JSON);
MyModel model = [self parse:json];
ViewController *vc = [[ViewController alloc] initWithData:model];
[self.navigationController pushViewController:vc animated:YES];
} failure:nil];

Resources