Saving image from url to disk not working - objective-c

I'm trying to download an image from an url in my app, so that it can be used by the Today Extension.
So I call [self downloadImageFromUrl:imageOne];, which uses this method:
- (void)downloadImageFromUrl:(NSString *)url {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *storeUrl = [fileManager containerURLForSecurityApplicationGroupIdentifier:#"group.myapp.TodayExtensionDefaults"];
NSString *path = [storeUrl absoluteString];
path = [path stringByAppendingPathComponent:#"Test/imageOne.png"];
NSLog(#"Path is %#", path);
NSLog(#"Response: %#", responseObject);
UIImage *image = responseObject;
[UIImagePNGRepresentation(image) writeToFile:path atomically:YES];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image error: %#", error);
}];
[requestOperation start];
}
So when I run this, everything executes fine. I get no errors. Yet, when I go to the folder logged in the simulator, no file is present. The error I get from the write action is Write returned error: The operation couldn’t be completed. (Cocoa error 4.). And so I changed the write action to:
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]) {
NSLog(#"Error: %#. %s, %i", error.localizedDescription, __PRETTY_FUNCTION__, __LINE__);
}
}
[UIImagePNGRepresentation(image) writeToFile:path options:NSDataWritingAtomic error:&error];
But I still get Write returned error: The operation couldn’t be completed. (Cocoa error 4.)

You are grabbing the absoluteString (which includes the scheme, amongst other things):
NSString *path = [storeUrl absoluteString];
I believe you intended the path:
NSString *path = [storeUrl path];

Related

Proper way of handling reference to NSError

