On Jan 26, 2009, at 9:09 AM, Robert Monaghan wrote:

I am trying to wrap my head around using a plist.

When you refer to a "plist", are you referring to a property list data structure as it might exist in memory, or a property list file? Or perhaps you're referring to user defaults?

In memory, a property list is just an abstraction. It's an NSString, NSNumber, NSData, NSDate, or a collection (NSArray or NSDictionary) containing only theses types. (For simplicity, I'm eliding mutable versions of these types and their Core Foundation equivalents.)


Everything that I have seen on the mailing list so far, involves using an NSTableView to work with NSDictionaries.

I'm sure that more reading will turn up lots of other examples that don't use NSDictionaries.

Key-Value Coding, Key-Value Observing, and Bindings all deal with objects which have properties, which may be referred to by name, a.k.a. key.

NSMutableDictionary is useful as a generic object which may have any set of properties. NSMutableDictionary can be nice for some rapid prototyping. However, you'll probably find that it will be insufficient for a real application.

The problem with using NSMutableDictionary for your model objects is that it's pure data with precious little behavior. Object-oriented programming is about combining data with the operations (behavior) that act on it. Unless your model has no smarts, no "business/domain logic", then a dictionary is not going to be sufficient.

So, you should definitely consider replacing your uses of NSMutableDictionary with real custom classes that represent your model objects. Sometimes doing so forces thinking about your design that exposes problems with it.


Here is what I have set up.
I have several NSButtons and pre-populated NSPopup Buttons, where the index values or states are stored as NSNumbers inside a plist file. Fairly easy to figure out. You just specify a key, and you get an NSNumber Object.

Here you say "plist file", but you can't create bindings to a file. So, what's really going on?


I am now trying use several of these plists. I have a list of projects in an NSTextColum, each with its own plist. I want to select one of the projects in the tableview, and have the controls changed to reflect the states from the appropriate plist. Again, in theory, plists should work well, here.

NSTextColum? I'm going to assume you mean a table column with text field cell. From what you're describing -- selecting a row in a table updates controls to reflect the selection -- this sounds like a master- detail interface.

Yes, "plists" should work well here, in the sense that you can model the set of projects using a to-many relationship represented with an array, and each element being a dictionary describing a project.

By the way, you haven't described the object having this to-many relationship.


So far, I have an NSArrayController working well, with a list of projects/plists. However, I can't seem to get the bindings to work for the various controls. I am attempting to bind the buttons and popup menus to *something" that that can drive them. I not only want the controls to be updated with each project that is selected, but have the NSDictionary that is holding the plist, to be updated when the user changes the controls.

You use the verb "drive" here (and below), but I'm not sure I follow what you mean. You want something to "drive" your buttons and pop-up menus? In what sense? Do what to them?

In the Model-View-Controller (MVC) design pattern, if anything is going to act on the views, it would be the controller. Often though, the views "drive" the controller, in the sense of sending action messages to it or setting its properties through bindings. The views also get their values from the controller through bindings, built on KVO.


Here is what I have tried:
NSDictionaryController.
I have an observeValueForKeyPath set up, to watch my NSArrayController, with a list of projects. Once I have a project selected, it then binds an NSMutableDictionary to the NSDictionaryController Something like this: [seqPrefsCtlr bind:NSContentDictionaryBinding toObject:self withKeyPath:@"prefsDict" options:nil];

Honestly, that is clear as mud to me.

Why are you using an NSDictionaryController? If you're using an NSMutableDictionary as a model object, that doesn't necessarily mean that an NSDictionaryController is appropriate. It's only appropriate if you need to treat the set of key-value pairs as an array, where the keys are not properties but part of the data. If you're using a dictionary as an entity, where its keys are properties, then I don't see where NSDictionaryController enters into it.

An NSArrayController is a mediating controller. There should be a coordinating controller somewhere, which you haven't described. The NSArrayController doesn't "own" the list of projects, it just presents a bindings-compatible interface to the list and handles some common tasks (like maintaining a selection).

