i added a key called "App" to my AppName-Info.plist manually , i can get the value from it by calling this code
NSBundle *mainBundle;
mainBundle = [NSBundle mainBundle];
NSString *value = [mainBundle objectForInfoDictionaryKey:#"App"];
NSLog(#"App: %#",value);
But what i couldn't do is changing the value with any code.. is it possible ? and if yes how can it be done ?
thanks for help in advance :)
You should consider NSUserDefaults or if you want to modify a bundled .plist try this.
NSString* plistFilePath = nil;
NSFileManager* manager = [NSFileManager defaultManager];
if ((plistFilePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"mySpecial/PathTo.plist"]))
{
if ([manager isWritableFileAtPath:plistFilePath])
{
NSMutableDictionary* infoDictioio = [NSMutableDictionary dictionaryWithContentsOfFile:plistFilePath];
[infoDictio setObject:#"foo object" forKey:#"fookey"];
[infoDictio writeToFile:plistFilePath atomically:NO];
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:nil];
}
}
You should not modify your apps Info.plist file (or anything in your app's bundle) at runtime. This is bad practice and will also break your bundles code signature (which will result in not being able to launch the app anymore!).
If you want to store settings you should have a look at NSUserDefaults.
It's not possible.
By default the AppName-Info.plist isn't copied into the bundle in the Copy Bundle Resources phase of the build.
So if you want to have a plist which you can write to an option would be to create it at run time in the temporary files location and read/write to it there.
This is a great place to research how to do it.
Related
I am creating an epub reader. In that I want to list out .epub files from iphone. So I want to know is there any possible way to list out the .epub files from iphone (not just from the project directory path but also anywhere else in the phone)?
No, this is not possible.
Since there is no filesystem access, except the the directory with in apps sandbox.
All apps have to store the files they use with there sandbox, you tell iOS that you app can op .epub files. Which will allow the user to open the file from, example an email in your app.
As answered by #rckoenes, it is not possible to access the filesystem other than your app bundle.
You can access the files in your app bundle like this:
NSString *bundlePathName = [[NSBundle mainBundle] bundlePath];
NSError *error;
NSArray *bundleContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePathName error:&error];
for (NSString *currentItem in bundleContents) {
if ([currentItem rangeOfString:#"." options:NSBackwardsSearch].location != NSNotFound) {
int tempIndex = (int)([fileName rangeOfString:#"." options:NSBackwardsSearch].location);
tempIndex++;
NSString *aStrExtension = [[fileName substringWithRange:NSMakeRange(tempIndex, [fileName length]-tempIndex)] lowercaseString];
if ([aStrExtension isEqualToString:#"epub"]) {
//Add this file to an array, to make it available for choosing and view its details
}
}
}
If you mean you want to open ePub files saved not inside your application bundle, then you cant, you will have access only to the files inside your app sandbox
As per the #rckoenes: Any files out of App bundle is not accessible,
So I retrieved .epub files like this way.
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot];
NSString *filename;
while ((filename = [direnum nextObject] )) {
if ([filename hasSuffix:#".epub"]) { //change the suffix to what you are looking for
[arrayListofEpub addObject:[filename stringByDeletingPathExtension]];
}
}
So I've read a ton of SO-questions about plists and how to save to them, and although I don't know, why no iPhone-Dev-Book I've seen so far covered this (they all used the tableView editing function), I managed to REALLY write to a plist by copying it to the documents folder like this:
pListPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
pListPath = [pListPath stringByAppendingPathComponent:#"piggyBanks.plist"];
NSLog(#"Path: %#", pListPath);
// if the file does not exist yet, create it, and copy the plist data into it, that can be found in the bundle
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:pListPath]) {
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"piggyBanks" ofType:#"plist"];
[fileManager copyItemAtPath:sourcePath toPath:pListPath error:nil];
}
// make the plist content available for usage
pListContent = [[NSMutableDictionary alloc] initWithContentsOfFile:pListPath];
NSLog(#"pListContent: %#", pListContent);
So far so good, but now, if I wanna change some plist value when a user taps on a tableViewCell (it's a custom one, if that's important), although pListPath, pListContent and others are properties, defined in .h and synthesized in .m, I have to redefine pListPath and pListContent inside didSelectRowAtIndexPath, to get the path to be known in that moment.
Could someone please tell me why? I mean, it's nice, that it works, but I'd like to know, why it has to be like that, or if I did a mistake somewhere else..
Thanks!
If plistPath is a string property of your class, you need to assign it as such:
if (!self.pListPath)
{
NSString *newPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
newPath = [newPath stringByAppendingPathComponent:#"piggyBanks.plist"];
self.pListPath = newPath;
}
Then afterwards, use self.pListPath instead of pListPath.
You are having to re-set it because at the moment, pListPath is being assigned to an autoreleased string which will have been removed by the time you need it again. Setting the property (assuming the property is retained or copied) will retain the string for you.
I googled for class variables in objective C, and found my way through various articles, till i found this blog/blogpost, which explains the self. and _underscore thing really well!
I now always declare my Ivars with an underscore, the properties without. Works out quite well. And to sum it up, why to ALWAYS use self.yourPropertiesName, you are calling a method (setter and getter)! And like any other method you are calling, you need to say, who is calling.
Hope this will help someone else too :)
I am trying to get started with unit testing an app that uses Core Data. In the setUp method of my unit first test, I can get the path to my data model but for some reason cannot convert it to a NSURL.
My setUp method is:
- (void)setUp {
NSBundle *bundle = [NSBundle bundleWithIdentifier:#"com.testcompany.LogicTests"];
STAssertNotNil(bundle, #"Error finding bundle to create Core Data stack.");
NSString *path = [bundle pathForResource:#"DataModel" ofType:#"momd"];
STAssertNotNil(path, #"The path to the resource cannot be nil.");
NSURL *modelURL = [NSURL URLWithString:path];
STAssertNotNil(modelURL, #"The URL to the resource cannot be nil. (tried to use path:%#, modelURL is %#)", path, modelURL);
...
}
The error I'm getting is:
/Users/neall/iPhone Apps/TestApp/UnitLogicTests.m:24:0 "((modelURL) != nil)" should be true. The URL to the resource cannot be nil. (tried to use path:/Users/neall/iPhone Apps/TestApp/build/Debug-iphonesimulator/LogicTests.octest/DataModel.momd, modelURL is (null))
I've checked the filesystem and the directory /Users/neall/iPhone Apps/TestApp/build/Debug-iphonesimulator/LogicTests.octest/DataModel.momd exists.
What am I missing here?
Thanks!
Try using [NSURL fileURLWithPath:path] instead to construct the url
Double check that you are seeing a directory called DataModel.momd at /Users/neall/iPhone Apps/TestApp/build/Debug-iphonesimulator/LogicTests.octest/DataModel.momd.
If you added a xcdatamodel file by the Add New File... command in Xcode, you would only have one file and it would be DataModel.mom (no trailing d). If that's the case, changing the
NSString *path = [bundle pathForResource:#"DataModel" ofType:#"momd"];
to
NSString *path = [bundle pathForResource:#"DataModel" ofType:#"mom"];
will fix your immediate issue.
You want to use the fileURLWithPath: that Claus suggested as well.
If you want to do versioning of your model in the future and you currently have only a .mom file, select your DataModel.xcdatamodel file in XCode and go to Design -> Data Model -> Add Model Version. This will force the creation of the DataModel.momd directory with the DataModel.mom file in it. You can just delete the new version it adds into that directory and your original tests will work.
xcdatamodel should also be added to
Project -> Targets -> "unit test target" -> build phases -> compile sources
After spending several hours stacking in July 2014 this post was one of several that in part led me to the working solution.
We somehow managed to break the surprisingly fragile (and mysterious) mechanism that links the bundle that your source code lives in to the bundle that runs the unit test. Further you might have a misnamed xcdatamodel. See comments for explanations:
-(NSManagedObjectContext *) getManagedObjectContext
{
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
//Replace MyClass with class that is from your data model
//really any of your classes should work
NSBundle * bundle = [NSBundle bundleForClass:[MyClass class]];
//You can uses this line to figure you what your bundle is actually named
//In my case the because my PRODUCT_NAME had spaces in it they was replaced with '-'
//(dashes) and I couldn't divine it from the info.plist and the Build Settings.
NSString * ident =[bundle bundleIdentifier];
//This will show you where your app is actually out building temporary files
//The exact location appears to change every version or to of Xcode so
//this is useful for figuring out what your model is named
NSString * bundlePath =[bundle bundlePath];
//Here replace Name_of_model_without_the_dot_xcdatamodel with the name of your
//xcdatamodel file without an extension
//Some tutorials will have you use AppName.xcdatamodel others will simply name it
//DataModel.xcdatamodel.
//In any event if bothe path and path1 return null then check the
//bundlePath by going to Finder and pressing Command-Shift-G and pasting
//bundlePath into the pop-up. Look around for a mom or momd file thats the name you want!
NSString* path = [bundle
pathForResource:#"Name_of_model_without_the_dot_xcdatamodel"
ofType:#"momd"];
//If the above 'path' and 'path1' is not then you want to use this line instead
NSString* path1 = [bundle
pathForResource:#"Name_of_model_without the_dot_xcdatamodel"
ofType:#"mom"];
//the above path lines are simply so you can trace if you have a mom or a momd file
//replace here appropriately
NSURL *modelURL = [bundle URLForResource:#"Name_of_model_without the_dot_xcdatamodel"
withExtension:#"momd"];
//the rest is boiler plate:
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSPersistentStoreCoordinator *psc =
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
[psc addPersistentStoreWithType:NSInMemoryStoreType
configuration:nil URL:nil options:nil error:nil];
[moc setPersistentStoreCoordinator:psc];
return moc;
}
Here is how you might use the above context:
-(void)testMyStuff
{
NSManagedObjectContext* context=[self getManagedObjectContext];
MyClass *myobj=[NSEntityDescription insertNewObjectForEntityForName:#"MyClass"
inManagedObjectContext:context];
}
One final note you may also have to add your source files and xcmodel under the "Compile Sources" of build phases. This unfortunately changes with almost every version of Xcode. For Xcode 5:
I'm in trouble with paths, relative paths, NSBundle and all the path/file related operations :)
While i run the simulator everthing goes right but all the file changes are not permanent, so everytime i run my app i have to repeat the initial setup of my app.
The question:
What is the proper way to read and write files (from resource dir) and make all the file changes permanent (updated into the project folder) ?
Thanks
EDIT: i write a simple method to do it, it's correct ?
-(NSString *) getPath:(NSString *)forResource
{
NSString *pathRes, *docPath, *destPath;
NSArray *foundPaths;
NSFileManager *fs = [[NSFileManager alloc] init];
foundPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
docPath = [foundPaths objectAtIndex:0];
destPath = [[NSString stringWithFormat:#"%#/%#",docPath,forResource] stringByStandardizingPath];
if(![fs fileExistsAtPath:destPath])
{
pathRes = [[NSBundle mainBundle] pathForResource:forResource ofType:nil];
[fs copyItemAtPath:pathRes toPath:destPath error:nil];
}
return destPath;
}
You cannot write to the application folder on the device. Write any persistent data to your documents directory. You can copy files from the resources directory to the documents directory on the first run to keep your existing logic.
NSSearchPathForDirectoriesInDomains( NSDocumentDirectory , NSUserDomainMask , YES );
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.