Editable table view bound to NSArrayController bound to NSUserDefaultsController
Asked Answered
F

1

10

I have a simple model Foo that represents a user preference and encapsulates an NSString and an NSNumber. I want to store an array of Foo’s in user defaults so that they persist between launches of the application; and I’d like to display them in an table view such that the user can add, remove, and edit them. The solution to this seemed pretty straightforward with bindings, but it’s proving impossible for me to actually get working.

When my application launches I register with NSUserDefaults a keyed archive of an array of initial Foo’s. In my XIB I have an array controller with its content array bound to the shared user defaults controller’s values controller key; with a key path of foos; ‘Handles Content As Compound Value’ checked; and NSKeyedUnarchiveFromData as the value transformer. The table view is then in turn bound to the array controller and the columns of the table to the properties of Foo.

This works perfectly when Foo’s are added and removed from the array—the foos key in user defaults is updated to reflect the new contents of the array. The problem is that changes to the properties of an individual Foo don’t similarly trigger the array to be written back to user defaults. I believe the reason for this is discussed in the ‘To-many Relationships’ section of ‘Registering Dependent Keys’ in the Key-Value Observing Programming Guide: changes to the array controller’s array itself are observed, but changes to properties of the contained elements aren’t.

Despite recognizing this as the problem I can’t for the life of me figure out what to actually do in order to make this work. When I observe a change on a Foo what object do I need to inform and with what message such that the entire array is written back to user defaults—NSUserDefaults, NSUserDefaultsController, NSArrayController? I wish the Apple documentation had more than a cursory mention of this problem and provided some actual example code to solve it—I’ve tried everything and I can’t find the magic incantation.

This is similar to How to get notified of changes to models via an NSArrayController but I can’t make the conceptual leap between what he’s doing with a custom view and the use of bind:toObject:withKeyPath:options:.

Thanks! :)

Farica answered 10/5, 2013 at 16:8 Comment(4)
This really sounds like a job for CoreData not NSUserDefaults.Vindictive
Can you elaborate? Just thinking about the amount of documentation I’d need to digest to even begin implementing this with CoreData gives me a headache. From a data model perspective, what I’m trying to do seems extremely straightforward—given the complexity of CoreData it seems like overkill here.Farica
well user defaults are for setting and you are trying to use it to store your data model form the sound of it. CoreData really isn't very difficult if you really do have a simple data model. xcode has project templates for it that basically does all the setup for you. it should solve the problem you describe too.Vindictive
I have to agree with Brad. CoreData is really the way to go and it really isn’t as hard as it might appear (were all database systems this easy).Clink
J
0

I'm an iOS developer so forgive me if there's some aspect of this that I'm missing due to iOS not supporting Cocoa bindings.

I could not tell for certain but it sounds as though you want to get a notification when any property of any Foo is modified so that you can signal a change to your foos array and thus have NSUserDefaults refresh the saved array. Is that correct?

Assuming I understand the scenario, and given that, as you said, modifying the array itself (i.e. adding/removing Foo's inside 'foos' is successfully signaling the change, it seems obvious that what you need to do is to signal that foos has been updated any time an individual property is updated.

To do that, you simply need to fire off a KVO notification when any property changes. Basically you would override your setters inside Foo - for any properties you care about observing. And send a notification there.

But how do yo you do this? The usual pattern to send a KVO notification goes like (for a property called openingBalance:

[self willChangeValueForKey:@"openingBalance"];
_openingBalance = theBalance;
[self didChangeValueForKey:@"openingBalance"];

But in your case you want to send a notification that the property 'foos' has changed. And its not a property on Foo. There's nothing that says that the notification has to only be for the specific property (or at least nothing that I'm aware of). But, you probably don't want a Foo to know it's composed inside a 'foos' property somewhere else.

So, how about just using a straight NSNotification? Create a generic method to fire a 'Foo' updated Notification inside Foo -- subclass your setters, and then fire this notification whenever you modify a Foo property.

In turn, inside your object that owns 'foos' subscribe to this notification and either directly update NSUserDefaults or, from there, fire off your KVO notifications for 'foos' there.

Judicature answered 26/10, 2013 at 19:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.