Wait the call to database - objective-c

Is there some method to wait until the call is completed? This is the code:
NSString *strImmagineURL = [NSString stringWithFormat:#"http://www.xxxgo.net/Scxxs/Evento/WIG2ricexxx.php?evento=%#",idLocale];
NSData *dataImmagineURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strImmagineURL]];
immagine = [UIImage imageWithData:dataImmagineURL];
[`arrayFoto` addObject:immagine];
I noticed that, when the connection in slow, addObject crashes my app because immagine is still nil.
I want to wait for immagine and afterward fill arrayFoto.

It's nil not because it's slow, but because the request failed. dataWithContentsOfURL: is a synchronous method, meaning execution of your program will not continue until the request completes or fails.
Properly handling error cases in HTTP requests requires a lot more setup. You should use something like ASI HTTP Request to handle your requests and success/error handlers more gracefully.

Related

How to get message-body immediately after receive a HTTP header by using NSURLSession?

I want to use NSURLSession to receive a xml stream from server and display each xml immediately on the screen.
Here is my delegate code:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// Enumerate each message-body.
[data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) {
// Convert message-body to xml string.
NSString *string = [[NSString alloc] initWithBytes:bytes
length:byteRange.length
encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
// Some code to display string.
// ...
});
}];
}
This code works fine except one problem.
The problem is, sometimes when receiving stream, the didReceiveData doesn't be called immediately after didReceiveResponse, it sometimes receive more than one HTTP messages, and then call didReceiveData once to pass all messages which it just receive for me.
It can sometimes take a while for receiving multiple messages, and makes my application not able to display the xml in realtime.
Is there any configuration or property can make it call didReceiveData immediately? I read the document but find nothing useful.
Thanks a bunch.
Update:
I tried to use NSURLConnection to do the same things, it runs perfectly without this problem.
Each didReceiveData is called behind didReceiveResponse immediately.
How can I make the didReceiveData of NSURLSession work just like NSURLConnection?
IIRC, NSURLSession should send data as it receives it, but only after it receives a certain about of data, or after a period of time.
If you're trying to get individual chunks of data, you might instead consider sending them back from the server as a multipart response. Each "part" would contain one of your messages, and you would get a new didReceiveResponse: callback between each one.
With that said, I'm not sure why NSURLConnection would behave differently. They use a lot of the same code under the hood. You might try filing a bug with Apple.

How to avoid app from crashing in synchronous request

I am posting some JSON data to a server using JSONKit.
Before posting the data, I am checking the internet connection.
But if the internet connection is lost after sending request, after this line:
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
my app crashes.
I don't want to use asynchronous method here.
Is there any particular way, where I can show an alert rather than having my app crash in this situation?
I got it. if there is no internet connection, then response variable will be nil. I can check that if (response == nil) and give my alert here.

How to run Asynchronous service call with NSOperationQueue or performSelectorInBackground

I am using NSURLConnection asynchronous call to load data from server.While calling service method with performSelectorInBackground,The method gets called but NSURLConnection does not provide any response.I am using asynchronous call because i have to validated authentication first.Whenever i tried service call on main thread it gives me proper data but UI gets freeze.
1) How to make asynchronous call with performSelectorInBackground
what is wrong am i doing by loading data asynchronously.
any help will be appreciated
Here is a possible way though not via NSURLConnection, but it doesn't block:
NSURL *xmlURL = [NSURL URLWithString:URL];
if (self.xmlParser != nil)
{
[self.xmlParser abortParsing];
self.xmlParser = nil;
}
dispatch_queue_t loadQueue1 = dispatch_queue_create("loadQueue1", NULL);
// Use another thread to avoid blocking main thread
dispatch_async(loadQueue1,^{
self.xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:xmlURL] autorelease];
// -------------------------------------------------------------------
[self.xmlParser setDelegate:self];
dispatch_async(dispatch_get_main_queue(),^{
if (self.xmlParser != nil)
{
[self.xmlParser parse];
}
});
});
dispatch_release(loadQueue1);
Ok,after debugging the code i got the error.It is not the asynchronous call but the updating method after receiving the huge response freezes my UI.Also the NSLog that i put to print the data causes the issue.
If you use NSURLConnection with the asynchronous API, you don't need to use performSelectorInBackground. You call it from the main thread, and you set a delegate, the delegate will be called when data is coming or another event is happening, without blocking the main thread.

