iPhone - May an async NSURLConnection HTTP response interfer with running process? - ios

I have a request :
[NSURLConnection connectionWithRequest:urlRequest delegate:self];
that is launched.
When I receive at any time :
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
I change the content of an array (remove or add items).
In another part of my program, I have to parse that array to work with its content.
So the question is : while I'm working with the content of the array ("for xxx in array" loop), may the response of the server (that can come at any time) cause the code of connectionDidFinishLoading change that array, and make the whole thing crash ?

Yes and it will cause your app to crash. You can either work with a copy of the array that is being modified by the NSURLConnection delegate method OR wait for the method to finish before iterating through the items in your array.
Maybe block that particular operation while there is an active NSURLConnection so the rest of your UI is still usable.

Related

iOS: How to check if #selector has been called and then continue on the for loop?

I have the following code below that loops through an array. I need to check if the finish or fail selector has been called iterating to the next object in my dataArray.
for (id object in dataArray) {
[client setDidFinishSelector:#selector(getDataFinish:)];
[client setDidFailSelector:#selector(getDataFail:)];
[client getData:object];
}
In my getDataFinish method I assign values and I am trying to keep it in order. If I use the above method, the values can get out of order since the client response time can be different for each request..
I see two possible solutions, depending on what you're actually trying to do. It sounds like you're making calls to the internet, so yes you will get varied response time (or no response at all). Because of this, I would recommend using NSNotification. See this answer for more information about that.
Another option is making a flag in your code (AKA a BOOL) that you set to YES when your method has completed. Again, if you're making calls to the web I would not recommend this method as you are setting yourself up for an infinite loop if the user has no service and the BOOL never changes.
If you are still having trouble let me know and I can provide a more detailed answer.

Why is there a delayed in establishing a connection to a database using NSURLConnection sendAsynchronousRequest?

excuse me for not knowing this, but I would like to know why there is a delay when I try to establish a connection to the database.
I am basically pulling data from a database to display the info back onto a UITableView but there seems to be a delay in the connection establishment.
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchFromDatabase];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
[self fetchFromDatabase];
NSLog(#"FOUR");
}
- (void)fetchFromDatabase
{
// The URL of the database
NSURL *url = [[NSURL alloc] initWithString:SDTET_DATABASE_SCHEDULE];
// Establish the connection to the database
[NSURLConnection sendAsynchronousRequest:[ [NSURLRequest alloc] initWithURL:url] queue:[ [NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"ONE !");
} ];
}
However, the output looks like so:
FOUR
FOUR
ONE !
Why doesn't ONE ! get printed out first?
How can I ensure the connection occurs before the numberOfSections is called, if possible?
Thanks
ONE gets printed last because you are calling an asynchronous method.
You can follow the flow of the program (which method calls which in what order). If we let our imagination stretch a bit, we could say that when a method is synchronous, it basically "says" to the next line of code to wait for it (the method) to finish.
However, if a method is asynchronous, it say "don't wait for me". The URL request method you are using is asynchronous. That means it starts to do its job but nobody waits, the execution of code continues in the next line immediately.
There is a piece in the puzzle that will clarify this more. The request method needs to be fed a completion block as a parameter. Explaining blocks is out of scope here, but you need to understand what block is to fully grasp the solution to your problem.
Suffice to say, once this asynchronous method finishes its job (which can be a long time and this is one of the reasons for asynchronous approach really), the block is invoked and executed.
So to answer your last question, you need to trigger a refresh on your table view, which you achieve by calling
[self.tableView reloadData]; inside the block. There is a nice logic here, since you know the block is executed ONLY after the asynchronous method concludes its work, you will refresh your table view knowing you have the updated data from db.
It's because it is an asynchronous request (meaning it doesn't necessarily happen immediately). It's a huge principle of computer science and it a much bigger concept than just Objective-C or iOS development.
In your case, you can get rid of the fetch being called in numberOfSectionsInTableView: and you can just call [self.tableView reloadData] in your completionHandler.
There is a delay because network access is slow. Sometimes it's REALLY slow.
Sometimes you can get a response in less than a second, and other times it can take the better part of a minute to get your response.
You should always write your network code to be asynchronous, which means that you submit the request with a method that returns immediately. Your program continues to run, and then you get notified once the response has been received.
That's what is going on with your code.
You don't want to send the request to load your data in numberOfSectionsInTableView. You should send the network request as early as possible, preferably before the view controller that will display the results is displayed.
However, it's pretty common to display a table view before you have the data to fill it. You might show an empty table view with a message that the data is loading, or with a progress dialog. In other cases you might know how many cells to display, and have the text data but not the images. In that case you might populate your cells with placeholder images. Once the data loads you can update the display.
In your case you should write your numberOfSectionsInTableView and numberOfRowsInSection: methods to return zero, and then make the completion method of your network call tell the table view to reload itself.

Setting NSOutputStream to be synchronous

