More Detailed Error From createFileAtPath? - objective-c

Is there anyway to get more detailed error data back from "createFileAtPath" I was kind of expecting an NSError? Currently I am using the BOOL return value.
success = [fileMan createFileAtPath:fileOnDisk contents:dBuffer attributes:nil];
if(success == YES) NSLog(#"FileCreated");
else {
NSLog(#"ERROR: Failed to create file");
return 1;
}
gary

I agree... I'd love to have a function for this that accepts NSError!
Errors returned in this case are usually one of the POSIX errors declared in errno.h (errno is automatically included for you as part of the Cocoa or Foundation headers).
To see the error, use the strerror function from errno.h and reference the global errno integer, which is set by the low-level POSIX io functions when a problem occurs:
if (![fm createFileAtPath:#"/etc/foobar.txt" contents:data attributes:nil])
{
NSLog(#"Error was code: %d - message: %s", errno, strerror(errno));
}
// output will be: Error was code: 13 - message: Permission denied
The list of error code constants are listed in the in the Error Handling Programming Guide for Cocoa (in addition to the errno.h header itself).

You're not supposed to use that method; they forgot to put this in the main docs (!), but if you read the Apple header file you find this comment:
/* These methods are provided here for compatibility. The corresponding
methods on NSData which return NSErrors should be regarded as the
primary method of creating a file from an NSData or retrieving the
contents of a file as an NSData. */
So, instead, Apple expects you to use this (which, from testing, appears to function exactly the same - except that it's located in a bizarre class where you'd never think to look for it (I want to create an empty file ... so I have to ... instantiate a nil NSData object? What?) it has an NSError object):
https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/Classes/NSData_Class/Reference/Reference.html#//apple_ref/occ/instm/NSData/writeToFile:options:error:

Related

CFNetwork error handling in Swift

I have a block of objective-c code that responsible for handling HTTP errors by checking the NSError code error. The code sends the error message back to a delegate (unless it's a error code that the app ignores, such as cancelled requests)
failure:^(NSURLSessionTask *task, NSError *error, id responseObject) {
NSString *errorMessage = [self getErrorMessage:responseObject withError:error];
if (error.code!=kCFURLErrorCancelled &&
error.code!=kCFURLErrorCannotFindHost &&
error.code!=kCFURLErrorCannotConnectToHost &&
error.code!=kCFURLErrorNetworkConnectionLost &&
error.code!=kCFURLErrorDNSLookupFailed &&
error.code!=kCFURLErrorNotConnectedToInternet &&
error.code!=kCFURLErrorTimedOut) {
if ([self.delegate respondsToSelector:#selector(didFailed:)]) {
[self.delegate didFailed:errorMessage];
}
}
if (completionBlock != nil) completionBlock(NO);
}];
I have several questions / issues related to this code block.
Is is sufficient to just check the error code? The framework I'm using might return a different type of errors and I'm not sure those error codes are unique.
How would I go and write the same code in Swift? I did find the error codes definitions under CFNetworkErrors enum. However, the error.code & the value from the CFNetworkErrors enum cannot be compared directly, as they have different types. Is it possible to cast error.code to a CFNetworkErrors and then compare the error codes?
Can I safely switch to use the NSURL errors such as NSURLErrorCancelled? Is there a 1-to-1 mapping between the CFNetworks errors and NSURL errors?
It is probably not sufficient to check the error code. You almost certainly need to check the HTTP status code as well, unless the delegate method you're calling already does that. An NSError typically tells you about transport-level failures, whereas the status code tells you about server-side failures, such as a file not existing.
No idea. That question is mostly unrelated to networking, so you should probably ask a separate question for it, and tag it as a Swift question. :-)
Yes, you should be using the Foundation codes. The values should always be identical, but there are likely to be more Foundation error codes than CF codes going forward, as not all of the Foundation-level functionality is exposed publicly at the CF layer.

does the inside of if condition make changes to objects?

I'm having trouble understanding this piece of code:
// assuming fm is NSFileManager object
if ([fm moveItemAtPath: #"newfile" toPath: #"newfile2" error: NULL] == NO){
NSLog(#"File rename Failed");
}
the moveItemAtPath method sends message in if condition.
does it actually rename newfile to newfile2 or is this just some test, to se if renaming file is possible?
The expression inside if will move the file if it can. The return value lets you find out after the fact if an actual change occurred. (For instance, if you are passing a path that does not exist you will receive NO.)

Pointer to Error-Pointer in Objective-C

Sorry, if found a lot of threads like the but they were not about this Error**-thing.
I tried to 'design' my methods like the error-examples I found. But calling the second, the error is not pointing to nil, the debugger says error: summary string parsing error.
This is my controller-method:
-(void) refresh {
NSError *error;
ServerApi *serverApi = [mainModel newServerApi];
NSArray *newItems = [serverApi getNewItems: &error];
...
This is the called method:
- (NSArray *) getNewItems: (NSError **) error {
// Breakpoint here, error is: 'error: summary string parsing error'
...
NSURLResponse *response;
NSData *responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: error];
I thought, I did the same as Apple with sendSynchronousRequest.... Their comment tells
error Out parameter (may be NULL) used if an error occurs
while processing the request. >>>>Will not be modified if the
load succeeds.<<<<
What did I do wrong and why does this work for Apples sendSynchronousRequest...?
The code is fine, as error does not need to be initialized "from outside".
Also, if you use ARC it will automatically initialize local object pointers to nil, so
NSError *error;
is no different than
NSError *error = nil;
under ARC.
While explicit initialization is still a good practice, that's not the source of any error here.
That being said,
summary string parsing error
is a lldb error. My hypothesis is that it gets confused by the double pointer, but I wouldn't worry too much.
By the way, you're doing a slight mistake in implementing this pattern.
Synchronous methods that may fail, should method to return a BOOL value indicating whether the computation was successful and then clients will check that value and subsequently inspect the error object in case it failed.
Checking the error object is in general a bad idea: even some Apple APIs can fail and yet return a nil error, so avoid doing that!
Remember to set your pointer to nil in refresh:
NSError *error = nil;
Also, remember, your checks should be:
Checking (*error) for nil (aka "there was no error passed") - operation may or may not have been successful;
Checking error for NULL (aka "there was no NSError* pointer passed, so don't assign it an object").

How to initialize, pass argument, and check error condition using NSError**

Xcode 4.3
I've read the SO questions on NSError**, so I wrote a simple test program that uses a slightly different syntax recommended by Xcode 4.3 (see __autoreleasing below), so I'm not 100% sure if this is correct, although the code does appear to function properly. Anyway, just a simple file reader, prints an error if the file can't be found.
Questions
Would like to know if the NSError initialization, argument passing using &, and error condition checking are correct.
Also, in the readFileAndSplit.. method, I noticed a big difference between if(!*error) and if(!error), in fact, if(!error) does not work when no error condition is raised.
File Reading Method w/Possible Error Condition
-(NSArray*) readFileAndSplitLinesIntoArray:(NSError *__autoreleasing *) error {
NSString* rawFileContents =
[NSString stringWithContentsOfFile:#"props.txt"
encoding:NSUTF8StringEncoding
error:error
NSArray* fileContentsAsArray = nil;
if(!*error)
fileContentsAsArray =
[rawFileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
return fileContentsAsArray;
Caller
SimpleFileReader* reader = ...
NSError* fileError = nil;
NSArray* array = [reader readFileAndSplitLinesIntoArray: &fileError];
if(fileError){
NSLog(#"Error was : %#, with code: %li",
[fileError localizedDescription],(long)[fileError code]);
}
There are a couple of issues.
First, As per Apple's Error Handling Programming Guide, you should be checking a method's return value to determine whether a method failed or not, and not NSError. You only use NSError to get additional error information in the event that the method failed.
E.g.:
NSArray* fileContentsAsArray = nil;
NSString* rawFileContents = [NSString stringWithContentsOfFile:#"props.txt"
encoding:NSUTF8StringEncoding
error:error];
if (rawFileContents)
{
// Method succeeded
fileContentsAsArray = [rawFileContents ...];
}
return fileContentsAsArray; // may be nil
Second, NSError out parameters are typically optional and may be NULL. But if you pass a NULL error variable into your method it will crash on this line:
if (!*error) {
because you're dereferencing a NULL pointer. Instead, you must always check for NULL before referencing a pointer, like so:
if (error && *error)
{
// Do something with the error info
}
However, if you rewrite the method as indicated above then you won't be accessing the error variable at all.

Memory management of a CFErrorRef returned by ABRecordSetValue

Consider some typical CF code involving error handling, say something like this:
ABRecordRef aRecord = ABPersonCreate();
CFErrorRef anError = NULL;
ABRecordSetValue(aRecord, kABPersonFirstNameProperty, CFSTR("Joe"), &anError);
How do I handle anError after this code? Do I have to retain it, to make sure it doesn't go away, and then later release it? Or am I already the owner and I only have to release it later?
In the Core Foundation framework, it's always the caller's responsibility to release an error returned through a CFErrorRef * argument. For example here's a header file comment from CFBundle.h:
CF_EXPORT
Boolean CFBundlePreflightExecutable(CFBundleRef bundle, CFErrorRef *error) CF_AVAILABLE(10_5, 2_0);
/* This function will return true if the bundle is loaded, or if the bundle appears to be */
/* loadable upon inspection. This does not mean that the bundle is definitively loadable, */
/* since it may fail to load due to link errors or other problems not readily detectable. */
/* If this function detects problems, it will return false, and return a CFError by reference. */
/* It is the responsibility of the caller to release the CFError. */
Chances are the AB framework uses the same convention.
According to "CFError.h" where CFErrorRef is defined: i.e.
typedef struct __CFError * CFErrorRef; // line 43 in CFError.h
if you scroll to the top, you will see this in line 14 to line 22:
CFError *error;
if (!ReadFromFile(fd, &error)) {
... process error ...
CFRelease(error); // If an error occurs, the returned CFError must be released.
}
It is the responsibility of anyone returning CFErrors this way to:
- Not touch the error argument if no error occurs
- Create and assign the error for return only if the error argument is non-NULL
Thus it seems like we do need to release the CFErrorRef ourselves!