For what purpose are you directly observing the array controller? What object is doing that observing and what is it doing in response to the change notifications it receives?

Why are you doing the binding in code? It's not wrong, but it seems like you're doing things the hard way, possibly because you don't understand the easy way.


No problem there.. But then I can't seem to figure out what to bind the assorted NSButtons and NSPopup menus to.

A typical case of a master-detail interface would be to have a controller exposing a to-many relationship property. Let's call it "projects".

There would be an array controller with its content array bound to the controller, with the controller key being "projects".

There would be a table view with a column bound to the array controller, controller key "arrangedObjects", and model key "name". This supposes that each project object has a property "name".

Next to the table there might be various detail controls. Each would be bound to the array controller, with controller key "selection" so that they show stuff related to whatever is selected in the table and update themselves as the selection changes. For each control, the model key would be different, depending on what aspect of the selected project that control represents. There might be a checkbox with its value binding bound to model key "completed" that shows whether a project has been completed, and allows you to toggle that state. Obviously, this supposes that a project has a "completed" property.

Pop-up menus are more complicated. For this illustration, let's suppose that a project may have a manager and we want the user to be able to select the manager for the project from a pop-up.

First is the question of what choices should the pop-up show. It may be that there's one set of managers that may be assigned to all projects -- the available managers doesn't change as you select different projects. In that case, your coordinating controller should expose an array property with those choices, say "managers". There would be a second array controller bound to that controller property. The pop-up's content would be bound to the arrangedObjects property of that. It may be useful to separately bind the pop-up's contentValues and/or contentObjects bindings, too, if the managers are represented by more than just a name.

Alternatively, let's suppose that each project has a different set of possible managers. That might be represented as an "availableManagers" property on the project object. So, there would be an array controller bound to the first array controller, using a controller key of "selection", and model key property "availableManagers". That is, this second array controller mediates access to the list of available managers of the project which is currently selected in the first array controller. Again, the pop-up's content would be bound to the arrangedObjects of this second array controller.

The second question is how the pop-up's current selection is related to the project object. Let's assume that projects have a property called "manager". Then the selectedObject or selectedValue binding of the pop-up would be bound to the first array controller, controller key "selection", model key "manager". That is, going backward, you want the pop-up to reflect and affect the "manager" property of a project. Which project? The one which is selected in the table, which is tracked by the "selection" property of the first array controller.

An important note: in the above, I wrote about keys naming properties of objects. It doesn't matter what kind of objects, just so long as they properly implement properties, including KVC and KVO compliance. They might be instances of custom classes, or they might be dictionaries. It doesn't much matter.


My other idea was to subclass NSArrayController, and override selectedObjects. With this, I wanted to bind each control to the NSMutableDictionary in File's owner (somehow), and have each control observe a key in the dictionary.

Again, I'm not sure what you're describing or why you think you need to go this way. It definitely sounds like the hard way. Subclassing NSArrayController should not be necessary for what you want to do.

This seems to be a one way street, however. I can get the keys in the dictionary to initially set the buttons/popups, but nothing else.

This goes back to the question of how you're establishing your "plist" data structure in memory. Are you sure you're getting mutable containers? If you're using NSPropertyListSerialization, be sure to pass the NSPropertyListMutableContainers mutability option.


Is there any sort of example that shows how an NSDictionary can drive a pile of controls with bindings? Or should I just brute-force this, and write other code?

Again, the verb "drive" sounds wrong to my ear, especially since NSDictionary is a passive data collection and drive is an active verb. It also seems to get the roles of things the wrong way around. The model doesn't "drive" the view. The model should be unaware of the view and even the controller.

Also, as I said above, the use of NSDictionary shouldn't change how you think about this problem, nor does it change the way bindings work. Any bindings example applies, just so long as you think in terms of objects having properties, which you can reference by name a.k.a. key.

So, Apple's discussion of the Master-Detail Interface here applies, regardless of how the model is implemented:

http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Tasks/masterdetail.html

Cheers,
Ken

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to