Write/Read from property list on iPhone - iphone

I know there are thousands of tutorials on the topic however I don't get it to work.
What I am trying to do is to read an value from a plist file. Then I check whether or not I have to update the value. This part is ok. However I don't managed to get the value saved in plist file. I have a Settings.Bundle and it is where I am trying to read/store.
It is just a simple string.
Thanks in advance

The following will give you a NSDictionary with your settings bundle information.
[[NSBundle mainBundle] infoDictionary]
If there are any other generic plist you would like to read you can easily load them into a NSDictionary using
[NSDictionary dictionaryWithContentsOfFile:filePath];
//or
[[NSDictionary alloc] initWithContentsOfFile:filePath]
Also I would like to add for reading and saving values I would recommend doing that in the NSUserDefaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
id anObj = [userDefaults objectForKey:#"myKey"];
//Modify anObj
...
[userDefaults setObject:anObj forKey:#"myKey"];
[userDefaults synchronize];//Save immediately if you choose

You cannot modify files/folders present in your app bundle (such as Settings.bundle.) They are read-only.

Related

iOS - plist file, nsuserdefaults or text data file

I am working on a mobile app which needs a lot of data. Simply put, the data will be for multiple languages and consist of all the words possible for that language. The app would start with only the English language and a lot of it's words. Then the user can choose to download more languages and their data.
I am trying to figure out the best way to read/save/update this data. Should I create a plist file with English data to start with and just keep adding more data as user downloads new languages? Or should I save all the data in nsuserdefaults? Or, should I just include a text file with all the data and parse it on the fly?
Suggestions?
ps: i understand that as this is a mobile app, file space and parsing time have to be considered
Please read my answer here: https://stackoverflow.com/a/7215501/832065
As said in that answer, all NSUserDefaults are stored together in one plist.
A plist and NSUserDefaults are basically the same, however NSUserDefaults should ONLY be used for saving preferences and not a big amount of data. So don't use NSUserDefaults!
I would consider saving this in a plist (NSDictionary). Like this you can have all data sorted in that file. Simply set the "words" (NSString I assume) as object, and the language as key.
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
[dict setObject:words forKey:language];
[dict writeToFile:filePath atomically:YES];
same for reading:
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSString *words = [dict objectForKey:language];
EDIT:
If each word is assigned to a number (you can just use NSArray) it doesn't differ much:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
NSArray *words = [NSArray arrayWithObjects:allTheWords,nil];
[dict setObject:words forKey:language];
[dict writeToFile:filePath atomically:YES];
same for reading:
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSArray *words = [dict objectForKey:language];
NSString *word = [words objectAtIndex:numberAssignedToWord];
I hope this helps! :)
I would recommend using either an SQL database directly or Core Data. If you have a lot of data, you don't want to load that data completely into memory all the time. It is also easier to handle updates, changes or additions of data.
But your question is so general, and doesn't tell anything on what you actually want to do with the data, how large the data is in KByte or MByte, it is hard to give any good answer.
Writing a lot of data into a text file, user defaults, plist or something similar, doesn't seem the right choice. Using Core Data would be the default way to do such things on iOS.
I thing the easiest way would be to handle the data in a NSMutableArray and store it within the application using NSUserDefaults.
You can handle your data within a NSMutableArray, like so:
NSMutableArray *yourDataStuff = [[NSMutableArray alloc] init];
[yourDataStuff addObject:#"Your data 1"];
[yourDataStuff addObject:#"Your data 2"];
[yourDataStuff addObject:#"Your data 3, etc.."];
Then you can store it using NSUserDefaults, like so:
[[NSUserDefaults standardUserDefaults] setObject:yourDataStuff forKey:#"myData"];
And read your data, like so:
yourDataStuff = [[NSUserDefaults standardUserDefaults] objectForKey:#"myData"];
I think you cannot save large data in the NSUserDefault , and you maybe can not do IO with plist , i had done this , when you read or save , you must read in the whole .plist , and you may reveive memory warning , or crash. you can use C (fread,fwrite) write the data in a .txt , and you may read and write with data stream.

Storing values from NSUserDefaults in Settings bundle

I am trying to find out how to save/store my values from NSDefaults so that when I exit the application they are stored in the Settings.bundle. This is what I am doing...
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:#"M1", #"IDMissiles",
#"G2", #"IDGuns",
#"B3", #"IDBombs",
#"KM", #"IDDistance", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:settings];
If I do the following, the values print out correctly from NSUserDefaults ...
NSLog(#"IDMissiles: %#", [userDefaults stringForKey:#"IDMissiles"]);
NSLog(#"IDGuns : %#", [userDefaults stringForKey:#"IDGuns"]);
NSLog(#"IDBombs : %#", [userDefaults stringForKey:#"IDBombs"]);
NSLog(#"IDDistance: %#", [userDefaults stringForKey:#"IDDistance"]);
However ... Each time I run the application the values in NSUserDefaults start off as (null), I was thinking that doing [[NSUserDefaults standardUserDefaults] synchronize]; would store the values for the next time I run the application, but no such luck.
Instead of using
[[NSUserDefaults standardUserDefaults] registerDefaults:settings];
try this:
[[NSUserDefaults standardUserDefaults] setObject:settings forKey:#"settings"];
Then, get from defaults like this:
NSLog(#"IDMissiles:%#[[[NSUserDefaultsstandardUserDefaults]objectForKey:#"settings"]objectForKey:#"IDMissiles"]);
One thing I discovered when working with the settings.bundle is that none of the values get initialized until you actually open the settings pane. You can have default values saved there, but they will return nil until you open the settings.
I'm not sure if this happens when you try and save values there but never open the settings pane.
If you are not using a settings pane, then you wouldn't want to use the registerDefaults option.
Try this instead.
[[NSUserDefaults standardDefaults] setObject:#"M1" forKey:#"IDMissiles"];
// set remaining values
[[NSUserDefaults standardDefaults] synchronize]; // this really only needs to be called if you plan on accessing values right away, otherwise they are saved automatically after the next run loop
From the documentation:
The contents of the registration domain are not written to disk; you need to call this method each time your application starts. You can place a plist file in the application's Resources directory and call registerDefaults: with the contents that you read in from that file.
In other words, you aren't storing anything by registering defaults like this. To have default values both in your app and in the settings bundle, you have to maintain the settings bundle separately as discussed here.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[NSUserDefaults removeObjectForKey:#"userDefaults"];
[userDefaults setObject:[settings objectForKey:#"mainData"] forKey:#"userDefaultsValue"];
[userDefaults synchronize];

Is it possible to read the structure of plist file?

I know that NSUserDefaults can read the key and value from plist file.
Is it possible to read the structure of a key?
For example:
Key 'Count' is an integer and has option
1,2,3,4,5
the codes below can get the value 'Count'
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"Count"]!=nil)
{
NSString *s=[[NSString alloc] initWithString: [defaults objectForKey:#"Count"]];
NSInteger v=[s intValue];
[s release];
}
But I prefer to get all options and store to a NSArray or a better storage structure.
Is it Possible?
I read bundle settings the following way:
// Get path to Root.plist file in settings bundle and retrieving its contents
NSString* tPath = [[NSBundle mainBundle] pathForResource:#"Settings" ofType:#"bundle"];
settingsBundle = [[NSBundle bundleWithPath:tPath] retain];
NSDictionary* tSetDict = [NSDictionary dictionaryWithContentsOfFile:[tPath stringByAppendingPathComponent:#"Root.plist"]];
// Get array of preference dictionaries
NSArray* prefs = [tSetDict objectForKey:#"PreferenceSpecifiers"];
// Iterate through dictionaries to find required value
for (NSDictionary* setDict in prefs){
NSString* type = [setDict objectForKey:#"Type"];
if (![type isEqualToString:#"PSMultiValueSpecifier"]){
// Get possible preference values for PSMultiValueSpecifier case
// You may need to know value type in advance - not sure about that
NSArray* values = [setDict objectForKey:#"Values"];
}
}
I'm not exactly sure I understand your question but you can't directly access the user defaults system save through its defined methods. You can't read them out in one big chunk.
The defaults system isn't actually a means of reading plist files, its an more of an API for accessing a database maintained by the OS itself. Although, you don't see it much on the iPhone, its actually a very large and complex system for managing preferences not only for individual apps but also for users, groups of users, computers and networks. It seems trivial on iOS because you don't have the flexibility of configuration and use that you have on MacOS proper.
It would be impossible to read out the entire defaults because they are huge and much larger than you would expect even on iOS.
Instead of jumping through the hoops in the code you showed in the answer, you should access the data in the key using one of the dedicated methods for the type of data stored. In this case:
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
NSInteger v=[defaults integerForKey:#"Count"];
Even if you did read the defaults out in one chunk, you would just find yourself using the same type of calls and code to access the data in the alternate data structure as you would in using the defaults in the first place. You might as well use the defaults system.

Store preferences in a file and import values to initialize variables

I want to store the content of some variables in a file. The user nor the application should change the value. It would be great if the user cannot read the content of this file. On startup the application reads the file and initialize some (global?) variables with the content.
The file should contain some values like server URL and so on. This could change, but I only want to manage a preference file rather than updating the source code. On an update for example only the preference file will get exchanged.
How can I manage that?
NSUserDefaults is not intended for such issues I think. Should I use a a plist or a normal txt file?
How would the access to the content of the file look like?
Cheers
Sounds to me like NSUserDefaults is exactly what you need. It will let you store URLs (as strings) and other basic types of variables.
Why do you think NSUserDefaults is not the right solution here?
Give it a try! It's easy to use and reliable.
Use a plist. You can load it with -[NSDictionary initWithContentsOfFile:] (you can save a dictionary to a plist just as easily with -[NSDictionary writeToFile:atomically:], though it doesn't sound like you need to do that).
So the solution I'll use is that I load a plist file as default value for my NSUserDefaults:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Settings" ofType:#"plist"];
NSDictionary *settingsDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
[[NSUserDefaults standardUserDefaults] registerDefaults:settingsDict];
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
NSString *serverURL = [settings stringForKey:#"ServerURL"];
NSLog(#"%#", serverURL);
Taken from iPhone App : Where do I put a config file?
So I'll use both plists and NSUserDefaults. settings I'll define as global variable in the main.m. One problem remains:
How to differentiate between user defaults and system defaults?

Saving application data state on exit

I have an NSMutableArray with 24 strings.
I need to save this data if a user gets a call or quits the application.
I have been looking into many examples but for some reason can’t seem to determine the best way to save the data.
The 24 strings correspond to 24 buttons and their state. When a button is clicked, it displays the corresponding Array info for that buttons tag (0 – 23). What I would need to retain is if 10 buttons where clicked and their data shown, how/what would be the best way of retaining this data so it can be reloaded when the app starts?
I am thinking I would need to store:
Button Tag,
Buttons corresponding Array value,
Button state (whether it has clicked and value is show or not)
I would store this data on exit of the application and then when app is started again, I would determine if this stored data exists, if so populate the array and examine the button states to determine if it had already been shown and if so, set it accordingly. Then when this file was loaded, I would delete the stored data (.DAT file if stored this way). This way if a user quits gracefully, on next start up, it would start a new game.
I have looked at several examples where they store data into a .DAT file but am having problem implementing this….and wondering if this is even the best way.
Any help or thoughts on this is greatly appreciated.
Geo…
you should be able to store it in NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:buttonState forKey:#"buttonState"];
[defaults synchronize];
// Then to get the state back
NSMutableArray* buttonState = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"buttonState"] mutableCopy];
You could save the data to a plist in the Documents directory. If the data is there, load it up, and if not, it would suggest a clean run.
To load the data:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [paths objectAtIndex:0];
NSString *filePath = [documents stringByAppendingPathComponent:#"buttonState.plist"];
NSMutableDictionary *buttonState = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
To save the data:
[buttonState writeToFile:filePath atomically: YES];
I save my data by using NSKeyedArchiver/NSKeyedUnarchiver, since your NSMutableArray already supports the NSCoding protocol, you can just use
yourArray = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
[NSKeyedArchiver archiveRootObject:yourArray toFile:file];
Edit:
I suppose NSArray's own methods work also
[NSArray writeToFile:]
[NSArray initWithContentsOfFile:]