Proper way of handling reference to NSError - objective-c

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

Related

NSFileHandle from NSURL failure

I'm trying to write some data to a file. I am able to obtain an NSURL from a file dialog and the NSURL obtained has the following value
file:///Users/brad/Documents/2016-05-04-06-53-35.csv
My code does the following:
NSURL* myFile = [panel URL]; // is file:///Users/brad/Documents/2016-05-04-06-53-35.csv
NSError *error = nil;
NSFileHandle *myFileHandle = [NSFileHandle fileHandleForWritingToURL:myFile error:&error];
NSLog(#"%#",error);
if (myFileHandle)
{
... // do something
}
else
{
NSLog(#"File operation failed");
}
I get an error which indicates the following
Error Domain=NSCocoaErrorDomain Code=2 "(null)" UserInfo={NSFilePath=/Users/brad/Documents/
Also myFileHandle is nil.
Any clues ?
See the documentation for NSFileHandle fileHandleForWritingToURL:error:. You get back nil if the file doesn't already exist.
Use NSFileManager to first check if the file exists or not. If not, use NSFileManager createFileAtPath:contents:attributes: to create an empty file.
NSURL *myFile = [panel URL];
if (![[NSFileManager defaultManager] fileExistsAtPath:[myFile path]]) {
[[NSFileManager defaultManager] createFileAtPath:[myFile path] contents:[NSData data] attributes:nil];
}
NSError *error = nil;
NSFileHandle *myFileHandle = [NSFileHandle fileHandleForWritingToURL:myFile error:&error];

Saving image from url to disk not working

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

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?

NSApplicationSupportDirectory invalid CFStringRef

I'm trying to store a file in the NSApplicationSupportDirectory, since i have a preloaded folder in my app, which i want to add files to once the app have been initialized. I'm trying to NSLog the content of the file, so i can see if it actually worked. From the debugger, i can see that the content is , which i don't not what means. Anybody?
NSString *document = [NSString stringWithFormat:#"%# %# %# %# %#", description, focus, level, equipment, waterDepth];
//NSLog(#"%#", document);
//get the documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *supportDirectory = [paths objectAtIndex:0];
//filename
NSString *filename = [NSString stringWithFormat:#"%#/%#", supportDirectory,[_nameTextField.text stringByAppendingString:#".txt"]];
NSLog(#"%#", supportDirectory);
NSLog(#"%#", filename);
[document writeToFile:filename atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
NSString *content = [[NSString alloc]initWithContentsOfFile:filename usedEncoding:nil error:nil];
NSLog(#"%#", content);
You're not passing a string encoding, you are passing NSStringEncodingConversionAllowLossy which is an option that can affect how encoding conversion happens for certain methods (not the one you're using). You need to pass something like NSUTF8StringEncoding.
When you write or read files, it is highly recommended to use the error parameter and handle the cases where you get an error. Also, this is very useful to help you debug your code.
For example, in your case, you can do like this:
NSError *error = nil;
BOOL success = [document writeToFile:filename atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&error];
if (!success) {
NSLog(#"Could not write file: %#", error);
} else {
NSString *content = [[NSString alloc]initWithContentsOfFile:filename usedEncoding:nil error:&error];
if (!content) {
NSLog(#"Could not read file: %#", error);
}
}
If you get an error that says that The operation couldn’t be completed. No such file or directory, that means that you did not previously create the folder. So you create it before trying to add content to it:
NSString *supportDirectory = [paths objectAtIndex:0];
NSError *error = nil;
BOOL success;
if (![[NSFileManager defaultManager] fileExistsAtPath: supportDirectory]) {
success = [[NSFileManager defaultManager] createDirectoryAtPath:supportDirectory withIntermediateDirectories:YES attributes:nil error:&error];
if (!success) {
NSLog(#"Could not create directory: %#", error);
}
}

Getting error while trying to make writeable copy of sqlite

I have a copy of my db in Supporting Files folder. I need update a couple of tables and I am trying to use method to make writeable copy of db.
- (void) createEditableDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *writableDB = [documentsDir stringByAppendingPathComponent:#"yc_ch.db"];
success = [fileManager fileExistsAtPath:writableDB];
NSString *defaultPath = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"yc_ch.db"];
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file:'%#'.", [error localizedDescription]);
}
}
I have created (typed, physically) the 'Documents' folder in the root. At moment hierarchy of docs looks like this:
When I run my app. I am getting NSAssert1(0, #"Failed to create writable database file:'%#'.", [error localizedDescription]);.
What's wrong with it?
You copy the file even if the file already exists in the Document, I think NSFileManager copyFileAtPath: will return NO if file already exists at destination, hence failing your check and raising your NSAssert
success = [fileManager fileExistsAtPath:writableDB];
if (success)
{
return;
}
So the file isn't copied over if it already exists in your document. If that's what causing the error, can you actually post the assertion, [error localizedDescription] will at least tell you what is wrong with it at the moment.
Edit:
- (void) createEditableDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *writableDB = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:#"yc_ch.db"];
NSLog(#"Document path = %#", writableDB);
success = [fileManager fileExistsAtPath:writableDB];
if (success)
{
return;
}
NSString *defaultPath = [[NSBundle mainBundle] pathForResource:#"yc_ch" ofType:#"db"];
NSLog(#"defaultPath = %#", defaultPath);
error = nil;
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file:%#", [error localizedDescription]);
}
if (error)
{
NSLog(#"error = %#", [error localizedDescription]);
}
}