I need to write a string into a file. For that, my code is:
-(void)writeToFile:(NSString *)fileName: (NSString *)data {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// the path to write file
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
[data writeToFile:appFile atomically:YES];
}
I am calling this method like this
ownServices *obj = [[ownServices alloc]init];
[obj writeToFile:#"iphone.txt" :#"this is mahesh babu"];
but it didn't write into the text file.
What's the reason? Can anyone please help me.
Thank u in advance.
The most likely problem is that the documents directory does not exist. Create it if it doesn't, then write to it:
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *parentDir = [paths objectAtIndex:0];
/* Create the parent directory.
* This is expected to fail if the directory already exists. */
(void)[[NSFileManager defaultManager]
createDirectoryAtPath:parentDir
withIntermediateDirectories:YES
attributes:nil error:NULL];
NSString *path = [parentDir stringByAppendingPathComponent:fileName];
/* Now write, and if it fails, you'll know why thanks to the |error| arg. */
NSError *error = nil;
BOOL ok = [data writeToFile:path options:NSDataWritingAtomic error:&error];
if (!ok) {
NSLog(#"%s: Failed to write to %#: %#", __func__, path, error);
}
Even simpler would be to use the latest API, which will create the directory for you if it doesn't already exist:
NSError *error = nil;
NSURL *parentURL = [[NSFileManager defaultManager]
URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask
appropriateForURL:nil create:YES error:&error];
if (!parentURL) {
NSLog(#"%s: *** Failed to get documents directory: %#", __func__, error):
return;
}
NSURL *furl = [parentURL URLByAppendingPathComponent:fileName];
error = nil;
BOOL ok = [data writeToURL:furl options:NSDataWritingAtomic error:&error];
if (!ok) {
NSLog(#"%s: *** Failed to write to %#: %#", __func__, furl, error);
}
Firstly, you are calling your method strangely. Rename the method to
-(void)writeString:(NSString *) data toFile:(NSString *)fileName
and call it like so:
[obj writeString:#"this is mahesh babu" toFile:#"iphone.txt"];
Secondly, writeToFile:atomically: is deprecated, use writeToFile:atomically:encoding:error::
NSError *error = nil;
BOOL success = [data writeToFile:appFile atomically:YES encoding:NSUTF8Encoding error:&error];
if (!success) {
NSLog(#"Error: %#", [error userInfo]);
}
This way, you also see what the error is.
Your code looks OK. Use the debugger (or an NSLog statement) to verify the values of data and appFile. If data is nil, nothing will happen (including no errors) because sending a message to nil is a no-op. It's also possible that appFile is not the path you think it is.
Check the permissions of the directory you are trying to write to (ls -la). On the device you can't, but on the simulator you can. Is it read-only for you? Is it owned by another user?
Assuming that isn't the problem, try calling with atomically:NO. Atomic file writing is performed by writing a file, then renaming it to replace the old one. If the problem is there, that will isolate the problem.
Bonus Style Critique
Class names should start with an uppercase letter: OwnServices instead of ownServices
Although your method name is perfectly valid, it's unusual to have two parameters with no words to separate them. A name like writeToFile:string: would be better.
Don't name a variable data if it is meant to point to an instance of something other than NSData. It's confusing, and there's almost a better (more specific) word you can use beside "data".
Related
As part of my app start-up i copy bundle files to my documents directory.
This works fine for three out of four of my files but the fourth one create a Zero KB file.
running on iOS 5.0 sim. I have cleaned the build several times and checked the file name capitalization vis correct.
the file appears in the directory but is zero kb and should be 24K
any help appreciated.
-(BOOL) CheckDBs: (NSString *)dbname
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:dbname];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL success = [fileManager fileExistsAtPath: dbPath];
NSLog(#"AppDelegate CheckDatabase: %# = %i", dbPath, success);
if (success) {
//NSLog(#"return YES");
return YES;
}
else {
return NO;
}
} // Complete - checks if files exist in the User Documents directory
-(void) copyDBs: (NSString *) dbname
{
//Using NSFileManager we can perform many file system operations.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:dbname];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname];
BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (success) {
// Version 4.0 code
//NSDictionary *attribs = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
//success = [fileManager setAttributes:attribs ofItemAtPath:dbPath error:&error];
NSLog(#"AppDelegate copyDatase: %# = %d", dbPath, success);
}
//NSLog(#"AppDelegate copyDatase: %# = %d", dbPath, success);
if (!success) {
NSLog(#"Failed to copy database: '%#'", [error localizedDescription]);
// NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Have you also checked the original file size?
Try resetting your simulator. From the NSFileManager documentation:
If a file with the same name already exists at dstPath, this method
aborts the copy attempt and returns an appropriate error.
Make sure the destination is empty and try again. Also, check the error object.
If all that checks out there has got to be an error in spelling the file name. Check if the exact file exists in bundle, NSLog wherever you use a file name or path, etc. You should find the error. Also check the appropriate folder in the Finder.
Instead of using
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname]
try
[[NSBundle mainBundle] pathForResource:shortName ofType:#"db"]
Ok I figured out what is causing the problem.
as i run the app the appdidfinishlaunching method is not complete before one of the view controllers is loading. That view controller attempts to access one of the files being copied over from the bundle.
I'm guessing that sqlite creates the file when you attempt to access the database, it creates it with with a zero bytes length.
So when my appdidfinish launching method checks for the existance of the file it exists due to the sql call.
This is usually only going to be a problem prior to the first run of the app as after that the database will exist.
problem now is how do i get the appdidfinish launching to complete prior to the rest being allow to start as the view controller in question is part of the mainwindow.xib
In my iOS app I would like that user can download some jpg file via iTunes. So I've enabled UIFileSharingEnabled. But users are now able to put files in my app. I would like to block that. Is there a way to do that ?
Thanks !
Don't think you can block it, but you can just delete unwanted files when your app becomes active.
Put some code a bit like the sample below - filling in the test to avoid deleting the files you want to be available in iTunes.
Call this from within applicationDidBecomeActive: in your application delegate.
If you're more cautious you might want to check the user hasn't dropped a jpg file with the same name as the one you've parked there. You could test for sameness of date or some such or, if you've not got many files, just delete everything and write them again when the app becomes active.
- (void) removeUnwantedFiles;
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:inboxPath error:NULL];
if (!directoryContents || [directoryContents count] == 0)
{
return;
}
for (NSString* fileName in directoryContents)
{
if ( /* some test of filename to see if it's one of my kosher files */ ) continue;
NSString* filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSError* error = nil;
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
// NSLog(#"Deleting (%#): %#", success ? #"succeeded" : #"failed", [filePath lastPathComponent]);
if (!success)
{
NSLog(#"Error: %#", [error localizedDescription]);
}
}
}
I'm simply trying to create a secure user file that will save basic login information so it does not need to be typed in every time the app launches. I know I cannot write to the bundle, so I am trying one of the other directories we are supposed to have access to, like caches, documents, and library, none of these have resulted in a successful write
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, NO);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSLog(#"Found cache directory: %#", cacheDirectory);
NSString *fileName = [cacheDirectory stringByAppendingPathComponent:#"987f065e4656dba6c"];
NSString *userData = [[NSString alloc] initWithFormat:#"%#\n%#\n%#", owner.schoolWebsite.text, owner.username.text, owner.password.text];
NSError *fileError = nil;
if(!([[NSFileManager defaultManager] createDirectoryAtPath:cacheDirectory withIntermediateDirectories:YES attributes:nil error:&fileError]))
{
NSLog(#"Error creating cache directory: %#\n%#\n%#\n%#", [fileError localizedDescription], [fileError localizedFailureReason], [fileError localizedRecoveryOptions], [fileError localizedRecoverySuggestion]);
}
if (!([[NSFileManager defaultManager] createFileAtPath:fileName contents:[userData dataUsingEncoding:NSASCIIStringEncoding] attributes:nil] ) )
{
NSLog(#"ERROR WRITING TO FILE: %#\n%#\n%#\n%#", [fileError localizedDescription], [fileError localizedFailureReason], [fileError localizedRecoveryOptions], [fileError localizedRecoverySuggestion]);
}
this has been plaguing me for a while, and this only fails on the device
Try replacing this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, NO);
wit this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
I've replaced NSLibraryDirectory (/Library) with NSCachesDirectory (/Library/Cache). Also denoted to expand tilde to full path which probably is the main cause behind this.
I had some problems after starting a new coredata project with the xcode 3.2.5... my previous projects with core data (in previous xcode) worked fine, so I dont know what is the difference??
so the error I get when I build and go to the view that calls the core data is>
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter'
the strange thing is that in my *AppDelegate.m, in (edited thanks Rog but still not working!)
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"staff.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath:storePath]; //new position for the storeUrl!
// Put down default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"staff" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
in the
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"staff.sqlite"];
I get the warning
NSURL may not respond to '-stringByAppendingPathComponent'
I option + click this stringByAppendingPathComponent and get (Symbol not found!!!!)
but in other projects I do option + click in the same and get the definition!!
so is this warning related to my error??
how to fix it???
Edit,
included this in my viewDidLoad
NSLog(#"path= %#", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]) ;
which gives me in console:
path= /Users/mkss9/Library/Application Support/iPhone Simulator/4.2/Applications/2F364C20-2B87-4ABB-AA3E-FB6F7C15096F/Documents
please!, Im getting crazy !!
thank you!
Some SDK Version ago (I don't know for sure when they did) apple changed the return type of applicationDocumentsDirectory in their project templates.
When you create a new project it looks like this:
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
in older templates it looked like this:
/**
Returns the path to the application's documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
and in between those two it looked like this:
/**
Returns the path to the application's Documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
So you have to be careful, because all the old code that relies on applicationDocumentsDirectory returning a NSString won't work with newer templates.
And you can't just replace the new version with the older version because this would result in a change in your core data methods.
So I would suggest you to write your own method for returning the documents directory. Apple changes their applicationDocumentsDirectory quite often.
I would imagine it's because -applicationDocumentsDirectory returns an NSURL * instead of an NSString *.
Firstly you need to make sure you applicationDocumentsDirectory method is returning a NSString.
Once that's out of the way, the subsequent crash is because you are passing a path and filename that don't exist yet.
So if you move your NSURL *storeUrl = [NSURL fileURLWithPath:storePath]; to after the code that checks for an existing file and puts a default one in case it doesn't exist, it should solve your problem.
NSFilemanager is returning true for the following, when there should not be any such file there yet. What is happening?
if([myManager fileExistsAtPath:[[self documentsDirectory] stringByAppendingPathComponent:#"Music/songlist.txt"]]){
NSLog(#"file is there");
}
The documentation for NSFileManager seems to recommend not checking to see if files exist, and instead just trying to read the file and handle any errors gracefully (e.g. file not found error). What you are describing doesn't sound like a race condition—which is what the documentation's recommendation is trying to circumvent—but what happens if you just try to load the file rather than checking to see if it exists? You could, for example, try the following:
NSError *error;
NSStringEncoding encoding;
NSString *fileContents = [NSString stringWithContentsOfFile:fileName
usedEncoding:&encoding
error:&error];
if (fileContents == nil)
{
NSLog (#"%#", error);
}
else
{
NSLog (#"%#", fileContents);
}
If you get a string with all of the file's contents, then the file is obviously there. If you get an error then something is up with myManager.
Print this out and see if it is what you expect.
NSLog(#"Directory: %#", [[self documentsDirectory] stringByAppendingPathComponent:#"Music/songlist.txt"]];
Also, check that you are defining myManager correctly.
This works as expected for me.
NSFileManager *myManager = [NSFileManager defaultManager];
NSString *documentsDirectory = NSHomeDirectory();
if([myManager fileExistsAtPath:[documentsDirectory stringByAppendingPathComponent:#"Music/songlist.txt"]]){
NSLog(#"file is there");
}