Wednesday, August 18, 2010

NSString appendToFile:usingEncoding:

Somewhat frequently I find myself looking in Apple's documentation for a method that I'm sure exists but doesn't show up when I search using the Documentation Viewer in Xcode. Usually in that case, the method does actually exist, but it's not from Apple. Nine out of ten times it means I've got a category method I've used so many times that I've forgotten Apple didn't provide it. The other one out of ten, it's just an indication that I'm slowly growing senile.

When this happens, I try to post the category methods because I figure if I've used the method enough over the years that I though it came from Apple, then other people might be able to use it.

One bit of functionality that I've always been surprised isn't in NSString class is the ability to append the contents of a string onto an existing file. There are methods for creating or replacing a file with the contents of a string, so why not to append the contents onto an existing file? It's a fairly common and very useful task. I've used it for creating application logs, and for other situations where I need to create large files that wouldn't be practical to keep in memory. This is especially nice to have on iOS given the lack of virtual memory.

Here's the category method I use for this. I dug this up yesterday from an older project after searching for it in Apple's documentation. I had to update it a little (it was old enough that it used the old deprecated cString methods.

#import <Foundation/Foundation.h>

@interface NSString(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path usingEncoding:(NSStringEncoding)encoding;

@implementation NSString(MCFileAppend)
- (BOOL)appendToFile:(NSString *)path usingEncoding:(NSStringEncoding)encoding
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:path];
if (fh == nil)
return [self writeToFile:path atomically:YES encoding:encoding error:nil];

[fh truncateFileAtOffset:[fh seekToEndOfFile]];
NSData *encoded = [self dataUsingEncoding:encoding];

if (encoded == nil) return NO;

[fh writeData:encoded];
return YES;



Malcolm Hall said...

Another way without needing to seek ;-) is:

NSOutputStream* os = [[NSOutputStream alloc] initToFileAtPath:filename append:YES];
[os open];
NSData *allData = [str dataUsingEncoding:NSUTF8StringEncoding];
[os write:[allData bytes] maxLength:[allData length]];
[os close];
[os release];

Daniel said...

Jeff, is there any reason you are not closing the file-handle? Or, just missing?


Jeff LaMarche said...


There's no need: when the file handle is deallocated, that will happen automatically. From the docs:

The deallocation of an NSFileHandle object deletes its descriptor and closes the represented file or channel unless the NSFileHandle object was created with initWithFileDescriptor: or initWithFileDescriptor:closeOnDealloc: with NO as the parameter argument.

Daniel said...

Ah, Ok. Thanks for the info, Jeff. But in case you might be accessing the same file in the same runloop cycle from somewhere else (for whatever reason) it might be safer to do so; what do you think?

Jeff LaMarche said...


It guess it couldn't hurt. if you're accessing two files simultaneously from different threads without using a mutex, you could have problems, though the problem is likely in the design of your app :)

But it certainly couldn't hurt to close the file handle when you're done. By all means, do so if it concerns you. :)

Daniel said...

For different thread this is a bad design, i agree. Just wanted to mention the case of consectutive calls in one run-loop-cycle on the main thread. You never know ;-)

SEO Services Consultants said...

Nice information, many thanks to the author. It is incomprehensible to me now, but in general, the usefulness and significance is overwhelming. Thanks again and good luck! Web Design Company

Melvin said...

Your code is not satisfying for people. It does not seem correct and properly design. I think you need some more practice in this. Anyways Good Try. Keep it up.

iphone application development