I've been trying to load images from a url using ASIHTTPRequest but I always come up with a blank UIImage. I think it might have something to do with iOS automatically choosing the #2x named version of images or vica versa.
[ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];
NSString *url_string = [NSString stringWithFormat:#"http://173.246.100.185/%#", [eventDictionary objectForKey:kEventDescriptionImageURLKey]];
NSURL *url = [NSURL URLWithString:url_string];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[request setSecondsToCache:86400];
[request setDelegate:self];
[request setCompletionBlock:^{
NSLog(#"Successful Update");
[self makeAssignment];
}];
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"%#", [error localizedDescription]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Update Failed"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}];
[request startAsynchronous];
NSLog(#"%#", url_string);
The makeAssignment method is below.
NSString *url_string = [NSString stringWithFormat:#"http://173.246.100.185/%#", [eventDictionary objectForKey:kEventDescriptionImageURLKey]];
NSURL *url = [NSURL URLWithString:url_string];
downloadedImage = [[UIImage alloc] initWithContentsOfFile:[[ASIDownloadCache sharedCache] pathToCachedResponseDataForURL:url]];
NSLog(#"%#", downloadedImage);
NSLog(#"%#", [[ASIDownloadCache sharedCache] pathToCachedResponseDataForURL:url]);
Nothing I do, including naming images #2x on the server or providing both versions, gets it to load. Any ideas? Has anyone done this before? When I load them locally (from within the package) I don't have any issues.
Thanks!
EDIT
Here's the log output
2011-03-19 11:46:11.088 clv[82974:207] Successful Update
2011-03-19 11:46:12.822 clv[82974:207] http://173.246.100.185/ying_yang_concert#2x.png
2011-03-19 11:46:12.844 clv[82974:207] >
2011-03-19 11:46:12.913 clv[82974:207] Successful Update
2011-03-19 11:46:12.932 clv[82974:207]
2011-03-19 11:46:12.932 clv[82974:207] /Users/jonathantpage/Library/Application Support/iPhone Simulator/4.3/Applications/A17C0938-D2ED-447C-BD17-94726C5E5A66/Library/Caches/ASIHTTPRequestCache/PermanentStore/FE05295C8CD7687DC7A505C9070B6FC7.png
It won't be the automatic #2x thing - if the system can't find an image with '#2x' appended to it it will simply use the original image and scale it up. If you want to verify that, just run the app on the simulator using the non-retina display mode.
If you run your app on the simulator you can actually browse the cache directory using the finder to verify your images are there as you expect. In your user directory, you would go to ~/Library/Application Support/iPhone Simulator/[Your SDK version]/Applications/[Your app]/ to get to your app sandbox, and form there probably into the app's library/cache directory.
Also, you log the cache path: what does it come out as? Are you sure it's a valid path? Again, if you're on the simulator you can quickly verify this by trying to open the path in the terminal/another app.
Nothing in your code looks particularly wrong to me at least, so it feels like there's something happening with the local file path. Perhaps you could post the NSLogs you're getting to the console.
So it turns out that everything was fine. What was wrong is that the completion block was being executed after the assignment to the UIImageView. By placing that in the makeAssignment method everything works. Previously I was just populating the UIImage there and then assigning that to the UIImageView in the main thread.
Related
I use AFImageRequestOperation to download some icons, meanwhile use SDWebImage to download some pics for main view. Each AFImageRequestOperation is added to my publicOperationQueue defined in app delegate, its maxConcurrentOperationCount is set to 10. Strange thing is that sometimes one or two of my 10+ icons will be replaced by some pic in main view, which should be downloaded by SDWebImage. And when I set a larger maxConcurrentOperationCount which is bigger than my icon counts, it works fine. I doubt if it has something to do with multiple NSOperationQueues sharing some resources and maxConcurrentOperationCount. Any one could help?
//below is the icon downloading code
//============================//
for(NSString *url in picUrls)
{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[urlRequest setHTTPShouldHandleCookies:NO];
[urlRequest addValue:#"image/*" forHTTPHeaderField:#"Accept"];
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
NSString *imageName = trimNilOrNuLL([url lastPathComponent]);
if(imageName.length > 0)
{
NSData *imageData = UIImagePNGRepresentation(responseObject);
[imageData writeToFile:[path stringByAppendingPathComponent:imageName] atomically:YES];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"%#",error);
}];
[[AppShare appDelegate].publicOperationQueue addOperation:requestOperation];
}
//============================//
and for SDWebImage, I use - (void)setImageWithURL:(NSURL *)url method in UIImageView+WebCache category to download pic
OK,From the question found that you are using AFNetworking .Then for image downloading why dont you use the UIImageView Extention of AFNetworking?
You dont have to implement any queue or anything for this
some code like this is enough i think
[self.imageview setImageWithURL:url placeholderImage:nil];
I am trying to use the ASIDownloadCache from the ASIHTTPRequest library. I think I have it almost set up but the data I am printing to the log is a bunch of numbers.. I think it might be a formatting problem.. but I would like to run it past someone with more experience first to make sure I'm doing it correctly and then to hopefully help me fix the issue.
The code belows shows you how I am setting up my cache, I am using this view for several data sets, hence the need to use an if statement so that I am only setting up the cache on specific data.
- (IBAction)setRequestString:(NSString *)string
{
//Set database address
NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:#"http://***.***.***.***:8888/codeData/"]; // iphone development
//PHP file name is being set from the parent view
[databaseURL appendString:string];
//call ASIHTTP delegates (Used to connect to database)
NSURL *url = [NSURL URLWithString:databaseURL];
checkDataSet = [[NSString alloc] initWithFormat:string]; //Loads ICMfg.xml into checkDataSet for setting up cache
//Create If statments here
if ([checkDataSet isEqualToString:#"ICMfg.xml"]) {
//Cache stuff goes in here
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];
[request setSecondsToCache:60*60*24*30]; // Cache for 30 days
[request setDelegate:self]; // A delegate must be specified
[request startSynchronous];
//[request setDidFinishSelector:#selector(requestFinished:)]; // And an appropriate
}
else
{
//this else statments lets all of the other datasets come through here
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
}
From here, when [checkDataSet isEqualToString:#"ICMfg.xml"] is true it will set the cache parameters and then calls the following method where I get everything ready to parse my information
- (void)requestFinished:(ASIHTTPRequest *)request
{
if ([checkDataSet isEqualToString:#"ICMfg.xml"]) {
BOOL success = [request didUseCachedResponse];
NSLog(#"------------>>>>>>> Success is %#\n", (success ? #"YES" : #"NO"));
responseString = [request responseString];
capturedResponseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#", capturedResponseData); //this prints out the weird data.
[self startTheParsingProcess:capturedResponseData];
}
else
{
responseString = [request responseString]; //Pass requested text from server over to NSString
capturedResponseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
[self startTheParsingProcess:capturedResponseData];
}
}
From here, I check my nslog to see the result of that NSlog and it spits out a bunch of numbers, below is a small section of the output. The next step for me is to check to see if anything is actually being parsed.. and also to see if the cache is working or not.. then I need to figure out hopefully with your help how to format the data correctly if thats my main problem..
also I would like to ask how to get this working asynchronously as currently I can only get it to work synchonosly.
2011-11-09 09:29:55.216 code[3968:207] ------------>>>>>>> Success is YES
2011-11-09 09:29:55.239 code[3968:207] <3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0d0a 3c494345 6e673e3c 52657375 6c742044 42566572 73696f6e 3d223132 33223e3c 5461626c 65733e3c 5461626c 65205461 626c654e 616d653d 2249434d 6667223e 3c526f77 733e3c52 6f77204d 414e5546 41435455 52455249 443d2237 30362220 4d414e55 46414354 55524552 3d22412d 445a4722 2049534c 4f434b4d 414e5546 41435455 5245523d 22462220 49535645 4849434c 453d2246 223e3c2f 526f773e 3c526f77 204d414e 55464143 54555245 5249443d 22333138 22204d41 4e554641 43545552 45523d22 412e522e 452e2220 49534c4f 434b4d41 4e554641 43545552 45523d22 46222049
any help would be greatly appreciated.
I don't see anything that immediately sticks out in your code as wrong.
The NSLog() is printing an NSData object, which is binary data so the hexadecimal numbers you are seeing are the representations of the bytes which is exactly what you would expect.
The NSData Class Reference:
description
Returns an NSString object that contains a hexadecimal
representation of the receiver’s contents.
(NSString *)description
Return Value
An NSString object that contains a hexadecimal representation of the receiver’s contents in
NSData property list format.
If you want to print out the string representation of this data, use:
NSString *capturedResponseString = [NSString stringWithUTF8String:[capturedResponseData bytes]];
I have a tested and working php script that pulls from a db and returns as an XML.
I have used ASIHTTPRequest to make a POST to the php script successfully.
I have looked for online help to get this to work. From what I understand, all the code is solid. It compiles properly, runs without problem, even makes the request without problem. I get no errors.
Since I have thoroughly tested and successfully used the php, I assume the break is on the iPhone side.
I do not understand why it does not download the XML file to the specified location.
Any insight would be greatly appreciated.
// Create the request
NSURL *url=[[NSURL alloc]initWithString:#"http://localhost:8888/Project/script.php"];
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url]retain];
// gather *ALL* data present
if ( [txtEmail.text length] > 0 )
[request setPostValue:txtEmail.text forKey:#"email"];
if ( [txtFName.text length] > 0 )
[request setPostValue:txtFName.text forKey:#"firstname"];
if ( [txtID.text length] > 0 )
[request setPostValue:txtID.text forKey:#"id"];
if ( [txtLName.text length] > 0 )
[request setPostValue:txtLName.text forKey:#"lastname"];
[request setDidFailSelector:#selector(requestFailed:)];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDownloadDestinationPath:#"/Library/Project/dbInfo.xml"];
[request setDelegate:self];
[request startSynchronous];
[request release];
[url release];
a few points on your code:
Why are you using a POST request to download a file? usually downloading is done via a GET request (if we're talking REST). You could use a simple ASIHTTPRequest + adding your post values as URL-Parameters (+ handle it on the php side accordingly).
you are allocating and retaining your ASIFormDataRequest instance, but only releasing it once -> results in a memory leak. (for every alloc/copy/retain -> release it).
To answer you question:
i think the downloadDestinationPath is wrong (the application has no permissions to write there).
Better:
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [docDir stringByAppendingPathComponent:#"dbInfo.xml"];
[request setDownloadDestinationPath:path];
Now your "dbInfo.xml" File is saved in the documents folder of your application.
If you're running your app in the simulator, you can find the directory at:
~/Library/Application Support/iPhone Simulator/IOS_SDK_VERSION/Applications/APP_UID/Documents
And:
if you implement the downloadProgressDelegate: request:didReceiveBytes: method, you can check if you're actually receiving data:
-(void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes {
NSLog(#"received bytes ..") ;
}
I m using sendSynchronousRequest to get the data from the server. I know that synchronous will wait until the data received for that request.
But the problem comes when user by mistake enters some non-existing url and than tries to get response. In this case, if user goes in to background and than comes into foreground it shows only black screen. It only shows status bar. Also its not showing any background application. I have to press Home button to come out of my application.
On simulator, After 1+ minute it shows me the message that "Request time out" (No crash).
On Device, within 1 min application get crashes.
Any suggestion. Any Help. This is really a serious issue in my app.
Thanks.
Just like Julien said, the watchdog is killing your app. To answer some questions:
why does this happen only on the simulator?
Because when you're debugging the watchdog leaves your app alone, it can take time.
why does this happen only when the user enters a wrong url?
Because of the system timeout, the system will keep trying for 60 secs if it can't find a server.
so the problem is synchronous vs asynchronous?
No, the problem is the thread, you can do the same operation in a background thread, just don't do it on the main thread and the watchdog will leave you alone.
why is the screen black when the app comes up?
Remember, you are making blocking stuff on the main thread, the thread that draws...
Hope that was all. Let me know if I missed something.
Why not setting a timeout for your connection?
NSString *urlString = TEST_CONNECTION;
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSURLRequest *request = [NSURLRequest
requestWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5.0];
NSData *conn = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
This should release the synchronous waiting after a number of seconds, which should solve your problem without going with an asynchronous call (which sometimes isn't the proper solution)
I know this works properly because this is how I check if I am connected to a certain VPN (where reachability flags totally fail).
you should take a look to this article: https://developer.apple.com/library/ios/#qa/qa1693/_index.html
iOs contains a watchdog, if your application is blocked to much time on an operation on the main thread, this one will be killed. (for more details about Watchdog: http://en.wikipedia.org/wiki/Watchdog_timer)
So if you want to download something, don't download it on the main thread.
RELATE
UIImage *image = [self.imgCache objectForKey:urlString];
if(!image){
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSLog(#"%#",response);
UIImage *img = [UIImage imageWithData:data];
//
if(img)
{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.imgCache setObject:img forKey:urlString];
completionBlock(img);
});
}
});
}
else{
completionBlock(image);
}
use ASIHttpRequest class instead of NSURLConnection , its nothing but wrapper around NSURLConnection and has very simple callbacks , you can also set time to complete a request. Please go through this link for more info http://allseeing-i.com/ASIHTTPRequest/
I think you first have to test user data whether it is correct or not and than only if it is correct, sends the request otherwise prompt user that "please enter correct data"...
or
when your parsing of data in response failed. You can also make protocol delegate method i.e FinishWithError so that you come up with your last UI.
Try this one:
#import "ASIHTTPRequest.h"
//In a method
[self performSelectorInBackground:#selector(DownLoadImageInBackground:) withObject:imgUrlArr];
-(void) DownLoadImageInBackground:(NSArray *)imgUrlArr1
{
NSURL * url = [Image URL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"URL Fail : %#",request.url);
NSError *error = [request error];
// you can give here alert too..
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
UIImage *imgInBackground = [[UIImage alloc]
initWithData:responseData];
[imageView setImage: imgInBackground];
}
This might help you: I am also loading a number of images at one time, so images that have no proper data show a black screen. To avoid this, try to resize your imageview.
You could check the reachability of the URL before starting the request.
Apple has Reachability Methods to do so. But its easier to use a wrapper. E.g. ASIReachability.
I think the application crashing because you does not get any data when user enters wrong URL and you are using this 'returned' nil NSData to do stuffs.
I think this will fix your problem
NSData *data=[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if(data!=nil){
///
} else {
NSLog(#"NO DATA");
}
I wonder how I can check if a file exist on a server or not, without downloading the data first.
I have around 30 different objects and some of them is connected to a movie on a server. At the moment I use NSData to control if the the URL exist, and then shows the movie, or if it doesn't and then alerts the user that there is no video for that object. The code I use for the moment:
NSString *fPath = [[NSString alloc] initWithFormat:#"http://www.myserver/%#", [rows idNr]];
NSURL *videoURL = [NSURL URLWithString:fPath];
NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
url = [NSURL URLWithString:fPath];
[fPath release];
if (videoData) {
[self performSelectorOnMainThread:#selector(playVideo:) withObject:url waitUntilDone:NO];
} else {
NSLog(#"videodata false");
errorLabel.hidden = NO;
activityView.hidden = YES;
}
"rows idNr" is the name of the object. This method is doing what I want, but the problem is that with NSData it first "downloading" the file, and when the URL is validated as a file, the movie is loading once again in the movieplayer. This means that it takes twice as long to load the file.
Suggestions?
It took me a while to dig out my answer to one of the previous questions on this topic. Quote:
You can use a NSMutableURLRequest to send a HTTP HEAD request
(there’s a method called setHTTPMethod). You’ll get the same
response headers as with GET, but you won’t have to download the whole
resource body. And if you want to get the data synchronously, use the
sendSynchronousRequest… method of NSURLConnection.
This way you’ll know if the file exists and won’t download it all if it does.
Make an URLConnecion object with desired url request and add NSURLConnectionDelegate into .h file like I want to check "yoururl" is exist or not then you need to do is
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString: #"http://www.google.com"]];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
and then you can track http status code in delegate function of NSURLConnectionDelegate
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
int code = [(NSHTTPURLResponse *)response statusCode];
if (code == 404)
{
// website not found
// do your stuff according to your need
}
}
You can also find various status code here.
NSError *err;
if ([videoURL checkResourceIsReachableAndReturnError:&err] == NO)
NSLog(#"wops!");
Here's the code for the accepted answer (for your convenience):
How to make call
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"HEAD"];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
You could do this by checking the size of the file via an FTP server, using the SIZE command. If the file size is zero then the file simply do not exist.
Check here on how to do this.
You could of course also do this by using a NSURLRequest with NSURLConnection, checking for the status to be either 200 (success) or 404 (failed). The 404 status doesn't have to be that the file doesn't exist though, it could also be that the file just couldn't be retrieved.