How do you read/write to/from and xcode plist? - xcode

I have a utility app with the flipsideview and I'm trying to use the flipsideview to enable the user to change settings that will effect the main view. I thought the easiest way to do this would be to write changes made on the flip side view to a plist but I am new to objective-c and can't seem to find any help in any forums. When I use the code I found online I keep getting a "Initializer element is not a compile-time constant". This is the code:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Settings.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"settings" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error];
}
Also, I am new to objective-c (making the transition from html, css, etc.) and so a description of what's going on would be much appreciated. And please don't just paste a link to the apple developer tutorials. I've been through them and can't seem to grasp it being I learn much better by doing a practical example.
Thanks ahead of time.

Use NSUserDefaults. That's what you're supposed to use for saving user preferences.

Related

UIImage imageWithContentsOfFile: not working on iOS8

When I test my app on iOS8 beta3 and beta5, I found an bug that about [UIImage imageWithContentsOfFile:].
When image resources are stored in sub-bundle of a main bundle, it will return nil if we initialize UIImage via the following method:
NSString *testBundlePath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"bundleā€¯];
NSBundle *bundle = [NSBundle bundleWithPath:testBundlePath];
NSString *path = [bundle pathForResource:#"play.png" ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path]; //iOS8 image = nil, iOS7 image != nil
But when the image resources are stored in main bundle, UIImage Initialization can be succeed through the following method:
NSString *path = [[NSBundle mainBundle] pathForResource:#"play.png" ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path]; //iOS8 and iOS7 image != nil
We have found this problem under iOS8 beta3 and it still exists under iOS8 beta 5.
The Demo app directory structure was below:
XXX.app
|----test~ipad.bundle
|----play.png
|----test~iphone.bundle
|----play.png
|----play.png
|----play~iphone.png
|----play~ipad.png
......
Does anyone have the same problem? I think this is a bug on iOS8, Therefore, many apps have used bundle to organize resources, such as images. But now, this bug will make thousands of apps become ugly and hard to use. We really hope that Apple can fix this bug in the next version, otherwise, all of my app will have to developing an new version for this bug!
It's not actually broken, you're just sending it the wrong path.
I'm guessing that you are using the simulator, and have saved a file somewhere in your app and then made note of the full path to that file. However, when you rebuild your app the UUID for the installed app changes, and your saved path is no longer valid (even though the files are still there). You can see your current active path by checking [[NSFileManager defaultManager] currentDirectoryPath] Note that the value changes each time you rebuild and re-deploy your app.
To get to your actual file, you'll need to do something like this:
NSString *image = #"myImage.png";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cache = [paths objectAtIndex:0];
NSString *fullPath = [NSString stringWithFormat:#"%#/%#", cache, image];
UIImage *image = [UIImage imageWithContentsOfFile:fullPath];
pathForResource:ofType: only looks in the Resources folder (and localised folders). If you are placing files into dedicated folders in the bundle then you need to use pathForResource:ofType:inDirectory: and supply the directory name.
Apple has fixed this bug on iOS8.1,Cheers
just use
NSString *fullPath = [bundle pathForResource:name ofType:#"png"];
NSData *data = [NSData dataWithContentsOfFile:fullPath];
UIImage *image = [UIImage imageWithData:data];

xcode cannot get sound file to play with AVAudioPlayer

I am new to iOS and trying to add sound to my app. I have followed several tutorials about adding a simple sound file to my project, but ALL with the same result. I get no errors, but no sound plays. Any ideas on what I am doing wrong? Any help would be appreciated!
- (IBAction)playSound:(id)sender {
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSLog(soundFilePath);
NSError *error;
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:&error];
player.numberOfLoops = 1;
[player play];
}
**UPDATE***
So I never had any luck with the above code which is very discouraging, but I did end up getting it to work using the AudioToolbox Framework instead, using this:
SystemSoundID soundID;
NSString *soundFile = [[NSBundle mainBundle]
pathForResource:#"test" ofType:#"mp3"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)
[NSURL fileURLWithPath:soundFile], & soundID);
AudioServicesPlaySystemSound(soundID);
Can anyone explain to me the difference, why this one works and the other does not?? I am curious more than anything. Would love some clarification.
Finally figured it out. I needed to create a property for AVAudioPlayer in my header file in order to get it to work. Here is what I did:
// .h file
#interface ThirdViewController : UIViewController {
AVAudioPlayer *audioPlayer;
}
#property (nonatomic, retain) AVAudioPlayer *audioPlayer;
- (void) playSound //method in .m file
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"test"
ofType:#"mp3"]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
[audioPlayer play];
}
Simplify your code by doing the follow. Also drop your test.mp3 to the root of your project directory in xcode. Your mp3 placement and location is very important. Also ensure volume is up, and vibrate is off of your device, and it's unmuted.
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"test"
ofType:#"mp3"]];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:nil];
[audioPlayer play];
Make sure you install quartz.h, or a specific framework that supports audio players. Just double check.
Had the same problem a while back and the framework must be installed properly.

Reading files from Documents directory using xcode

with reference to my own question,
UITable is not getting populated with NSMutableArray here i got all the file names into a table. And these files where stored in 'Resources' folder. Now i need to fill the same table from the files stored in a subfolder say 'sample' which is in documents folder of ios simulator.
Earlier i did this with the following code
files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:
[[NSBundle mainBundle] bundlePath] error:nil];
search_results_array = [files filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"self BEGINSWITH[cd] 'h'"]];
here both files and search_results_array are arrays. Now the reading is not from resources. How can i edity it? anybody please help.
Use the following:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //Library directory is not accessible via iTunes, Document directory is
NSString *docDirectory = [paths objectAtIndex:0]; //2
path = [docDirectory stringByAppendingPathComponent:yourString]; //3
files = [[NSDictionary alloc] initWithContentsOfFile: path];
Assuming files is an NSDictionary.

