Can't get FileHandle of existing file for append

I have a block of code

                if ( [fm fileExistsAtPath: userLogTextFile] ) {
                    NSFileHandle * file;
                    file = [NSFileHandle fileHandleForUpdatingAtPath: userLogTextFile];
                    NSData  *   dataBuffer = [NSData dataWithBytes: [outputLine UTF8String] length: [outputLine length]];
                    if ( nil == file ) {
                        NSLog(@"Failed to open file");
                        NSLog ( @"Failed to create file Handle or write to log file: %@, %@", userLogTextFile, NSStringFromSelector( _cmd ) );
                        NSLog ( @"%@", outputLine );
                        NSLog ( @"Error: %@", error.description );
                       return;
                    }
                    [file seekToEndOfFile];
                    [file writeData: dataBuffer];
                    [file closeFile];
                } else {
                    NSLog ( @"Log file does not exist: %@", userLogTextFile );
                }

On execution the FileHandle is instantiated but "file" is alkways nil after line 3.k

As a side note why is there no append to file method? Leaving large files in memory is a nice way tp sell memory but really

Any advice is grateefully received as to how to solve this problem.

ClarkW

Answered by QuinceyMorris in 173368022

You don't need both dispatch_io_create and dispatch_io_create_with_path. One or the other. The first is when you already have a file descriptor, the second when you have a path.


>>figure out how to set file handle to the end to append data


The dispatch_io_write function has an offset parameter. The documentation says this is relative to the file position set when you called dispatch_io_create_with_path, if you open with "random" io_type, or ignored if you open with "stream".


So, using the correct oflag and mode parameters to cause the file to opened for appending, and try "stream" first. If you're lucky, a single dispatch_io_write will be all you need, and you won't even need to know the existing file length.


>> have gotten obscenly complicated


I dunno, you started with open, seek, write, close. With GCD you have open, write, close. The GCD C interface is a bit ugly, but you get asynchronicity for free, and you get progress monitoring for free.


As I said, GCD I/O has a small learning curve. If you truly hate the GCD approach, then it's OK to go back to NSFIleHandle. Just create your own file descriptor in advance, to find out what the open error was, and make sure you handle exceptions.

The problem with NSFileHandle is that it has no good mechanism for returning errors. NSFileHandle reads and writes, for example, throw a Cocoa exception if an error occurs, which (in modern Cocoa apps) will typically crash the app unless you make sure you catch them.


In this case, it could be — I guess — that the file exists but doesn't have the permissions for writing.


You could, perhaps, try creating your own file descriptor via posix I/O calls, which at least return errors, then create a NSFileHandle that uses your file descriptor.


However, NSFileHandle is pretty much obsolete these days. For "convenience" reads and writes of entire files, you can use NSData read and write methods. For anything more sophisticated, including appending to an existing file, use Grand Central Dispatch (GCD). It has far better I/O support, and though it has simple synchronous reads and writes it also makes asynchronous I/O very easy.


See:


developer.apple.com/reference/dispatch/1667684-grand_central_dispatch_gcd?language=objc


There's a little bit of learning curve for the terminology and for the way it handles data buffers, but it's well worth your effort. Spending any time at all on NSFileHandle is pretty much a waste.


P.S. Any file manager API that does not have an outError parameter is obsolete, and has been replaced by something that does return the error. There's no replacement for 'fileExistsAtPath' because it's no longer recommended to make such checks, ever. The problem is the timing window after you check but before you access the file, during which the file could be deleted by another app.


The recommended approach is to try the actual operation you want (such as opening the file), and deal with the "file doesn't exist" error if it's returned. Clearly, you can't do that with NSFileHandle, but you can with GCD.

Gosh writing and reading and appending to a file I created have gotten obscenly complicated.


So I need to do a

dispatch_io_t dispatch_io_create(dispatch_io_type_t type, dispatch_fd_t fd, dispatch_queue_t queue, void (^cleanup_handler)(int error));

Then

dispatch_io_t dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, int oflag, mode_t mode, dispatch_queue_t queue, void (^cleanup_handler)(int error));

Then figure out how to set file handle to the end to append data and to do that.

Then I can do a:

void dispatch_io_write(dispatch_io_t channel, off_t offset, dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t io_handler);

Then an

void dispatch_io_close(dispatch_io_t channel, dispatch_io_close_flags_t flags);


Seems to me the edisting NSstring and NSData reads and writes APIs could do this for me.

Or I could do this in C++ and just have it work.

KIs there an ekxample of doing this somewhere. Not an Apple example as they are unreadable. Well more research I guess.

Thank you Quincy

I'm only slightly daunted but I am truely grateful!

ClsrkW

Accepted Answer

You don't need both dispatch_io_create and dispatch_io_create_with_path. One or the other. The first is when you already have a file descriptor, the second when you have a path.


>>figure out how to set file handle to the end to append data


The dispatch_io_write function has an offset parameter. The documentation says this is relative to the file position set when you called dispatch_io_create_with_path, if you open with "random" io_type, or ignored if you open with "stream".


So, using the correct oflag and mode parameters to cause the file to opened for appending, and try "stream" first. If you're lucky, a single dispatch_io_write will be all you need, and you won't even need to know the existing file length.


>> have gotten obscenly complicated


I dunno, you started with open, seek, write, close. With GCD you have open, write, close. The GCD C interface is a bit ugly, but you get asynchronicity for free, and you get progress monitoring for free.


As I said, GCD I/O has a small learning curve. If you truly hate the GCD approach, then it's OK to go back to NSFIleHandle. Just create your own file descriptor in advance, to find out what the open error was, and make sure you handle exceptions.

You oulf be right Sir. Perhaps I am being to resistive to change.

I'll give it a shot.'

Clark

Can't get FileHandle of existing file for append
 
 
Q