A better solution? - Accessing an array of strings saved to user defaults via bindings (solved in the end...)
A better solution? - Accessing an array of strings saved to user defaults via bindings (solved in the end...)
- Subject: A better solution? - Accessing an array of strings saved to user defaults via bindings (solved in the end...)
- From: Peter <email@hidden>
- Date: Fri, 15 Jul 2011 01:57:28 +0200
I have found various references on this list and on the web about the impossibility to hook up an *editable* single-column NSTableView to an NSMutableArray of strings via bindings - and about some clever ways to work around this limitation. These workarounds may work in principle, but fail in a NSUserDefaultsController-NSArrayController-NSTableView setup.
I tried to be exceedingly clever using the following category (as proposed by Joanna Carter http://carterconsulting.org.uk/forums/viewtopic.php?f=3&t=14):
@interface NSMutableString (KVCHelper)
@property (copy) NSString *string;
@end
@implementation NSMutableString (KVCHelper)
@dynamic string;
- (NSString *) string{
return self;
}
@end
alongside with various NSValueTransformers converting strings or arrays to their mutable variants, as well as converting the array to a mutable dictionary and back using this one
#import "ArrayOfStringsToArrayOfMutableDictionariesVT.h"
#define kStringKey @"stringKey"
@implementation ArrayOfStringsToArrayOfMutableDictionariesVT
+ (Class) transformedValueClass
{
return [NSMutableArray class];
}
+ (BOOL) allowsReverseTransformation
{
return YES;
}
- (id) transformedValue:(id)array
{
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (NSString *myString in array) {
[myMutableArray addObject:[NSMutableDictionary dictionaryWithObject:myString forKey:kStringKey]];
}
return [myMutableArray autorelease];
}
- (id) reverseTransformedValue:(id)array
{
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (NSDictionary *myDictionary in array) {
[myMutableArray addObject:[myDictionary objectForKey:kStringKey]];
}
return [myMutableArray autorelease];
}
@end
All to no avail, they work but don't help. The dead end is always NSUserDefaults, or more precisely its controller. It's such a pain that you can save an NSArray in user defaults but then nevertheless be unable to access it using bindings.
So my question is:
How could I possibly wrap an NSArray in the user defaults in a way that I can conveniently access it via bindings to populate an editable NSTableView? Is there a way to wrap an NSArray into some kind of opaque data object, so that the array controller is prevented from accessing single items in the array, but instead receives it from and hands it over to the user defaults controller in a single item lump?
My point is: I'd like to abandon saving an NSArray in the user defaults if I can use something similarly ordered and access it via bindings instead. I know I could create a wrapper class for each string element in the array, but I wonder if there isn't an easier solution choosing a different container object.
There is still a rumor about the NSUserDefaultsController yielding immutable objects and this being one source for this problem. That's untrue. It seems to have changed with OS 10.4. I can write data from a table view to the applications preferences plist, but the data type is wrong - the string "test" (set via the table view in the GUI) is wrapped into a dictionary on write, whereas the rest (set programmatically on initialization) are simple strings:
{
fruitsList = (
{
abc = test;
},
Orange,
Strawberry,
Raspberry,
Whatnot
);
}
OK - this was the pointer to myself - silly...
I converted my original array of strings (= myArray) to a mutable array of mutable dictionaries, each containing a single string under some arbitrary key (which has to be kept constant for all these dictionaries, of course). So my +initialize method looks as follows:
NSArray *myArray = [NSMutableArray arrayWithObjects:@"Apple", @"Orange", @"Strawberry", @"Raspberry", @"Whatnot", nil];
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:[myArray count]];
for (NSString *myString in myArray) {
[myMutableArray addObject:[NSMutableDictionary dictionaryWithObject:myString forKey:@"abc"]];
}
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
myMutableArray,
@"fruitsList",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
And my application prefs plist looks like this:
{
fruitsList = (
{
abc = Apple;
},
{
abc = Orange;
},
{
abc = Strawberry;
},
{
abc = Raspberry;
},
{
abc = Whatnot;
}
);
}
Setting the model key path for the table column to abc (while checking Handles Content as Compound Value) makes it possible to access and edit all these strings via bindings.
Is there a more elegant/efficient solution than looping over the array and then once again into the opposite direction for real use outside the prefs (I could use a block in 10.6, I know)?
At least it works and it's a lot better than writing all the glue code. There is also no need to write and use a wrapper class.
Sorry for having answered my own question in such a long winded manner - and again thanks for any pointers to alternative approaches!_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden