iOS - appending text to a file in the application bundle - objective-c

I'm trying to append some text to a file that resides in the application bundle with the following code:
NSString *dotsStr = [NSString stringWithFormat:#"%i", dots];
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%i_500", dots] ofType:#"txt"];
NSString *contents = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:nil];
NSString *newpath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%i_50", dots] ofType:#"txt"];
[contents appendToFile:newpath encoding:NSASCIIStringEncoding];
When appending to the file, I'm using a category on NSString:
- (BOOL) appendToFile:(NSString *)path encoding:(NSStringEncoding)enc;
{
BOOL result = YES;
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:path];
if (!fh)
{
[[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil];
fh = [NSFileHandle fileHandleForWritingAtPath:path];
}
if (!fh) return NO;
#try {
[fh seekToEndOfFile];
[fh writeData:[self dataUsingEncoding:enc]];
}
#catch (NSException * e)
{
result = NO;
}
[fh closeFile];
return result;
}
This is working fine on the simulator, however, when I restart the app, the old file gets loaded, without the appended text. I have no idea how this is possible.
Does anyone know how I could change this category so that the changes are persistent?
Thanks in advance! :)

You can't write to the app bundle.
(For quite obvious security reasons, by the way.)
If you're intending to change a file, copy it to a writable location, for example in the Documents directory.
(Oh, and before you ask it: the simulator uses OS X's file system, where this restriction is not present, so that's why it works on the simulator. Another thing the simulator fails to simulate.)

Application bundle is read-only. You need to copy your file to application's Documents directory before modifying it.

Related

How to read the plist file of ANOTHER app on Mac OS X?

