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>
Related
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.
I have pickerview which is showing times in minuts like (5,10,20). If user will select any of the time he will get updates after selected time. And my application will run as usual but after 5 minutes a message will show some updates occured.
How I will implement this concept? What coding should I do?
As per my guess should I go with threading?
You could have a NSTimer which starts when you choose a time from your pickerview.
timer = [NSTimer scheduledTimerWithTimeInterval:pickerValueInSeconds target:self selector:#selector(updateMethod) userInfo:nil repeats:NO];
You choose no repeat, then in your updateMethod you make a request:
-(void)updateMethod
{
NSString *url = #"url...";
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:someURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:20.0]; // with some timeout interval.
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if(theConnection)
{
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
systemsData = [[NSMutableData data] retain];
}
else
{
// Inform the user that the connection failed.
NSLog(#"NSURLConnection failed!");
}
}
Now in your
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
and
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
you take care of the data or error and then start a new timer..
Note that you also need to implement
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[systemsData setLength:0];
}
and
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[systemsData appendData:data];
}
You can use a NSTimer.
You can read more about NSTimer here on stackoverflow or on the docs.
You can create a NSTimer and wait for the selected duration. Once timeout is completed
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
the message is fired to the selector. There are plenty of sample codes on google.
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.
I am trying to speed up my application download speed. I used Asynchronous NSURLConnection to download contents from the server, it was working fine with one connection.
I use the code from this post to implement multiple delegate objects. Multiple NSURLConnection delegates in Objective-C
When I created 2 NSURLConnection objects, each one is trying to download different files.
The callback didReceiveData routine was called but the it only received data of the first NSURLConnection object until the first connection was done then it started to receive the data from the second NSURLConnection. I want these two connections to receive data at the same time,what should I do? Here is my current code.
-(IBAction) startDownloadClicked :(id) sender
{
while (bDownloading)
{
int nCurrentCon = 0;
while (nCurrentCon < 2)
{
[self downloadAFile:[filenameArray objectAtIndex:nCurrentCon]];
nCurrentCon++;
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
}
- (void) downloadAFile: (NSString*) filename
{
NSString* urlstr = #"ftp://myftpusername:password#hostname";
NSURLRequest* myreq = [NSURLRequest requestWithURL:[NSURL URLWithString:urlstr]];
DownloadDelegate* dd = [[DownloadDelegate alloc] init]; //create delegate object
MyURLConnection* myConnection = [[MyURLConnection alloc] initWithRequest:myreq delegate:dd
startImmediately:YES];
}
Then in my Delegate Object, I implemented these routines
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receiveBuffer setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"receiving data for %#", targetFileName); //the file name were set when this delegate object is initialized.
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Download Failed with Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"File %# - downloaded.", targetFileName);
}
Your code looks okay. I have a similar setup that works successfully (although there seems to be a limit of four concurrent conections).
The main difference between your and my code is that you use FTP while I use HTTP. Why don't you try it with HTTP connections just to see whether you have run into a restriction of FTP connections on the iPhone?
The connection doesn't respond when beeing set from a new thread:
Code 1 (responds fine):
[self setConnection];
}
- (void)setConnection{
NSLog(#"setting myConnection with request");
myConnection = [[[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:requestURL] delegate:self] autorelease];
}
Log 1:
2010-02-25 10:44:04.384 Untitled[1002:207] setting myConnection with request
2010-02-25 10:44:06.093 Untitled[1002:207] didReceiveResponse
2010-02-25 10:44:06.094 Untitled[1002:207] didReceiveData
2010-02-25 10:44:06.094 Untitled[1002:207] DidFinishLoading
Code 2:
[NSThread detachNewThreadSelector:#selector(setConnection) toTarget:self withObject:nil];
}
- (void)setConnection{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"setting myConnection with request");
myConnection = [[[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:requestURL] delegate:self] autorelease];
[pool release];
}
Log 2:
2010-02-25 10:40:50.280 Untitled[972:4003] setting myConnection with request
Delegates:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveResponse");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"didReceiveData");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"DidFinishLoading");
}
Why is that?
What is the correct way for sending a request & receiving a response- without freezing the main thread / UI.
From the NSURLConnection documentation:
For the connection to work correctly
the calling thread’s run loop must be
operating in the default run loop
mode.
and
Note that these delegate methods will
be called on the thread that started
the asynchronous load operation for
the associated NSURLConnection object.
In your case, what probably happen is the thread is finished before your class actually executed all it’s code.
Read this for a solution on how to start the run loop:
http://www.depl0y.com/2009/02/20/nsurlconnection-in-its-own-thread/
Or, create a second thread and avoid issues of runloops altogether by doing a NSURLConnection sendSynchronousRequest:returningResponse:error, for which no special threading or run loop configuration is necessary in the calling thread.
Do not forget to call back to the main thread via performSelectorInMainThread:
It doesn't work because the thread is finished before your class actually executed all it’s code.
All you need to do now is start the run loop so the thread doesn't exit and the download can work.
[[NSRunLoop currentRunLoop] run];
You can see this mini tuto here: NSURLConnection in it's own thread