I use the following three delegate methods to pull images from S3. They are called in the presented order.
-(void)request:(AmazonServiceRequest *)request didReceiveResponse:(NSURLResponse *)response;
-(void)request:(AmazonServiceRequest *)request didReceiveData:(NSData *)data;
-(void)request:(AmazonServiceRequest *)request didCompleteWithResponse:(AmazonServiceResponse *)response;
I've noticed that if an image is large that sometimes the didReceieveResponse will get called before the previous didCompleteWithResponse has finished. This screws up the NSData received in the second delegate.
How do I fix this? All three are getting called in a for statement iteration for each iteration. like so:
for (NSString *name in array){
#try {
S3GetObjectRequest *gor = [[S3GetObjectRequest alloc]initWithKey:name withBucket:[Constants pictureBucket]];
[gor setDelegate:self];
[s3 getObject:gor];
}
I have not worked with S3 but I am sure that there would be some way of distinguishing the three different AmazonServiceRequest *request in the delegate functions(maybe the key you are using to init ). So you can take whatever action you are doing with the NSData by first making a check on the request you are getting back in the delegate functions
Related
hi in one of my application. i have to send a request to the server (json server) many times continously.my url will be like this as mention below
#"http://185.185.116.51/servername/serverjspfilesname.jsp?filterID=21&ticket=65675656565656567"
actually i have many filter id's (filter id you can find at top).in order to chnage the filterid continously i used for loop like this as mention below
for(int i=0;i<[appdelegate.listOfFiltersArray count];i++)
{
filtersDataModelObject=[[ListOfFiltersDataModel alloc]init];
filtersDataModelObject=[appdelegate.listOfFiltersArray objectAtIndex:i];
homescreenstring=[NSString stringWithFormat:#"http://%#/servername/serverjspfilesname.jsp?filterID=%#&ticket=%#",Ip,filtersDataModelObject.filterID,[storeData stringForKey:#"securityTicket"]];
NSLog(#"url is %#",homescreenstring);
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:homescreenstring]];
connection=[[NSURLConnection alloc]initWithRequest:request delegate:self];
if(connection)
{
homeScreenResponseData=[[NSMutableData alloc]init];
}
else
{
NSLog(#"connection failed");
}
}
actually after each condition is satisfied in for loop i have to connect with the server for getting the data from the server using nsurlconnection delegate methods. but here after complete execution of for loop only nsurlconnection delegate methods are executing with last filterid which is getting from the appdelegate.listOfFiltersArray array.
but i would like to call the server for each filterid.
if anyone know please let me know.thanks in advance.
Create one count variable int count in .h file.
int count = 0 //in .m file
Now use this method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//one request finished
count ++;
//request another using count
}
The solution that Prince proposed is not general as you will face the problem of defining the
"filterid" for each request.
And what you are doing is a bad approach. Dont mix the web request with your business logic code.The web stuff should be handled by a separate file handling all the requests throughout the app. And that class will implement delegation.
For delegation you need to do the following.
In your Network class header (Networkclass.h) add the protocol
#protocol NetworkControllerDelegate <NSObject>
-(void) UrlResponseRecieved :(NSData *)responseData;
-(void) UrlResponseFailed:(NSError *)error;
#end
and in NetworkClass.m (implementation fie) do the following
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (receivedData != nil)
{
if([delegate respondsToSelector:#selector(UrlResponseRecieved:)])
[self.delegate UrlResponseRecieved:receivedData];
[receivedData release];
receivedData = nil;
}
[connection release];
}
if you still get stuck you may refer to the following
What's wrong on following URLConnection?
or you may read any asynchronous downloading tutorial first.
I have a webservice that returning 20 results each time (it is a limitation of the service provider). I want to call this service 10-20 times repeatingly and update my UI each time.
Is there best practice for this situation? I do not want to block the ui while calling the server. This causes problems if the user want to perform actions while the action in progress
(like navigating away from the current page)
Thanks!!!
what you can do is call the webservice in a background thread, collect the required data and jump back to main thread and update the UI.
We are doing the above(i.e jumping from background thread to main thread) because it is not recommended to update any UI in the background process.
you can call you webService in background by using
[self performSelectorInBackground:#selector(MyWebService) withObject:nil];//you can pass any object if you have
and to come back on main thread when the background task is over you can do..
[self performSelectorOnMainThread:#selector(myMainFunction) withObject:nil waitUntilDone:YES];
you can change the last parameter i.e. waitUntilDone:No also. By doing this, user will not have to wait till the UI is updated. they can carry there task.
you can use NSTimer for periodic calling your webService.
hope that helped :)
It depends on how you want to display the information.
If you're using the asynchronous connection (in my opinion, more effective than calling a synchronous connection in the background) and its delegate, it should not block the user interface:
- (void)loadData {
NSString *urlString = #"http://www.stackoverflow.com";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
// delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// clear out or intialize instance data variable
[myData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// convert data to whatever it's supposed to be (for example, array)
NSString *dataString = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
NSArray *dataArray = [parser parseStringToArray:dataString];
[myArray addObjectsFromArray:dataArray];
//update tableview either using reload data (instant) or using updates (for smooth animation)
}
You can then recall the loadData method at the end of didFinishLoading: method to loop it.
I want to implement a classX that makes some asynchronous calls but in the eyes of the caller ClassC, classX should seem sequential.
ClassC would instantiate ClassX which makes some HTTP calls through NSURLConnection and thus, it becomes asynchronous (and I want it to stay async).
The logic flows like this:
ClassC:
- (void)callerWork
{
// shouldn't return until connection response completes
// and receives all of the response data
BOOL result = [classX doSomeWork];
}
ClassX:
- (void)doSomeWork
{
// With an NSURLRequest, initiates a NSURLConnection
// Only after all of the data is received (or error) from
// NSURLConnection via NSURLConnectionDelegate methods,
// return from this method
}
// Implements relevant NSURLConnectionDelegate methods
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{ }
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{ }
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{ }
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{ }
#end
Basically, I want to mimic how NSXMLParser works in that you invoke its "parse" method which doesn't return until all of the work is done. To process the data, NSXMLParser will call the relevant NSXMLParserDelegate methods, implemented by the calling class, like so:
- (void)parseWork
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataToParse];
[parser setDelegate:self];
// 'parse' method doesn't return right away - execution continues
// only after all of the data has been parsed, which means the delegate
// methods are getting called in the interim.
BOOL parseResult = [parser parse];
}
What's a good way to do this?
I know that you need to have one class serve as the delegate of the other for the callbacks
but with what mechanism do you make the '[parser parse]' not return right away?
You classX method could start another thread to do all the async work and sleep itself until the worker thread wakes it on completion. Don't call this method from the main thread though, as you don't want to sleep in the main UI thread.
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?