I am creating a plugin to another application which I would like to access one plist file.
I tried the following, but the file can't be found.
NSBundle* bundle = [[NSBundle mainBundle] initWithPath:#"~/Library/Containers/com.Apple.Externalapp/"];
NSString* filePath = [bundle pathForResource:#"container" ofType:#"plist"];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSLog(#"File exists");
} else {
NSLog(#"File doesn't exist");
}
What am I doing wrong?
You are starting with [NSBundle mainBundle]. That method returns the NSBundle for your application, completely initialised. You send an initWithPath message to this initialised bundle; things will go very wrong from then on.
You probably want [[NSBundle alloc] initWithPath... ].

How can I read supporting files by line in Xcode?

I'm currently making a web browsing program in Xcode 4.5.1 for OS X and I am trying to work on a list of bookmarks. What I hope to do is to have a supporting file called Bookmarks.txt in which I would list bookmarks like this:
Google
http://www.google.com/
Apple
http://www.apple.com/
Microsoft
http://www.microsoft.com/
I have already looked at a lot of pages discussing this, but none of them apply to what I'm doing. What I have now is
NSMutableArray *list;
NSString *contents;
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Bookmarks" ofType:#"txt"];
if (filePath) {
content = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
for (NSString *line in [contents componentsSeparatedByString:#"\n"]) {
[list addObject:line];
}
}
as well as Dave DeLong's method, but I get all kinds of errors with Dave DeLong's and with this one nothing happens. Any help would be great, but I am just starting out at Xcode and know very little.
Thanks!
Your objects aren't initialized.
NSMutableArray *list = [[NSMutableArray alloc] init];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Bookmarks" ofType:#"txt"];
if (filePath) {
NSString * content = [NSString initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
for (NSString *line in [contents componentsSeparatedByString:#"\n"]) {
[list addObject:line];
}
}

Writing to a file in Objective C

I'm just trying to write into a .txt file in Objective C. Here is the code:
BOOL success = [str writeToFile:#"tmp/cool.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];
if(success)
{
NSLog(#"done writing!");
}
else
{
NSLog(#"writing failed: %#", [error localizedDescription]);
}
The output of this code is "The folder cool.txt does not exist". I dont understand this, since the ".txt" would deem it to be file.
What am I doing wrong?
I wrote a demo for you, assume that you use iOS.
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"tmp"]; //get tmp path
NSString *filePath = [path stringByAppendingPathComponent:#"cool.txt"];
NSString *str = #"hello world";
NSError *error;
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
NSString *str1 = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", str1);
Assume you use iOS as the platform...
You need to first get application's document directory so that you can write into that directory (you can use library or temporary directory but document directory is most common)
You have to make sure that 'tmp' directory exists under the document directory.

Rename file in Cocoa?

How would I rename a file, keeping the file in the same directory?
I have a string containing a full path to a file, and a string containing a the new filename (and no path), for example:
NSString *old_filepath = #"/Volumes/blah/myfilewithrubbishname.avi";
NSString *new_filename = #"My Correctly Named File.avi";
I know about NSFileManager's movePath:toPath:handler: method, but I cannot workout how to construct the new file's path..
Basically I'm looking for the equivalent to the following Python code:
>>> import os
>>> old_filepath = "/Volumes/blah/myfilewithrubbishname.avi"
>>> new_filename = "My Correctly Named File.avi"
>>> dirname = os.path.split(old_filepath)[0]
>>> new_filepath = os.path.join(dirname, new_filename)
>>> print new_filepath
/Volumes/blah/My Correctly Named File.avi
>>> os.rename(old_filepath, new_filepath)
NSFileManager and NSWorkspace both have file manipulation methods, but NSFileManager's - (BOOL)movePath:(NSString *)source toPath:(NSString *)destination handler:(id)handler is probably your best bet. Use NSString's path manipulation methods to get the file and folder names right. For example,
NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
[[NSFileManager defaultManager] movePath:oldPath toPath:newPath handler:nil];
Both classes are explained pretty well in the docs, but leave a comment if there's anything you don't understand.
It's worth noting that moving a file to itself will fail. I had a method that replaced spaces with underscores and made the file name lowercase and renamed the file to the new name. Files with only one word in the name would fail the rename as the new name would be identical on a case-insensitive file system.
The way I resolved this was to do a two step rename, first renaming the file to a temporary name and then renaming it to the intended name.
Some pseudocode explaining this:
NSString *source = #"/FILE.txt";
NSString *newName = [[source lastPathComponent] lowercaseString];
NSString *target = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
[[NSFileManager defaultManager] movePath:source toPath:target error:nil]; // <-- FAILS
The solution:
NSString *source = #"/FILE.txt";
NSString *newName = [[source lastPathComponent] lowercaseString];
NSString *temp = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#-temp", newName]];
NSString *target = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
[[NSFileManager defaultManager] movePath:source toPath:temp error:nil];
[[NSFileManager defaultManager] movePath:temp toPath:target error:nil];
I just wanted to make this easier to understand for a newbie. Here's all the code:
NSString *oldPath = #"/Users/brock/Desktop/OriginalFile.png";
NSString *newFilename = #"NewFileName.png";
NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
[[NSFileManager defaultManager] movePath:oldPath toPath:newPath handler:nil];
NSLog( #"File renamed to %#", newFilename );
here's a more recent example for iOS, the NSFileManager method is a little different:
NSString *newFilename = [NSString stringWithFormat:#"%#.m4a", newRecording.title];
NSString *newPath = [[newRecording.localPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
[[NSFileManager defaultManager] moveItemAtPath:newRecording.localPath toPath:newPath error:nil];
For the icing on top, a category on NSFileManager:
#implementation NSFileManager (FileManipulations)
- (void)changeFileNamesInDirectory:(NSString *)directory changeBlock:(NSString * (^) (NSString *fileName))block
{
NSString *inputDirectory = directory;
NSFileManager *fileManager = [NSFileManager new];
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:inputDirectory error:nil];
for (NSString *fileName in fileNames) {
NSString *newFileName = block(fileName);
NSString *oldPath = [NSString stringWithFormat:#"%#/%#", inputDirectory, oldFileName];
// move to temp path so case changes can happen
NSString *tempPath = [NSString stringWithFormat:#"%#-tempName", oldPath];
NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFileName];
NSError *error = nil;
[fileManager moveItemAtPath:oldPath toPath:tempPath error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
return;
}
[fileManager moveItemAtPath:tempPath toPath:newPath error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
}
}
}
#end

OSX Application, get file data that user selected using NSOpenPanel

I am new to developing OSX Applications. I normally do iOS Apps, so alot of the concepts carry over. However, I cannot quite seem to figure out why I cannot retrieve the data of a file on my system.
Is there something that needs to be done first in order to read files on the users system?
Here is what I have:
- (IBAction)btnBrowse:(id)sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel beginWithCompletionHandler:^(NSInteger result){
if (result == NSFileHandlingPanelOKButton) {
//Get the file url user selected
NSURL *file = [[panel URLs] objectAtIndex:0];
//Get the file data
NSError *error;
NSString *fileData = [NSString stringWithContentsOfFile:[NSString stringWithFormat:#"%#", file] encoding:NSUTF8StringEncoding error:&error];
//This returns null
NSLog(#"%#", fileData);
//This says that the file does not exist
NSLog(#"Error: %#", error.localizedDescription);
}
}];
}
NSOpenPanel returns NSURL objects, the easiest solution is to use the NSURL related API
NSString *fileData = [NSString stringWithContentsOfURL:file encoding:NSUTF8StringEncoding error:&error];
If you don't know the text encoding you could use this API, if the reading succeeds, encoding contains the used encoding.
NSStringEncoding encoding;
NSString *fileData = [NSString stringWithContentsOfURL:file usedEncoding:&encoding error:&error];
Use the -[NSURL path] method instead of calling 'file' directly, as it's a NSURL, not NSString.