checking for bad NSURLConnection - objective-c

i am new to Objective-C and going through the book, The Big Nerd Ranch Guide to Objective-C programming..
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSURL *url = [NSURL URLWithString:#"http://www.google.com/imagess/logos/ps_logo2.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSError *error =nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:NULL
error:&error];
if(!data){
NSLog(#"fetch failed %#", [error localizedDescription]);
return 1;
}
NSLog(#"the files is %lu bytes", [data length]);
BOOL written = [data writeToFile:#"/tmp/google.png"
options:NSDataWritingAtomic
error:&error];
if(!written){
NSLog(#"write failed: %#",[error localizedDescription]);
return 1;
}
NSLog(#"Success!");
NSData *readData = [NSData dataWithContentsOfFile:#"/tmp/google.png"];
NSLog(#"the file read from disk has %lu bytes", [readData length]);
}
return 0;
}
the problem is this, if i change the *url to http://aFakeDomain.com/imimimi/myImage.png then my data object will be nill because there is no HOST and everything works fine.. but if i use google as the domain and point to a bad file location then the data object stil has header info and is NOT nil thus i never get the error i should.
whats the best way to make sure that the *url successfully found a file.
thanks

You will need to pass a response to the NSURLConnection call and then check its status code:
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&httpResponse
error:&error];
int code = [httpResponse statusCode];
you will get a 404 status code in your case.

NSUrlResponce *responce = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&responce
error:&error];
if (error) {
// handle the error
}
if (![[responce MIMEType] isEqualToString:#"image/png"]) {
// failed to get png file
}

There are also some mistakes of the file name;
instead of #"/tmp/google.png", you should use the code list blow:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"google.png"];

Related

JSON returning data but data parameter is nil

I'm messing with some API stuff and tried the following:
#define searchWebService #"https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=b667841296224aab0371a6f4a4546662&format=json&per_page=20&page=1"
// Construct url string for search
NSString *urlString = [NSString stringWithFormat:#"%#&text=%#&nojsoncallback=1", searchWebService, keyword];
//NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// Create url via formatted string
NSURL *url = [NSURL URLWithString:urlString];
// Get all data from the return of the url
NSData *photoData = [NSData dataWithContentsOfURL:url];
// Place all data into a dictionary
NSDictionary *allData = [NSJSONSerialization JSONObjectWithData:photoData options:kNilOptions error:nil];
Here is the URL that is built:
https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=b667841296224aab0371a6f4a4546662&format=json&per_page=20&page=1&text=ball&nojsoncallback=1
When I plug this URL into a web browser I get formatted JSON but when I try to plug that into:NSData *photoData = [NSData dataWithContentsOfURL:url];
I get 'data parameter is nil'.
Any ideas what I'm doing wrong?
UPDATE:
I'm now using:
// Construct url string for search
NSString *urlString = [NSString stringWithFormat:#"%#&text=%#&nojsoncallback=1", searchWebService, keyword];
NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:theRequest queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"BOOM %s %#", __func__, response);
if(!connectionError){
}
else{
NSLog(#"%s %#", __func__, connectionError.localizedDescription);
}
but neither of the logs ever show in the console.
UPDATE: TEST PROJECT
I've created a brand new project and put the following code in the viewDidLoad method:
NSString *urlString = [NSString stringWithFormat:#"%#", webServiceGetGlobalScores];
NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:formattedURLString]];
NSLog(#"theRequest: %#", theRequest);
[NSURLConnection sendAsynchronousRequest:theRequest queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"BOOM %s %#", __func__, response);
if(!connectionError){
}
else{
NSLog(#"%s %#", __func__, connectionError.localizedDescription);
}
}];
And this is the defined #define webServiceGetGlobalScores #"http://www.appguys.biz/JSON/iTapperJSON.php?key=weBeTappin&method=getGlobalScores"
But still the sendAsynchronousRequest does not log anything.
UPDATE 3
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Construct url string for search
NSString *urlString = [NSString stringWithFormat:#"%#", webServiceGetGlobalScores];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSLog(#"urlRequest: %#", urlRequest);
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{NSLog(#"BOOM %s %#", __func__, response);
NSLog(#"ERROR: %#", error);
if ([data length] > 0 && error == nil){
NSLog(#"BOOM");
}
}];
}
Try using + dataWithContentsOfURL:options:error: instead, which gives you an NSError object. Plus, notice that that the docs say:
Do not use this synchronous method to request network-based URLs. For
network-based URLs, this method can block the current thread for tens
of seconds on a slow network, resulting in a poor user experience, and
in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the
dataTaskWithURL:completionHandler: method of the NSSession class. See
URL Loading System Programming Guide for details.
You can use this code for downloading data,
NSString *urlString = //your whatever URL
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:theRequest queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"%s %#", __func__, response);
if(!connectionError){
//Parse your JSON data
}
else{
NSLog(#"%s %#", __func__, connectionError.localizedDescription);
}
}];
Edited
Other way works!!! o_O see following
Now i surprised why following code working instead of above.
NSString *urlString = #"https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=b667841296224aab0371a6f4a4546662&format=json&per_page=20&page=1&text=ball&nojsoncallback=1";
NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSError *theErr;
NSData *thd = [NSData dataWithContentsOfURL:[NSURL URLWithString:formattedURLString] options:0 error:&theErr];
if(theErr){
NSLog(#"%#", theErr.localizedDescription);
}
else{
NSLog(#"%#", [[NSString alloc] initWithData:thd encoding:NSUTF8StringEncoding]);
}
3rd Way,
Newer iOS simulator version > 6.x having some issue. Reset your simulator and check it out your code.
For reference go NSURLConnection GET request returns -1005, "the network connection was lost"

Using apples suggested code isnt excluding files from icloud backup

I have an app that has been rejected 3 times due to icloud backup issues. Apple have written back to say that I need to use there bit of code to exclude the files from being backed up. However this isnt working and i am at wits end.
Here is the code i've used
- (BOOL)downloadFile:(NSString *)fileURI targetFolder:(NSString *)targetFolder targetFilename:(NSString *) targetFilename{
#try{
NSError *error = nil;
NSURL *url = [NSURL URLWithString:fileURI];
if(![url setResourceValue:#"YES" forKey:NSURLIsExcludedFromBackupKey error:&error]){
NSLog(#"KCDM: Error excluding %# from backup %#", fileURI, error);
}else{
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingString:targetFolder];
NSError *error = nil;
if(![[NSFileManager defaultManager] fileExistsAtPath:dataPath]){
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:&error];
}
NSString *filePath = [NSString stringWithFormat:#"%#/%#%#", documentsDirectory,targetFolder,targetFilename];
return [urlData writeToFile:filePath atomically:YES];
}
}
}
#catch(NSException * e){
NSLog(#"Error download: %#",e);
}
return false;
}
what am i doing wrong?
You try to set NSURLIsExcludedFromBackupKey for the http://-Url you download from the web. That won't work.
You have to set this key-value pair for the actual file that is saved on the device.
Additionally you are not supposed to set this value to the string #"YES", you must use a NSNumber object representing the boolean value YES.
For example:
NSString *filePath = [NSString stringWithFormat:#"%#/%#%#", documentsDirectory,targetFolder,targetFilename];
if ([urlData writeToFile:filePath atomically:YES]) {
// did write correctly
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
if(![fileURL setResourceValue:#YES forKey:NSURLIsExcludedFromBackupKey error:&error]){
NSLog(#"KCDM: Error excluding %# from backup %#", fileURI, error);
return NO;
}
// could set NSURLIsExcludedFromBackupKey
return YES;
}
// could not write to file
return NO;

TBXML with NSData in Xcode

I have a URL that returns a pretty flat XML file: <entries><title>val1</title><author>Bob</author></entries> The code runs ok:
NSString *urlString = [NSString stringWithFormat:#"http://www.somesite.php?qid=%d", __inum];
NSLog(#"urlString = %#", urlString);
NSURLResponse * response = nil;
NSError * error = nil;
NSURLRequest * urlRequest = [NSURLRequest requestWithURL: [NSURL URLWithString:urlString]];
NSData * myData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
NSLog(#"%#", myData);
TBXML *sourceXML = [[TBXML alloc] initWithXMLData:myData error:nil];
TBXMLElement *rootElement = sourceXML.rootXMLElement;
if (rootElement) {
NSLog(#"Root element found...");
TBXMLElement *qaElement1 = [TBXML childElementNamed:#"title" parentElement:rootElement];
if (qaElement1) {
NSString *idAttribute = [TBXML valueOfAttributeNamed:#"title" forElement:qaElement1];
NSLog(#"Got to the 1st call for idAttribute... %#", idAttribute);
}
else { NSLog(#"There is no value for title..."); }
}
else { NSLog(#"Root element must be null..."); }
}
It finds the root element and gets to the call for valueOfAttribute:#"title" but the value is always (null).
So my question: do I have to do something to convert the NSData back to man readable (I was under the impression TBXML gave that option to work with the NSData and did the calculation). If not, what is the call to create (and then use) an NSString in UTF8 from 'myData'?
what is the call to create (and then use) an NSString in UTF8 from 'myData'?
use initWithData:encoding:
NSString *str = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
or stringWithUTF8String: if you know myData is null-terminated UTF8 string
NSString *str = [NSString stringWithUTF8String:[myData bytes]];
Do not ignore error.
NSError *error = nil;
TBXML *sourceXML = [[TBXML alloc] initWithXMLData:myData error:&error];
if (error) {
// handle it, at least log it
}

Making stringWithContentsOfURL asynchronous - Is it safe?

I attempted to make -[NSString stringWithContentsOfURL:encoding:error:] asynchronous, by running it a-synchronically from a background thread:
__block NSString *result;
dispatch_queue_t currentQueue = dispatch_get_current_queue();
void (^doneBlock)(void) = ^{
printf("done! %s",[result UTF8String]);
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
result = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com/"] encoding:NSUTF8StringEncoding error:nil];
dispatch_sync(currentQueue, ^{
doneBlock();
});
});
Its working fine, and most importantly, its asynchronous.
My question is if it's safe to do this, or could there be any threading problems etc.?
Thanks in advance :)
That should be safe, but why reinvent the wheel?
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// etc
}];
You can also use:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSString *searchResultString = [NSString stringWithContentsOfURL:[NSURL URLWithString:searchURL]
encoding:NSUTF8StringEncoding
error:&error];
if (error != nil) {
completionBlock(term,nil,error);
}
else
{
// Parse the JSON Response
NSData *jsonData = [searchResultString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *searchResultsDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions
error:&error];
if(error != nil)
{
completionBlock(term,nil,error);
}
else
{
//Other Work here
}
}
});
But yes, it should be safe. I've been told though to use NSURLConnection instead due to error calls and such when communicating via the internet. I'm still doing research into this.
-(void)loadappdetails:(NSString*)appid {
NSString* searchurl = [#"https://itunes.apple.com/lookup?id=" stringByAppendingString:appid];
[self performSelectorInBackground:#selector(asyncload:) withObject:searchurl];
}
-(void)asyncload:(NSString*)searchurl {
NSURL* url = [NSURL URLWithString:searchurl];
NSError* error = nil;
NSString* str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (error != nil) {
NSLog(#"Error: %#", error);
}
NSLog(#"str: %#", str);
}

How can I see components of NSData?

I am trying to write a PDF file from NSData I got from an http request and then display the pdf. When I call [response description], I get <30>, so I am at least getting something back. However, when I run this code, an error comes up: "failed to find PDF header: `%PDF' not found."
Is there some way to see what my NSData is if it is not a pdf? Am I going about this incorrectly? Thanks!
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
if (response) {
NSString *description = [response description];
NSLog(#"description: %#", description); //this prints: <30>
} else {
NSLog(#"response is nil");
}
NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *filename = #"Test1.pdf";
NSString *dirPath = [dirs objectAtIndex:0];
NSError *error = nil;
NSString *path=[dirPath stringByAppendingPathComponent:filename];
[response writeToFile:path atomically:YES];
NSLog(#"Write returned error: %#", [error localizedDescription]);
NSURL *pdfUrl = [NSURL fileURLWithPath: path];
[webView loadRequest:[NSURLRequest requestWithURL:pdfUrl]];