GCD and async NSURLConnection

I know that if I create an NSURLConnection (standard async one), it will call back on the same thread. Currently this is on my main thread. (work fine too).
But i'm now using the same code for something else, and I need to keep my UI snappy....
If i do
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* and inside here, at some NSURLConnection is created */
});
.. is it possible that my NSURLConnection is created but my thread disappears before the url connection has returned?
I'm new to GCD. How would one keep the thread alive until my url connection returned, or is there a better way I could be doing this?
So really the issue isn't the lifetime of the thread on which your block runs, it's the fact that this particular thread is not going to have a runloop configured and running to receive any of the events coming back from the connection.
So how do you solve this? There are different options to think about. I can list a few, and I'm sure others will list more.
1 - You could use a synchronous connection here. One disadvantage is that you won't get callbacks for authentication, redirection, caching, etc. (All the normal disadvantages of synchronous connections.) Plus each connection will of course block a thread for some period of time, so if you're doing a lot of these then you could potentially have a few threads blocked at once, which is expensive.
2 - If your connection is simple and you are using iOS5 then you can use this method:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))
This will start an asynchronous connection and then allow you to specify a completion handler (for success or failure) and a NSOperationQueue on which you want that block to be scheduled.
Again, you have the disadvantages of not getting the callbacks you might need for authentication, caching, etc. But at least you don't have threads hanging around blocked by connections that are in flight.
3 - Another option for iOS5 is to set the queue for all delegate callbacks:
- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);
If you use this, then all of the delegate methods will be executed in the context of whatever NSOperationQueue you specify. So this is similar to option #2, expect that you get all of the delegate methods now to handle authentication, redirection, etc.
4 - You could set up your own thread that you control specifically for managing these connections. And in setting up that thread, you configure a runloop appropriately. This would work fine in iOS4 and 5 and obviously gives you all of the delegate callbacks that you want to handle
5 - You might think about what parts of your asynchronous connection handling are really interfering with your UI. Typically kicking off the connection or receiving delegate callbacks are not that expensive. The expensive (or indeterminate) cost is often in the processing of the data that you collect at the end. The question to ask here is are you really saving time by scheduling a block on some queue just to start an asynchronous connection that will go off immediately and do its thing on another thread anyway?
So you could just start the connection from the main thread, and receive all of the delegate callbacks on the main thread, and then in your implementation of those delegate methods fire off whatever expensive work you need to do on some other queue or thread.
So something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// go ahead and receive this message on the main thread
// but then turn around and fire off a block to do the real expensive work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Parse the data we've been collecting
});
}
Again, this is not comprehensive. There are many ways to handle this, depending on your specific needs here. But I hope these thoughts help.
Just as an answer to why your thread was disppearing (and for future reference) the NSURLConnection needs a runloop. If you had added
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
You'd see that the connection runs properly and the thread doesn't disappear untill the connection was completed.
First off, your block and every variable you use within it will get copied to GCD, so the code will not be executed on your thread but on the global queue.
If you want to get your data back on the main thread, you can nest an async call after your data has been fetched:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"www.stackoverflow.com"]];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error) {
// handle error
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the data
});
});
But why not use NSURLConnection's built in asynchronous support? You need an NSOperationQueue, but if you are doing alot of network fetches it is the way to go anyway:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"www.stackoverflow.com"]];
[NSURLConnection sendAsynchronousRequest:request
queue:self.queue // created at class init
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// do something with data or handle error
}];
Personally, I use a library like AFNetworking or ASIHTTPRequest to make networking even easier, which both support blocks (the former utilizes GCD and is a bit more modern).
dispatch_queue_t queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[btnCreateSmartList setEnabled:NO];
[dbSingleton() createEditableCopyOfDatabaseIfNeeded];
[dbSingleton() insert_SMART_PlaceList:txtListName.text :0:txtTravelType.text: [strgDuration intValue]:strgTemprature:Strgender:bimgdt];
[self Save_items];
//*********navigate new
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
[self performSelector:#selector(gonext_screen) withObject:nil afterDelay:0.0];
});
});

