-[NSString writeToFile:] does not change the contents of the file - ios

I'm trying to write to a file "index.html" that I have in my resources. I can load the file with no problem, but I can't seem to write to it. Nothing is showing up as an error, it simply doesn't write. The app doesn't abort or anything, but when I re-load the file nothing has changed.
My writing code:
NSBundle *thisBundle = [NSBundle bundleForClass:[self class]];
NSString *path = [thisBundle pathForResource:#"index" ofType:#"html"];
NSString *myString = [[NSString alloc] initWithFormat:#""];
[myString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
My loading code:
[myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]isDirectory:NO]]];
What am I doing wrong?

The reason that your existing code doesn't overwrite the index.html file is that an application can not overwrite its resources. Apple's iOS Application Programming Guide specifically says:
This is the bundle directory containing the application itself. Do not write anything to this directory. To prevent tampering, the bundle directory is signed at installation time. Writing to this directory changes the signature and prevents your application from launching again.
Instead, write to your documents directory. You can get the path to the documents directory like this:
NSString * docsDir = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
Note that NSHomeDirectory() on iOS simple returns the path to your application's bundle. Once you have the path to the documents directory, you can write to a resource, let's say index.html, as follows:
NSString * path = [docsDir stringByAppendingPathComponent:#"index.html"];
[myString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
Note that I changed your error: parameter to nil. This will not actually affect anything, but it is common practice to use nil to indicate NULL Objective-C objects.

Try to move your file to Documents Directory before perform the operation this bunch of code makes the work
.h
#import <Foundation/Foundation.h>
#interface NSFileManager (NSFileManagerAdds)
+ (NSString*) copyResourceFileToDocuments:(NSString*)fileName withExt:(NSString*)fileExt;
#end
.m
#import "NSFileManager + NSFileManagerAdds.h"
#implementation NSFileManager (NSFileManagerAdds)
+ (NSString*) copyResourceFileToDocuments:(NSString*)fileName withExt:(NSString*)fileExt
{
//Look at documents for existing file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", fileName, fileExt]];
NSFileManager* fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:path])
{
NSError *nError;
[fileManager copyItemAtPath:[[NSBundle mainBundle] pathForResource:fileName ofType:fileExt] toPath:path error:&nError];
}
return path;
}
#end
Finally you should use it in something like that:
[NSFileManager copyResourceFileToDocuments:#"index" withExt:#"html"];

Related

How can I edit a file in app bundle?

In my app, I load data from a CSV file stored in the bundle resources. However, I'd like to be able to update this file programmatically when the user taps the Update button. Is there a way to change a resource in the app bundle programatically? Here is the code I use to access the file:
NSString *path = [[NSBundle mainBundle] pathForResource:#"query_result" ofType:#"csv"];
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:path];
First of all load file from bundle and after that just store in Document directory then you can make change on this file again save on document directory to access changed file.
Use this code copy from mainbundle to Document.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dataPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"tessdata"];
NSLog(#"Datapath is %#", dataPath);
// If the expected store doesn't exist, copy the default store.
if ([fileManager fileExistsAtPath:dataPath] == NO)
{
NSString *tessdataPath = [[NSBundle mainBundle] pathForResource:#"eng" ofType:#"traineddata"];
[fileManager copyItemAtPath:tessdataPath toPath:dataPath error:&error];
}
-(NSString*) applicationDocumentsDirectory{
// Get the documents directory
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
return docsDir;
}
And then get the Saved file to change...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:#"recentDownload.txt"];

Accessing and using the files in the app sandbox

I have an iOS application in which I download some files from a remote server and store them in the app sandbox directory, Documents. I am sure the files are saved in the sandbox properly because when I run the following piece of code:
NSFileManager* fileManager = [NSFileManager defaultManager];
NSString* documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSError* error;
NSLog(#"%#",[fileManager contentsOfDirectoryAtPath:documentPath error:&error]);
I get the following output:
2014-07-09 17:39:07.768 Sample[13413:60b] (
"test.json",
"test.png"
)
However, I can not access these files in the Documents directory. I try to get the contents of test.json with the following code:
NSString* testJsonDir = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"json"];
NSString* testJson = [NSString stringWithContentsOfFile:testJsonDir encoding:NSUTF8StringEncoding error:&error];
This returns testJsonDir as nil and gives an error with code 258. Is there some other way to do this?
[NSBundle mainBundle] refers to the .app bundle, not the Documents directory. To access test.json, use this:
NSError *error = nil;
NSURL *documentsUrl = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];
NSString *testJson = [NSString stringWithContentsOfURL:[documentsUrl URLByAppendingPathComponent:#"test.json"] encoding:NSUTF8StringEncoding error:&error];
I'm using -URLsForDirectory:inDomains: instead of NSSearchPathForDirectoriesInDomains() because former is a preferred way.
[NSBundle pathForResource:ofType:] will locate files within the app bundle, not the documents folder.
You want:
NSString *testJsonDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *testJsonFile = [testJsonDir stringByAppendingPathComponent:#"test.json"];
NSString *testJson = [NSString stringWithContentsOfFile:testJsonFile
encoding:NSUTF8StringEncoding
error:&error];

Copy images from an app to Documents Directory and rewrite them after it

I have an app where I have a certain amount of .jpg pictures (roughly 300). They serve as something to start with, because they're actually located in the internet, but obviously it's more convenient for user not to download all of them at the first start of the App, but to have them pre-packed.
I am required to rewrite these images everytime I get new information from server. Obviously, I can't touch the app bundle, so I see my steps like this:
Unpack the images from bundle to the Documents Directory at the first start of an App.
Access them only from Documents Directory, but not from bundle.
If it's necessary, I should rewrite them.
And thus my code will be unified, cause I will always use the same path to get the image.
The problem is that I know very little about the whole file system thing in iOS, so I don't know how to unpack the particular bundle contents to Documents Directory and also I don't know how to write to Documents Directory either.
Could you please help me with some code and also confirm that my solution scheme is right?
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *destPath = [documentsDirectory stringByAppendingPathComponent:#"images"]; //optionally create a subdirectory
//"source" is a physical folder in your app bundle. Once that has a blue color folder (not the yellow group folder)
// To create a physical folder in your app bundle: drag a folder from Mac's Finder to the Xcode project, when prompts
// for "Choose options for adding these files" make certain that "Create folder references for …" is selected.
// Store all your 300 or so images into this physical folder.
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"source"];
NSError *error;
[[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destPath error:&error];
if (error)
NSLog(#"copying error: %#", error);
Edited per additional comment from the OP:
To rewrite with the same file name to the same directory, you can use a combination of fileExistsAtPath and removeItemAtPath to detect and remove the existing file before writing.
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
}
// now proceed to write-rewrite
Try this code
-(void)demoImages
{
//-- Main bundle directory
NSString *mainBundle = [[NSBundle mainBundle] resourcePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = [[NSError alloc] init];
NSArray *mainBundleDirectory = [fm contentsOfDirectoryAtPath:mainBundle error:&error];
NSMutableArray *images = [[NSMutableArray alloc]init];
for (NSString *pngFiles in mainBundleDirectory)
{
if ([pngFiles hasSuffix:#".png"])
{
[images addObject:pngFiles];
}
}
NSLog(#"\n\n Doc images %#",images);
//-- Document directory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
//-- Copy files form main bundle to document directory
for (int i=0; i<[images count]; i++)
{
NSString *toPath = [NSString stringWithFormat:#"%#/%#",documentDirectory,[images objectAtIndex:i]];
[fileManager copyItemAtPath:[NSString stringWithFormat:#"%#/%#",mainBundle,[images objectAtIndex:i]] toPath:toPath error:NULL];
NSLog(#"\n Saved %#",fileManager);
}
}

Return NSBundle file path that is created before from an URL

So I got a function:
+ (NSString *)dataFilePath:(BOOL)forSave {
NSURL *url = [NSURL URLWithString:#"https://dl.dropbox.com/u/35612216/sample.xml"];
NSData *data = [NSData dataWithContentsOfURL:url]; // Load XML data from web
// construct path within our documents directory
NSString *applicationDocumentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *storePath = [applicationDocumentsDir stringByAppendingPathComponent:#"sample.xml"];
// write to file atomically (using temp file)
[data writeToFile:storePath atomically:TRUE];
return [[NSBundle mainBundle] pathForResource:storePath ofType:#"xml"];
//return [[NSBundle mainBundle] pathForResource:#"sample" ofType:#"xml"];
}
And this seems to not work, but when I add a supporting file in my project (sample.xml) and just use this line:
return [[NSBundle mainBundle] pathForResource:#"sample" ofType:#"xml"];
It will work. But I need to get my XML data from the url instead of the supporting file (resource file)
Does anybody know how I can fix this?
The NSBundle method loads the file from the app's bundle, while you've saved the file to the app's Documents directory (which are different locations).
All you need to do is return storePath; which will give you the NSString representation of where the file is located.

Delete image from app directory in iPhone

I want to delete an image from my iPhone app.
I use the method below, passing the name of the image as an argument.
The problem is that the image isn't deleted.
- (void)removeImage:(NSString*)fileName {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.png", fileName]];
[fileManager removeItemAtPath: fullPath error:NULL];
NSLog(#"image removed: %#", fullPath);
NSString *appFolderPath = [[NSBundle mainBundle] resourcePath];
NSLog(#"Directory Contents:\n%#", [fileManager directoryContentsAtPath: appFolderPath]);
}
The last two lines show the content in my app directory and the image I want to delete is still there. What am I doing wrong?
You are trying to delete a file in the Documents directory. You then read the contents of the bundle resources directory. These are not the same directory.
If you're trying to delete a file in the Documents directory, the you should rad that directory in your NSLog() at the end. If you're trying to delete a file inside your bundle, this is impossible. App bundles are signed and cannot be modified.
your code looks ok, so try adding some 'NSError' object to you code:
- (void)removeImage:(NSString*)fileName {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.png", fileName]];
NSError *error = nil;
if(![fileManager removeItemAtPath: fullPath error:&error]) {
NSLog(#"Delete failed:%#", error);
} else {
NSLog(#"image removed: %#", fullPath);
}
NSString *appFolderPath = [[NSBundle mainBundle] resourcePath];
NSLog(#"Directory Contents:\n%#", [fileManager directoryContentsAtPath: appFolderPath]);
}
In the code above I passed a NSError the error parameter of removeItemAtPath. If the system can't delete the file, this method will return NO and fill the error object with the error raised.
Based on your comment I found out that you are trying to delete the default.png and replace it with another one. Unfortunately, this is impossible. The image default.png is a part of your application bundle, which cannot be modified once it has been created and signed (this is a security measure from Apple, so applications cannot change after they have been reviewed). The only locations where you can create and delete files is inside the sandbox given to your application (the Documents folder).

Resources