My code looks like this, sometimes app crashes on the last line, when it tries to log the error. What am I doing wrong?
BOOL isDir;
NSError *error;
NSString *downloadPath = [[NSString stringWithFormat:#"%#/%#", [download downloadFolder], [download escapedTitle]] stringByExpandingTildeInPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:downloadPath isDirectory:&isDir])
{
[fileManager createDirectoryAtPath:downloadPath withIntermediateDirectories:YES attributes:nil error:&error];
if (error)
NSLog(#"%#", [error localizedDescription]);
}
I've also attached the output from the console:
In Cocoa, the NSError ** is only valid if the called method returns an error, which in this case would be if -createDirectoryAtPath:... returns false.
Instead of testing for if (error), test for the return value of the -createDirectoryAtPath: method being false, and you'll be good to go.
For example:
if (![fileManager createDirectoryAtPath:downloadPath withIntermediateDirectories:YES attributes:nil error:&error]) {
NSLog(#"%#", [error localizedDescription]);
}

AFNetworking + queue + cancel operations + junk files

i 'm downloading some files using AFNetworking using a queue. Here is my code:
apiClient =[[AFHTTPClient alloc]initWithBaseURL: [NSURL URLWithString:ZFREMOTEHOST]];
for (NSString *element in self.productsArray) {
NSURL *url = [NSURL URLWithString:element];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *documentsDirectory = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [paths objectAtIndex:0];
NSString *targetFilename = [url lastPathComponent];
NSString *targetPath = [documentsDirectory stringByAppendingPathComponent:targetFilename];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:targetPath append:NO];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure case
NSLog(#"BaaZ File NOT Saved %#", targetPath);
//remove the file if saved a part of it!
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:targetPath error:&error];
if (error) {
NSLog(#"error dude");
}
if ([operation isCancelled]) {
//that doesn't work.
NSLog(#"Canceled");
}
}];
[op setDownloadProgressBlock:^(NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
if (totalBytesExpectedToRead > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.alpha = 1;
self.progressView.progress = (float)totalBytesRead / (float)totalBytesExpectedToRead;
NSString *label = [NSString stringWithFormat:#"Downloaded %lld of %lld bytes", totalBytesRead,totalBytesExpectedToRead];
self.progressLabel.text = label;
});
}
}];
[self.resourcesArray addObject:op];
}
for (AFHTTPRequestOperation *zabols in self.resourcesArray) {
[apiClient.operationQueue addOperation:zabols];
}
the code is working fine on file downloading but i want some cancel functionality so i have a button with an action that has the code below:
[apiClient.operationQueue cancelAllOperations];
the operations cancel file but then there are some junk files on the Documents folder. By saying junk i mean file that started downloading i canceled them and the i get a file with the same name but useless can't be opened cause it's damaged.
How can i prevent AF from doing that and keep only the completed files when i cancel it?
any help would be grateful.
Also tried canceling job by job like that:
for (AFHTTPRequestOperation *ap in apiClient.operationQueue.operations) {
[ap cancel];
NSLog(#"%#",ap.responseFilePath);
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:ap.responseFilePath error:nil];
}
and deleting the junk files but that doesn't work either.
Try using AFDownloadRequestOperation extension, it additionally supports resume a partial download, uses a temporary directory and has a special block that helps with calculating the correct download progress.
You can also try that : github

Still backups to iCloud even when addSkipBackupAttributeToItemAtURL is implemented?

My recent iOS app just got rejected because the application is storing data on Documents so it is backed up by iCloud, this is not allowed since the data is downloaded from a server.
I'm trying to rebuild the code right now and have hit a tough spot when storing the data in the a folder calles Application Support instead.
But even though I'm using addSkipBackupAttributeToItemAtURL it still shows up as a backup to iCloud.
I'm only targeting 5.1 so it's not a versioning problem.
I've added the affected code here below:
NSString *newPath = [NSMutableString stringWithFormat:#"%#/Library/Application Support/", NSHomeDirectory()];
if (![[NSFileManager defaultManager] fileExistsAtPath:newPath]) //Does directory already exist?
{
if (![[NSFileManager defaultManager] createDirectoryAtPath:newPath
withIntermediateDirectories:NO
attributes:nil
error:&error])
{
NSLog(#"Create directory error: %#", error);
}
}
NSString *guidesPath = [newPath stringByAppendingPathComponent:#"/Guides"];
if (![[NSFileManager defaultManager] fileExistsAtPath:guidesPath]) //Does directory already exist?
{
if (![[NSFileManager defaultManager] createDirectoryAtPath:guidesPath
withIntermediateDirectories:NO
attributes:nil
error:&error])
{
NSLog(#"Create directory error: %#", error);
}
}
NSString *dataPath = [guidesPath stringByAppendingPathComponent:dbName];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
{
if (![[NSFileManager defaultManager] createDirectoryAtPath:dataPath
withIntermediateDirectories:NO
attributes:nil
error:&error])
{
NSLog(#"Create directory error: %#", error);
}
}
NSString *newGuidesPath = [dataPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *guidesURL = [NSURL URLWithString:newGuidesPath];
[self addSkipBackupAttributeToItemAtURL:guidesURL];
NSString* path = [dataPath stringByAppendingPathComponent:
[NSString stringWithString: finalName] ];
if (![fileManager fileExistsAtPath:path]) {
[myData writeToFile:path atomically:YES];
NSString *docFile = [dataPath stringByAppendingPathComponent: #"guideid.txt"];
[[guideID dataUsingEncoding:NSUTF8StringEncoding] writeToFile:docFile atomically:NO];
DLog(#"Saved database here:%#", path);
}else{
DLog(#"Already exists, no need to copy");
}
}
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"Error excluding %# from backup %#", [URL lastPathComponent], error);
}
return success;
}
Actually found a solution,
I've encoded the URL which was not needed.
Just added this to the folder
NSURL *guidesURL = [NSURL fileURLWithPath:guidesPath];
[guidesURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:NULL];
And skipped the exclude method, just flagged it inside the code, which seems to work.
Did you try create new back up on your device from icloud settings?

AFnetworking downloading multiple files

I'm using this code to loop through an array to download multiple files and write to disk.
-(void)download
{
//set url paths
for (NSString *filename in syncArray)
{
NSString *urlpath = [NSString stringWithFormat:#"http://foo.bar/photos/%#", filename];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlpath]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:filename];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
but the problem is it calls the success block after each file is done, (which it should) but I just need one final call back to reload some data and end a progress HUD.
Any pointers in the right direction would be great.
Maybe someday this will help someone, but I was able to use a workaround that probably has major issues but its okay for my simple usage.
I just deleted each line from the sync array after it was processed then ran my code i needed.
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
[SVProgressHUD showWithStatus:#"Updating Photos"];
[syncArray removeObject:filename];
if (!syncArray || !syncArray.count)
{
NSLog(#"array empty");
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:self];
[SVProgressHUD dismissWithSuccess:#"Photos Updated"];
}
You can use AFHTTPClient to enqueueBatchOperations and this has a completionBlock which is called when all operations are finished. Should be exactly what you're looking for.

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]];