News: Stay up to date

The Étoilé community is an active group of developers, designers, testers and users. New work is being done every day. Visit often to find out what we've been up to.


Property Value Coding and EtoileUI

Posted on 4 January 2011 by Christopher Armstrong

I'm a relatively new developer to Étoilé working on parts of the ProjectManager service. Last year I put together a basic window manager from something started by David Chisnall, and now I'm trying to get some of the other architectural bits and pieces prototyped and running, such as the task bar and project management components.

This led me to start working with EtoileUI, our user-interface framework. I was doing some stuff with Property-Value Coding, which Quentin Mathé was kind enough to help me out with. He gave me the following detailed but useful explananation about why it was created and how it interacts with EtoileUI.

Property Value Coding is a just a small addition to support reading/writing properties independently of the Key Value Coding (KVC) semantic that GNUstep already supports.

There are two methods used to retrieve and set a property: -valueForProperty: and -setValue:Property:. Both of these use the primitive KVC implementation that NSObject supports, and -setValue:forProperty: checks the property exists before invoking -[NSObject(Model) setPrimitiveValue:forKey:]. If it doesn't, NO is returned, rather than raising an undefined key exception. You declare the properties that you implement by overriding the -properties method in your model class and returning an array of strings containing the property names.

Property Value Coding lets us access values that aren't normally KVC-compliant. For example, for an NSDictionary [dict valueForKey: @"count"] won't return [dict count], while [dict valueForProperty: @"count"] will return it. This way it's possible open an inspector that lets you browse/edit any object properties even if their KVC implementation doesn't support it.

In addition ETLayoutItem overrides -valueForProperty: and -setValue:forProperty: to look up the value on the represented object when one is provided, otherwise on the layout item itself (i.e. when -representedObject returns nil). This makes possible to set/unset a represented object and have the layout item continues to return valid values (at least for basic NSObject properties).

When the layout item has no represented object, -[ETLayoutItem setValue:forProperty:] automatically adds the given value to a dictionary in case the property doesn't exist yet (instead of returning NO). This allows to use ETLayoutItem as an extensible model object when it makes sense. For example, in a compound document, the layout item tree is the model, so we can just store the property values on the layout item itself.

You can also use a layout item as a represented object of another layout item. This is how EtoileUI creates the "meta" UI representation visible when you call -inspect: on a layout item.

The general rule is that you don't have to call -valueForProperty: and -setValue:forProperty: from within your model objects, you should just ensure these methods return the right values for the properties you declare with -properties. The NSObject(Model) implementation don't have to be overridden if the model objects have accessors for each property.

The only common case where you call -valueForProperty: and -setValue:forProperty: is when you write EtoileUI aspect subclass (style, layout etc.) which want to read/write model properties. In this case, you are expected to always invoke these methods on the layout item and never directly on the represented object.

Model objects which support basic Key Value Observing (KVO) compliance (posting a KVO notification every time a property changes) can be used as represented objects that trigger automatic UI updates. To do so, your model objects must override -[NSObject(Model) observableKeyPaths]. ETLayoutItem can then intercept the KVO notification and trigger a redisplay or update its widget view.

As mentioned before, the method -[NSObject(Model) properties] declares the object properties that can be accessed through Property Value Coding. This method is going to be renamed -propertyNames soon since it conflicts with various Cocoa APIs. In future, the NSObject(Model) implementation will be changed to return the property names from an object model description bound to it rather than directly as it does now.