I am reading a file in iphone app. Its Works Fine.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSLog(#"%#",documentsDirectory);
NSString *fileName = [NSString stringWithFormat:#"%#/test.txt",documentsDirectory];
NSLog(#"%#",fileName);
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName
usedEncoding:nil
error:nil];
NSLog (#"%#",content);
}
works good when i am using filename as test.txt
But when i add another file in the resource suppose test1.txt then NSLog(#"%#",documentsDirectory) and NSLog(#"%#",fileName) shows the right result. But
NSLog (#"%#",content); prints null in the log. So what is the reason?
I am printing detail error and it prints
NSFilePath = "/Users/sam-xxx/Library/Application Support/iPhone Simulator/3.2/Applications/51197946-6042-4A90-AA39-F07F8A649308/Documents/test1.txt";
NSUnderlyingError = Error Domain=NSPOSIXErrorDomain Code=2 "Operation could not be completed. No such file or directory";
It would be best here to check to see if an error is returned:
NSError *error;
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName
usedEncoding:nil
error: &error];
if (error) NSLog(#"Error !: %#", [error localizedDescription]);
That will (hopefully) give you a clue as to whats going on.
(Edited to give example bundle resource usage as the file is in the bundle not the Documents directory).
Docs for NSBundle are here: NSBundle Documentation
You have 2 choices, the one you suggest:
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
Which will return the Bundle resource directory with the filename appended to that path.
Personally I prefer the pathForResource:ofType: method:
NSString *filename = [[NSBundle mainBundle] pathForResource: #"test1" ofType: #"txt"];
As this will not only tell you if the file exists (returns nil if it does not) but will also search the localisation directories if you have them.
Did you check if the file in the error message exists? When run in the simulator you can simply open Finder, press cmd+shift+g and paste the directory to the file.
Maybe you didn't add it to the target? Open the information window for the file in your resources and check which targets are checked.
When you run a app in Simulator, you could copy and paste the right file into the app's Documents directory use Finder, but on iPhone or iTouch how could you paste a file into the Documents directory.
We got only 2 method to put something into Documents directory when app is running on iPhone or iTouch.
Create one.
Copy one from the bundle.
in your code, you read a file in the Documents directory, you could be paste a file in the directory manually use Finder. Try this.
NSString *path = [[NSBundle mainBundle] pathForResource:#"test.txt" ofType:nil];
NSString *content = [[NSString alloc] initWithContentsOfFile:path
usedEncoding:nil
error:nil];
NSLog (#"%#",content);
Related
I am having some strange problem.
I cant read plist file from application mainbundle
the file is in the "copy bundle resource"
but somehow it can not be read
here is the code
NSLog(#"%#",[[NSBundle mainBundle] pathForResource:#"8chk" ofType:#"plist"]);
it prints (null) all the time
here is the screenshot
can anyone tell me what is going on?
I am afraid to say that , you are not adding this 8chk.plist to your target.
Drag and drop plist file again in project, make sure you tick on checkbox while adding file to project which is saying "copy file in ProjectName"
then use this code to make it accessible and read values from plist
NSString *path = [[NSBundle mainBundle] pathForResource: #"YourPLIST" ofType: #"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: path];
[id obj = [dict objectForKey: #"YourKey"];
OR
// Reads the value of the custom key you added to the Info.plist
NSString *value = [mainBundle objectForInfoDictionaryKey:#"key"];
//Log the value
NSLog(#"Value = %#", value);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Info.plist"];
This might work. make sure to copy the plist to target and put it in root directory.also u can try to put the plist files where the apps default plist is kept.
:)
Hi Shoeb Amin please check your plist file is add inside Target->Copy Bundle resource
Your path is wrong. copy bundle resource should read just 8chk.plist, not 8chk.plist ..in resources/8chk if the project navigator shows the correct tree.
In the Simulator I can save an NSMutableArray to a file and read it back with the following code:
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:#"RiskValues"]){ // If file exists open into table
NSLog(#"Risk Values File Exists");
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullFileName = [NSString stringWithFormat:#"RiskValues", documentsDirectory];
gRiskValues = [[NSMutableArray alloc] initWithContentsOfFile:fullFileName];
gRiskValuesAlreadyInitialised = YES;
} else {
NSLog(#"Can't find RiskValues file, so initialising gRiskValues table");
Do something else .......
}
This doesn't work on the device. I have tried to locate the file using the following but it still doesn't work:
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent#"RiskValues"];
What am I doing wrong?
Great answers from everyone. I have resolved the file path and existence issues at a stroke. Many, many thanks.
You have to provide absolute path here:
if ([fileManager fileExistsAtPath:#"RiskValues"])
So it must look like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent:#"RiskValues"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath: fullFileName]){ // If file exists open into table
NSLog(#"Risk Values File Exists");
gRiskValues = [[NSMutableArray alloc] initWithContentsOfFile:fullFileName];
gRiskValuesAlreadyInitialised = YES;
} else {
NSLog(#"Can't find RiskValues file, so initialising gRiskValues table");
Do something else .......
}
NSString *fullFileName = [NSString stringWithFormat:#"RiskValues", documentsDirectory];
this line, you're not creating your full path string right. what you should do is
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent:#"RiskValues"];
also this check
if ([fileManager fileExistsAtPath:#"RiskValues"])
Will never pass on iOS as it is not a full path to any place you are allowed to write at in your sandbox. I suppose it works on the simulator because on the mac it's looking up relatively to the HD root (or something, not sure how the mac file system works :) ), but on the iOS you're going to have to give it a path to a file/directory in your documents (maybe by appending #"RiskValues" to it or whatever)
1) [NSString stringWithFormat:#"RiskValues", documentsDirectory] is just #"RiskValues". So this name points to file in application's directory.
2) [fileManager fileExistsAtPath:#"RiskValues"] searches for file in application directory. It's available for read/write in simulator (it's in your computer file system after all) but it's read-only on device.
BTW (NSFileManager Class Reference)
Attempting to predicate behavior based
on the current state of the file
system or a particular file on the
file system is not recommended. Doing
so can cause odd behavior in the case
of file system race conditions. It's
far better to attempt an operation
(such as loading a file or creating a
directory), check for errors, and
handle any error gracefully than it is
to try to figure out ahead of time
whether the operation will succeed.
Solution:
1) Do not check file presence. Just try to make dictionary with initWithContentsOfFile:encoding:error:
2) You want it to be in documents directory so construct path like this
[documentsDirectory stringByAppendingPathComponent:#"RiskValues"];
After adding it as a resource, the database file itself is in the project root.
I've only been able to open it by specifying the full path as OS X sees it, i.e., "/Users/Louis/Documents/Test Project/test.db".
But of course there is no such path on an iPhone.
I think I should define the path as "application root/test.db" but I don't know how, or if that would even work anywhere else besides my development machine.
Thanks for any ideas.
To get the path of the file you've added in XCode you would use pathForResource:ofType: with your mainBundle.
NSString *path = [[NSBundle mainBundle] pathForResource:#"yourDb" ofType:#"sqlite"];
But you can't change files in the mainBundle. So you have to copy it to another location. For example to the library of your app.
You could do it like this:
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString *targetPath = [libraryPath stringByAppendingPathComponent:#"yourDB.sqlite"];
if (![[NSFileManager defaultManager] fileExistsAtPath:targetPath]) {
// database doesn't exist in your library path... copy it from the bundle
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"yourDb" ofType:#"sqlite"];
NSError *error = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:targetPath error:&error]) {
NSLog(#"Error: %#", error);
}
}
Don't just use the SQLite API, use this amazing wrapper called FMDB: https://github.com/ccgus/fmdb
Getting Paths to Standard Application Directories
I'm writing an app that copies some contents of the bundle into the applications Document's directory, mainly images and media. I then access this media throughout the app from the Document's directory.
This works totally fine in the Simulator, but not on the device. The assets just come up as null. I've done NSLog's and the paths to the files look correct, and I've confirmed that the files exist in the directory by dumping a file listing in the console.
Any ideas? Thank you!
EDIT
Here's the code that copies to the Document's directory
NSString *pathToPublicationDirectory = [NSString stringWithFormat:#"install/%d",[[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]];
NSString *manifestPath = [[NSBundle mainBundle] pathForResource:#"content" ofType:#"xml" inDirectory:pathToPublicationDirectory];
[self parsePublicationAt:manifestPath];
// Get actual bundle path to publication folder
NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:pathToPublicationDirectory];
// Then build the destination path
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d", [[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]]];
NSError *error = nil;
// If it already exists in the documents directory, delete it
if ([fileManager fileExistsAtPath:destinationPath]) {
[fileManager removeItemAtPath:destinationPath error:&error];
}
// Copy publication folder to documents directory
[fileManager copyItemAtPath:bundlePath toPath:destinationPath error:&error];
I am figuring out the path to the docs directory with this method:
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
}
And here's an example of how I'm building a path to an image
path = [NSString stringWithFormat:#"%#/%d/%#", [self applicationDocumentsDirectory], [[thisItem valueForKey:#"publicationID"] intValue], [thisItem valueForKey:#"coverImage"]];
It turned out to be an issue where the bundle was not being updated on the device and apparently didn't have the same set of files that the Simulator had. This blog post helped a bit: http://majicjungle.com/blog/?p=123
Basically, I cleaned the build and delete the app and installed directly to the device first instead of the simulator. Interesting stuff.
I don't see where documentsDirectory is defined.
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d", [[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]]];
Perhaps the following will do the trick
NSString *destinationPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:[NSString stringWithFormat:#"%d", [[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]]];
Your copy code looks fine, and you say you're getting no errors.
But I'm intrigued by "The assets just come up as null." Are you sure you're accessing the file name later with the exact same name?
Unlike the simulator, a real iPhone has a case sensitive file system.
NSString *myfile = [[NSBundle] mainBundle] pathForResource:#"fileName" ofType:#"plist"];
NSMutableArray *mydata= [[NSMutableArray alloc] initWithContentsOfFile:myfile];
/* code to modify mydata */
[mydata writeToFile:myfile atomically:YES]
In case of simulator 'fileName.plist' is modified but in case of iphone device file remains unchanged. There is no exception seen either.
Is the above code expected to work fine on both iphone and simulator ?
Also in the debugger when I hover over 'mydata' I see different values in case of simulator and device. In case of simulator I see for example, '5 objects' but in case of actual device it shows '{(int)[$VAR count]}'. Could this be related to file not being written ?
You can't write to files in a bundle's resource directory. On top of that you wouldn't want to, because any changes would be overwritten when you update your app. The documents directory persists across versions and (I believe) it is backed up via itunes.
Here is a snippet that checks the documents directory for a plist. if the file doesn't exist it copies a plist out of the resources into the documents directory.
BOOL success;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"score.plist"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return success;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"score.plist"]];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
return success;