A method for 'Save As...' in Core Data app

CD's been an enormous learning curve for me and there's still a bit for me to go, but any help on the following could enable me to lift the current weight on my shoulders!
I'm trying to write a method that implements a "Save As.." for the user in my CD app.
So far I've got:
[saveAsPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
NSPersistentStoreCoordinator *psc = [self persistentStoreCoordinator];
NSURL *oldURL = [self URLOfInternalStore]; //returns the current store's URL
NSURL *newURL = [saveAsPanel URL];
NSError *error = nil;
NSPersistentStore *oldStore = [psc persistentStoreForURL:oldURL];
NSPersistentStore *sqLiteStore = [psc migratePersistentStore:oldStore
toURL:newURL
options:nil
withType:NSXMLStoreType
error:&error];
}
}];
Unfortunately, I just get the error:
Object's persistent store is not reachable from this NSManagedObjectContext's coordinator.
Should I 'remove' and then 'addPersistentStore...' to update it to the new URL? The doc's seem to suggest that all will be handled with in the 'migrate' method.
Thanks in advance!
Edit:
Ok, well, I've come up with my own 'dirty' method. I can imagine that this isn't an approved way of doing things, but there's no error thrown up and the app works as expected at all times (not often I can say that, either!):
-(IBAction)saveAsAction:(id)sender
{
NSSavePanel *saveAsPanel = [NSSavePanel savePanel];
[saveAsPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
[self saveAction:#"saveAsCalling"];
NSURL *newURL = [saveAsPanel URL];
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:internalStore] toURL:newURL error:&error];
//internalStore is a hard-wired NSString that holds the path to the bundle's database
}
}];
}
-(IBAction)loadAction:(id)sender
{
NSOpenPanel *loadPanel = [NSOpenPanel openPanel];
[loadPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
[self saveAction:#"loadCalling"];
NSURL *newURL = [loadPanel URL];
NSURL *oldURL = [NSURL fileURLWithPath:internalStore];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [SELF_MOC persistentStoreCoordinator];
[psc removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:oldURL] error:&error];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:newURL options:nil error:&error];
[[NSFileManager defaultManager] removeItemAtURL:oldURL error:&error];
[[NSFileManager defaultManager] copyItemAtURL:newURL toURL:oldURL error:&error];
[psc removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:newURL] error:&error];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldURL options:nil error:&error];
}
}];
}
The basic reasoning is this: to do a 'SaveAs...' I simply copy out the SQLLite store file in the mainBundle to wherever the user selects and rename it to what they want - as per TechZen's suggestion.
To do a 'Load' then I first removePersistentStore from the bundle's file, add the one that the user's just chosen. Delete the bundle store (which in theory isn't now being used) and then copy the user's choice back into the bundle. Finally, the two operations of remove and addPersistentStore are performed to point the app back to it's bundle's file which is now the user's choice.
Hope that makes sense. If anyone has any thoughts on just how unprofessional a methodology this is then please - be kind as I'm fairly new - let me know. I can't find anything that is more elegant.
I know Apple don't like you using removePersistentStore and addPersistentStore but, as I say no errors are reported (in my actual code I scattered NSLog lines throughout to report what error is holding).
You only use a SaveAs... in a document based app. If you use Core Data as your model, you need to use NSPersistentDocument to save your data. It provide the SaveAs... functionality you seek.
Straight Core Data is used for more database-like apps in which the entire app operates from one data set (more or less.)

Overwriting a file programmatically in Cocoa

This code copies the referenced file and places it in the Docs Directory. I'm trying to build a simple backup solution. The problem is this operation does not overwrite the existing file if the operation is repeated.
Two questions:
What's the best way to overwrite in code?
How difficult would it be to append the current date to each copied file? In this case there would be no overwrite operation. This would be much more useful for keeping incremental backups. If I decide to do it this way I understand I would need to create a new path in order to keep things organized.
Thanks.
Paul
NSString * name = #"testFile";
NSArray * files = [NSArray arrayWithObject: name];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws performFileOperation: NSWorkspaceCopyOperation
source: #"~/Library/Application Support/testApp"
destination: #"~/Documents/"
files: files
tag: 0];
You could try using NSFileManager, example below (untested):
// Better way to get the Application Support Directory, similar method for Documents Directory
- (NSString *)applicationSupportDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
return [basePath stringByAppendingPathComponent:#"testApp"];
}
- (void) removeFile {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *applicationSupportDirectory = [self applicationSupportDirectory];
NSError *error = nil;
NSString* filePath = [applicationSupportDirectory stringByAppendingPathComponent: #"testFile"];
if ([fileManager fileExistsAtPath:filePath isDirectory:NULL]) {
[fileManager removeItemAtPath:filePath error:&error];
}
}
Edit:
Take a look at the NSFileManager Class Reference for other functions that might be useful (for your second question).

Resources