NSURLConnection caching and receiving data - iphone

Quick question: On the method
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
from the NSURLConnectionDataDelegate, if I don't append the data given by the method on my NSData, will it be lost? or does the connection save it somewhere as well?

No, it's not saved anywhere. If you want to keep it, you must do so yourself.

typically this method is where you would keep track of your data, with an NSMutableData instance variable:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[myData appendData:data];
}

Related

NSURLConnectionDownloadDelegate method not getting called

i m working on a project that connect to server and download data from it. i like to support resume download if connection gets interrupt. my approach is to save the downloaded portion of data to a destination file; and if connection gets interrupted, i want to mark the downloaded portion using connection:didWriteData:totalBytesWritten:expectedTotalBytes and later resume from the stopped portion with server.
my code:
- (IBAction)connectToServer:(UIButton *)sender
{
// setup url and send request to server
NSURL *url = [NSURL URLWithString:BASED_URL];
self.urlRequest = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
self.urlConnection = [[NSURLConnection alloc] initWithRequest:self.urlRequest delegate:self];
// start receive data if connection established
if (self.urlConnection){
self.receivedData = [NSMutableData data];
NSLog(#"starting to receive data");
} else {
// handle error
NSLog(#"failed to connect to server");
}
}
- (void)doSomethingWithData
{
// handle data here
}
#pragma NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// received data
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// error connection
NSLog(#"connection failed");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Data receiving succeed, received: %d bytes of data", [self.receivedData length]);
}
- (void)connection:(NSURLConnection *)connection didWriteData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long)expectedTotalBytes
{
NSLog(#"not getting called");
}
my question is how come the "connection:didWriteData:totalBytesWritten:expectedTotalBytes" method never get called?
thanks so much!
chris
Have u added this in .h file:
#interface yourViewContoller : UIViewController <NSURLConnectionDataDelegate, NSURLConnectionDelegate>
According to Apple's documentation:
The NSURLConnectionDownloadDelegate protocol describes methods that should be implemented by the delegate of instances of NSURLConnection created using Newsstand Kit’s downloadWithDelegate: method.
Also, it says:
If you are using NSURLConnection directly, your delegate class should instead implement the methods defined in the NSURLConnectionDataDelegate protocol.

Requesting Data from a web server on iOS device

Currently, the way our app works is we download a text file that describes the information we want. Then we use NSURLConnection and request that one packet that the text file describes. Then we ask for the next packet, wait for it to come in, check it, then ask for the next packet.
Is there a better way to do this without the text file? I feel like I should be able to have the app say, "InformationForJohnDoe" and then the server will just start sending all the packets for JohnDoe, but in this scenario, I don't know how I'd know which data is which in my connectionDidFinishLoading delegate method.
The web service implementation looks like this:
[WebGet(UriTemplate = "GetTestData/{file}")]
public Stream GetTestData(string file)
{
string fileName =
"\\testdirectory" + file;
if (File.Exists(fileName))
{
FileStream stream = File.OpenRead(fileName);
if (WebOperationContext.Current != null)
{
WebOperationContext.Current.OutgoingResponse.ContentType = "text/.txt";
}
return stream;
}
return null;
}
I'm not much of a C# person. I've only just started in C# and web services.
the Idea is that you append to a NSData you define in the class the NSData you get from the
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
method.
Then, in the
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
method, you know that all data is completely loaded and ready for use.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Connection didReceiveData of length: %u", data.length);
[self.dataData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do seomething with self.dataData
}

Why NSURLConnection blocks my UI?

I've been reading several threads and questions about this issue but I didn't find the solution.
I have some asynchronous calls performed with
[NSURLConnection connectionWithRequest:anURLRequest delegate:self];
The problem is that I want the interface to be operative but it is blocked until the connection is finished.
Is this solved launching another thread? Where is exactly the problem?
EDIT
Ok, after retrieve data I parse it with NSXMLParser, that do it synchronously and blocks main thread. Is this correct? Then, maybe I need to parse in another thread. Anyone has a guide?
From the docs:
Messages to the delegate will be sent on the thread that calls this method. For the connection to work correctly the calling thread’s run loop must be operating in the default run loop mode.
Are you sure that this code is being called on a run loop in default mode and not from a thread created by yourself with a different type of run loop mode?
The UI should not be locking up when you use connectionWithRequest. Try creating a label in your UI and have your connection update it with the current amount of data, like so:
- (void)downloadContentFromUrl:(NSURL *)url {
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
receivedData = [[NSMutableData data] retain];
self.downloadProgressLabel.text = #"Downloading...";
} else {
// oh noes!
}
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[receivedData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
int kb = [receivedData length] / 1024;
self.downloadProgressLabel.text = [NSString stringWithFormat:#"Downloaded\n%d kB", kb];
}
connectionWithRequest does indeed run in it's own thread - no need for you to worry about this. In fact it must be started from the main thread. Check out the NSUrlConnection doc for more info.
+ (id)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate];
This method should create an asynchronous-request (that means that it runs in the background and it doesn't block the UI). You should check if there's another class/method in your file that blocks the UI (for example NSData's '+ (NSData *)dataWithContentsOfURL:(NSURL *)URL').
I don't know if it could help anyone, but I've the same problem (asynchronous URL request blocking the UI) but it was due to:
NSLog(#"dataReceived: %#", data);
in the connectionDidReceiveData method.
In my case I was trying to update the UIProgressView.progress property. I calculated the new value like that
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.progress setProgress:self.downloadedData.length / self.fileSize ];
[self.downloadedData appendData:data];
}
Which doesn't work, I replaced this snippet of code with
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
self.downloadedSize += data.length;
[self.progress setProgress:self.downloadedSize / self.fileSize ];
[self.downloadedData appendData:data];
}
And now the progress view updates with no problem.

NSURLConnection doesn't call delegate methods

I saw similar questions here, but I couldn't find solution to my problem.
I have a simple NSURLConnection in main thread (At least I didn't create any other threads), but my delegate methods aren't get called
[[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
and no methods called, e.g.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"didReceiveResponse");
}
self is also a delegate for NSXMLParser, but I think it should not be a problem, as I have this working in my other class in the same project. I checked everything 10 times already, but can't find any problem.
I've seen some hack to solve it here: http://www.depl0y.com/?p=345 but I don't like it, May be someone knows better solution? thanks
The only reason I know is a separate thread (that is already terminated when the delegate methods are called).
Try to NSLog(#"Is%# main thread", ([NSThread isMainThread] ? #"" : #" NOT"));right before the url connection creation
Try running your connection on main thread:
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[connection start];
The autorelease is dangerous. The calls to the delegate are made after your function returns (asynchronously). Are you retaining it somewhere else?
You have to release the NSURLConnection object in the - (void)connectionDidFinishLoading:(NSURLConnection *)connection callback as pointed out in the Apple documentation, not elsewhere:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Do whatever you want here
// Release the connection
[connection release];
}
Don't release it with autorelease, as Lou Franco suggested.
If it is not the problem, then maybe you have to implement all the required methods in the delegate class:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
The delegate is retained by NSURLConnection so you don't have to worry about it.
I think you may have missed NSURLConnectionDelegate in your class header file.
For example:
#interface yourClass : NSObject <NSURLConnectionDelegate>

UIProgressbar in Cococa Touch

I want to display an UIProgressbar depending on the amount of data downloaded from the server.
One way I implemented is I made a NSURLConnection and set the delegate.
The - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
gave me the expectedContentLengh
And in the - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
I am getting the data part by part every time until all the data are downloaded.
But this did not solve my problem.
Is there any other way to do this ?
All your suggestions are appreciated.
Thanks
If you know the expected content length, just keep a running total of how much you've received so far divided by the total amount you'll get:
// These should be properties of the delegate class
UIProgressView * progressView;
long long expected;
long long gotSoFar;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
gotSoFar += data.length;
progressView.progress = gotSofar / expected;
}
If you get type warnings on the division, cast each long long before doing the division.