I know there are few questions similar to this one, but in all cases the answer is to make it asynchronous.
According to the apple documentation (even though it is not recommended) polling is an available option. However, I just couldn't implement it.
Should probably mention I am doing it in c# using Xamarin, but if you
can give me an answer on how to do this in Objective-C that would be
good too.
Here is my attempt
while(true)
{
if (outStream.HasSpaceAvailable())
{
int output = ((NSOutputStream)outStream).Write(data, (uint)data.Count());
}
}
The problem here is that outStream.HasSpaceAvailable() is false indefinitely.
Reason why I want to do it synchronously:
Currently (in a test app) I am doing it asynchronously and it works for sending one stream of data per call to the method. However in the real App I will need to send lots of small data packets one after the other. Therefore, I need to wait for the data to be sent before I can send another packet.
If I try putting many calls to the delegate the data keeps overwriting the previous call...
It would be great if you could let me know how to do it synchronously first (for the sake of having one answer out there on it). If you think there is a better way to do it in an async way let me know too.
Thanks
EDIT :
Here is how I set up the session
SESSION=new EASession (Accessory, Protocol);
NSStreamStatus outputStream= SESSION.OutputSream;
outputStream.Open();
This is done when a button is pressed and before the while loop above (obviously).

NSURLConnection connectionDidFinishLoading Not Called Randomly

I have an iPad application where I am making asynchronous calls using NSURLConnection. In some cases I receive all the response data in connection:didReceiveData:, but connectionDidFinishLoading is never called. There is no error. This is somewhat random, because the same responses do complete at other times.
The way that my class works is that around 20 requests are sent in a row using:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
And then I just wait for them to come back. Is this a valid way to create multiple requests?
Here is a sample header of a response that did not complete. It is indistinguishable from the header of a response that did complete.
>Keep-Alive: timeout=5, max=100
>Transfer-Encoding: Identity
>Server: Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.8b mod_jk/1.2.26
>Content-Type: application/json;charset=UTF-8
>Connection: Keep-Alive
>Date: Wed, 03 Apr 2013 05:25:32 GMT
>Cache-Control: private, no-transform, max-age=600
One weird symptom of the problem is that I started checking the expected content length using:
long long download_size =[response expectedContentLength];
The Content-Length is never set in the http header. When a request fails the download_size is -1 (expected), when the same request does not fail the download_size is set to some number. However, there are many cases where the download_size is not set and the response does not fail.
This is not a great way to initiate 20 requests because:
The requests operate concurrently, and thus if you don't have some class that encapsulates all of the response data and the like from the individual requests, your app can be confusing the response data from various requests;
Because they happen concurrently and because iOS only allows five concurrent requests to a given server, it will delay (and possibly timeout) the others.
You have a bunch of different approaches, but you probably:
You might want to be doing this network stuff on a background queue;
If you want concurrent operations (and there's an observable performance benefit from doing so), you could use NSOperationQueue to have concurrent operations, but limit how many concurrent operations are ongoing (to 4 or 5) with the use of maxConcurrentOperationCount. This process is trivial if you're using synchronous network operations in these background operations, but surprisingly complicated if you use asynchronous network operations with your own delegate methods in the background queue, though.
If you really need (a) to use the asynchronous network calls using your own delegate methods (e.g. updating progress views, streaming protocols, etc.); and (b) you want to enjoy concurrent operations, it will be considerably easier to use AFNetworking than it will be to write your own. I went through this exercise of writing my own, but having done that exercise once, I now more fully appreciate what AFNetworking brings to the table.
Sure, you could get around all of this by managing your own array of pending network requests, initiate the first one, and have the connectionDidFinishLoading and/or didFailWithError kick off the next one in your queue, but you lose the performance gain of concurrency. But it is one simple work-around.
I do hope you are use separate objects here. You are saying
NSURLConnection *connection =
[[NSURLConnection alloc] initWithRequest:request
delegate:self startImmediately:YES];
Well, I sure hope those are 20 different self objects. You must not expect this to work with a single object acting as the delegate for all 20 requests simultaneously!

How to read the response headers every time using AFNetworking?

While using a 3rd party API, I have the requirement to cancel all traffic when a custom response header is set to a certain value. I am trying to find a nice place to do this check only once in my code (and not in every success/failure block, where it works fine). From what I understand, this could be done by overriding -(void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation in my custom AFHTTPClient subclass, but when I implement it like that:
-(void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation
{
NSLog(#"[REQUEST URL]\n%#\n", [operation.request.URL description]);
NSLog(#"[RESPONSE HEADERS]\n%#\n", [[operation.response allHeaderFields] descriptionInStringsFileFormat]);
[super enqueueHTTPRequestOperation:operation];
}
the response headers are nil. Can anybody help me with that?
At the moment when operations are being created and enqueued in AFHTTPClient, they will not have the response from the server--that will be assigned when the request operation is actually executed.
Although the requirement to cancel all traffic seems unorthodox (at least if outside of the conventions of HTTP), this is easy to accomplish:
In your AFHTTPClient subclass, add a BOOL property that stores if requests should be prevented, and then used in enqueueHTTPRequestOperation. Then, override HTTPRequestOperationWithRequest:success:failure: to execute the specified success block along with some logic to set the aforementioned property if the salient response is present.

Resources