Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Hello,

I am saving my application prefernces into a plist file. I want to be able to edit the preferences through my program.

I have tried using NSMutableDictionary and NSMutableArray as in:
Code:
//Read the preferences from the PLIST file to the local cache
	NSMutableDictionary *appPreferences = [NSMutableDictionary dictionaryWithContentsOfFile:[PREFERENCES_FILE stringByExpandingTildeInPath]];
	NSMutableArray *appPrefKeys = [appPreferences allKeys];

However, I am getting warning saying "initialization from distinct Objective C type". How can I solve this warning? Is there some other way of editing the plist file thru program?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
1) Why are you not using NSUserDefaults which is provided for this purpose (saving user preferences)

2) The allKeys method returns a NSArray, not a NSMutableArray
 

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Thanks for the helpful response.

I am very sure if I can use NSUserDefaults in my case. Because I want to store some application execution related data into the Plist file and want to read this data from the plist file during execution of the program. Some paths in the program will change the data in this plist file, so I want to be able to edit/insert data to plist. I will not be having any GUI components for interacting with the plist file. pls suggest if NSUserDefaults can still be used?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
I fail to see why any of that matters: NSUserDefaults is designed to support the storing, retrieving and use (store, retrieve, alter, delete) at run-time of per-user application data. Unless you are storing huge amounts of data (in which case you shouldn't be using a plist of any sort) it is the correct answer for this.
 

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Oh I See... it looks like that I should be using some other file format for saving this data. The data will vary from user to user and can be lots in some cases. Very much like how the browsers maintain user cache info etc.

Any suggestions on better file formats for this purpose?
 

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Thanks a bunch. Will check if I can use some other file format for saving my data.

Am just getting a bit curious ... any idea on the max size (ideal) limit for PLIST file?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Thanks a bunch. Will check if I can use some other file format for saving my data.

Am just getting a bit curious ... any idea on the max size (ideal) limit for PLIST file?

As far as I am aware there is no hard limit on the size of a plist (beyond what the filesystem imposes), but I feel that if it's getting to be above a few hundred KB then it's probably too big: either too many keys or binary data that should be stored in it's own file. For simply stored basic key/value pairs you should be able to store thousands before it gets anything like that big...
 

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Thanks for the helpful reply.

It looks like that I can still make use of a plist file for storing the data if it can go upto a few hundred KBs. However, is it possible to edit the contents without using NSUserDefaults or to specify an input file name for NSUserDefaults?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Thanks for the helpful reply.

It looks like that I can still make use of a plist file for storing the data if it can go upto a few hundred KBs. However, is it possible to edit the contents without using NSUserDefaults or to specify an input file name for NSUserDefaults?

You can use the methods of NSArray or NSDictionary (or their Mutable counterparts) to read/write from any file you want. Which one you choose depends on what you want at the root of your plist. But if this data in any way represents user preferences or similar you should be using NSUserDefaults: it guarantees the file is saved in the correct location on the file system, has the correct name, works correctly with network home directories etc.
 

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Finally I have been able to use NSMutableDictionary and NSMutableArray to edit an existing plist file.

The original plist file contents are:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Days</key>
	<array>
		<string>Monday</string>
		<string>Tuesday</string>
	</array>
	<key>Months</key>
	<array>
		<string>Jan</string>
		<string>Feb</string>
	</array>
</dict>
</plist>


The code that I have used to update and existing array and insert a new array into this plist file is:
Code:
	NSMutableDictionary *testDict = [[[NSMutableDictionary alloc] initWithContentsOfFile:[@"~/Library/Test.plist" stringByExpandingTildeInPath]] autorelease];
	NSEnumerator *testEnumerator = [[NSEnumerator alloc] autorelease];
	testEnumerator = [testDict objectEnumerator];
	
	NSMutableArray *testArray ;
	while(testArray = [testEnumerator nextObject])
	{
		for(int i = 0; i < [testArray count]; i++)
		{
			NSLog([testArray objectAtIndex:i]);
		}
		
		if(testArray == [testDict objectForKey:@"Months"])
		{
			[testArray addObject:@"Mar"];
		}
	}
	
	NSMutableArray *newArray = [[[NSMutableArray alloc] init] autorelease];
	[newArray addObject:@"2000"];
	[newArray addObject:@"2004"];
	
	[testDict setObject:newArray forKey:@"Years"];
	
	[testDict writeToFile:[@"~/Library/Test.plist" stringByExpandingTildeInPath] atomically:TRUE];

The code is working fine. I have posted it here for review and suggestions on improving it (in case it can be improved). Pls suggest.
 

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
Code:
NSEnumerator *testEnumerator = [[NSEnumerator alloc] autorelease];
testEnumerator = [testDict objectEnumerator];

Since you sent the new object autorelease, this won't leak, but it's not necessary. You assign the pointer to a new enumerator to testEnumerator, and on the very next line you assign a totally different enumerator. Just get rid of the first line, and declare testEnumerator on the 2nd instead.

Code:
if(testArray == [testDict objectForKey:@"Months"])
Sadly, this works, but i think it is bad form. Others may have a different opinion, but i think you should use isEqualToArray: to compare arrays, not compare pointers. Again, in this very specific case the pointers will be the same, but this is a dangerous habit to develop.

Code:
for(int i = 0; i < [testArray count]; i++)

If you know, absolutely, that every object is an array (or they all at least respond to count and objectAtIndex: ), this isn't terrible, but you're really making this "niche" code by assuming that every object in the dictionary is an array. You may want to at least ask if the object respondsToSelector:, or better, ask if it isKindOfClass:[NSArray class]
http://developer.apple.com/DOCUMENT...apple_ref/occ/intfm/NSObject/isMemberOfClass:

Once you're sure, you can send these messages.

Otherwise, it looks good. If you are using 10.5 or above, i would recommend the for...in syntax instead of bothering with a special enumerator, but that's not necessary.

-Lee

EDIT: Thought this through more, and since you want to do something special for one particular key, it seems better to use for...in or an enumerator over they keys instead. It's inconsequential to get a value when you have the key, and you can just check isEqualToString between the desired key and the current key, instead of having to compare the arrays.
 

McBgnr

macrumors regular
Original poster
Apr 13, 2009
144
0
Thanks a lot lee1210. These are wonderful suggestions. The links are also very helpful.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.