UITableView Refresh Data

I have a UITableViewController that when opened displays a table of the following object:
class {
NSString *stringVal;
int value;
}
However, whenever this controller opens, I want it to download the data from the internet and display "Connecting..." in the status bar and refresh the stringVal and value of all of the objects. I do this by refreshing the array in the UITableViewController. However, to do this the UI hangs sometimes or even displays "blank" table cells until the operation has ended. I'm doing this in an NSOperationQueue to download the data, but I'm wondering if there's a better way to refresh the data without those weird UI bugs.
EDIT:
the UI no longer displays blank cells. This was because cellForRowAtIndexPath was setting nil values for my cellText. However, it still seems somewhat laggy when tableView.reloadData is called even though I'm using NSOperationQueue.
EDIT2:
Moreover, I have two problems: 1. the scrolling prevents the UI from being updated and 2. when the scrolling does stop and the UI starts to update, it hangs a little bit. A perfect example of what I'm trying to do can be found in the native Mail app when you view a list of folders with their unread count. If you constantly scroll the tableview, the folders unread count will be updated without any hanging at all.
Based on your response in the question comments, it sounds like you are calling [tableView reloadData] from a background thread.
Do not do this. UIKit methods, unless otherwise specified, always need to be called from the main thread. Failing to do so can cause no end of problems, and you are probably seeing one of them.
EDIT: I misread your comment. It sounds like you are not updating the UI from a background thread. But my comments about the architecture (i.e. why are you updating in a background thread AFTER the download has finished?).
You state that "when the data comes back from the server, I call a background operation..." This sounds backwards. Normally you would have your NSURLConnection (or whatever you are using for the download) run on the background thread so as not to block to UI, then call out to the main thread to update the data model and refresh the UI. Alternatively, use an asynchronous NSURLConnection (which manages its own background thread/queue), e.g.:
[NSURLConnection sendAsynchronousRequest:(NSURLRequest *)
requestqueue:(NSOperationQueue *)queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler];
And just make sure to use [NSOperationQueue mainQueue] for the queue.
You can also use GCD, i.e., nested dispatch_async() calls (the outer to a background queue for handling a synchronous connection, the inner on the main queue to handle the connection response).
Finally, I will note that you in principle can update your data model on the background thread and just refresh the UI from the main thread. But this means that you need to take care to make your model code thread-safe, which you are likely to mess up at least a couple times. Since updating the model is probably not a time consuming step, I would just do it on the main thread too.
EDIT:
I am adding an example of how one might use GCD and synchronous requests to accomplish this. Clearly there are many ways to accomplish non-blocking URL requests, and I do not assert that this is the best one. It does, in my opinion, have the virtue of keeping all the code for processing a request in one place, making it easier to read.
The code has plenty of rough edges. For example, creating a custom dispatch queue is not generally necessary. It blindly assumes UTF-8 encoding of the returned web page. And none of the content (save the HTTP error description) is localized. But it does demonstrate how to run non-blocking requests and detect errors (both at the network and HTTP layers). Hope this is helpful.
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(netQueue,
^{
// We are on a background thread, so we won't block UI events (or, generally, the main run loop)
NSHTTPURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
// We are now back on the main thread
UIAlertView *alertView = [[UIAlertView alloc] init];
[alertView addButtonWithTitle:#"OK"];
if (data) {
if ([response statusCode] == 200) {
NSMutableString *body = [[NSMutableString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
[alertView setTitle:#"Success"];
[alertView setMessage:body];
}
else {
[alertView setTitle:#"HTTP Error"];
NSString *status = [NSHTTPURLResponse localizedStringForStatusCode:[response statusCode]];
[alertView setMessage:status];
}
}
else {
[alertView setTitle:#"Error"];
[alertView setMessage:#"Unable to load URL"];
}
[alertView show];
[alertView release];
});
});
dispatch_release(netQueue);
EDIT:
Oh, one more big rough edge. The above code assumes that any HTTP status code != 200 is an error. This is not necessarily the case, but handling this is beyond the scope of this question.