Mac OS X Essentials • 1:05:32
Cocoa bindings is a collection of technologies that provide a means of keeping model and view values synchronized in your application. Find out how to create a more consistent user interface with less code, and how to refactor your code to leverage built-in classes. Bring your laptop and walk through a practical introduction to Cocoa bindings.
Speaker: Malcolm Crawford
Unlisted on Apple Developer site
Transcript
This transcript has potential transcription errors. We are working on an improved version.
So, my name is Malcolm Crawford, Technical Writer at, with Rent-a Mob, apparently... Technical writer at Apple, welcome then to this afternoons session, getting started with Cocoa bindings. What's the intention for this session? First of all, it is very much a getting started session, so in previous years, we've got into rather more detail than we're going to go into in this session.
The presentation's going to be broken up broadly into two sections. First of all addressing these goals; to answer the questions, first of all, what is Cocoa Bindings? What's the problem that it solves? Why would you be interested in using Cocoa bindings in your application? What benefits will you accrue and so on? And then the rather trivial question right to the end, what is a binding? And the goal, really, in the first half then, is to provide you with an understanding of how bindings actually works and in particular then, to prove that its not magic. There's no magic involved in this. You can write the code that's required yourselves to establish bindings and so on. That's part of, part of what we'll see.
Part of the goal is to then show you the simple rules and technologies that underlie how bindings work. To many people, bindings sometimes appear very opaque, very complicated, that's typically as a result of what you might view as being emergent behavior, as a result of the combination of very simple rules. So, understanding the simple rules and behaviors that underlie Cocoa bindings will help you diagnose any problems that you might have. So that's the majority of the presentation.
During the second half of the presentation, or second quarter or so of the presentation, we'll look at some of the difficulties that people sometimes encounter and some thoughts as to how you might approach learning bindings and so on. So learning from those who have gone before you. What else will you learn? Primarily during the first three quarters or so, first of all then, what are the technologies that underlie bindings? What is it that you have to understand in order to figure out how bindings works and so on. We'll look at, how do you actually establish bindings, both in an interface builder and very importantly, in codes. This goes very much towards the let's prove that bindings aren't magic. You can write the line of code yourself that's required.
We'll also then look at what's the role of an NS controller object? What are, for those of you who've looked at any of the bindings related materials in the interface builder, what are those green cubes or green spheres that appear with an interface builder? So, let's actually get on, straight away, with answering the basic questions. First of all, while you are here, what is, what is Cocoa bindings? Let's address that straight off. It can be addressed in very, with a very simple answer. A collection of technologies for keeping models and views synchronized.
That encompasses the whole of what bindings are about. There's really not very much to it in terms of concept. Why might this be of interest to you? Well the way that bindings approaches the problem typically means that you write less code yourself, a whole lot of code we'll see, disappears when you use bindings and typically, also, bindings means that you end up with a more polished, more refined user interface that takes care of some of the edge cases that you don't necessarily have time to deal with, at least in version one of your application.
So, bindings is a collection of technologies for keeping models and views synchronized. What's a problem they solve? Well, from a rather more abstract perspective, for those of you in particularly who managed to do the headstart. Can I just see actually, who managed to go through the headstart sample? Anybody? A few, okay, so I'll have to do a quick recap then of exactly what the headstart sample did.
So I might go into a little bit more detail. Bindings give you an abstraction or refractoring of some of the typical sorts of problems that you have to solve within your own application or just about every application that you have to write. And that was illustrated then in the head start sample, so actually go through that now.
The headstart sample basically took a very simple application, not as simple as the one we're going to consider in a moment, but still, fairly simple application that had a table view that displayed a number of URLs, allowed you to add a URL to the table view, inspect the detail of a particular URL so it had the URL, creation date, and title associated with it.
That was the user interface. To make sure everything worked correctly, it was a document based application so it was a custom, sub-clause of NS document, that first of all, had a whole lot of outlets pointing out into the user interface. I'm trusting then, that everybody's aware of what IB outlets are. Anybody not know what IB outlets are? Okay. So I'm afraid you're going to get a little bit lost for two of you.
But, bear with me, hopefully it will make sense as we go along. So this is going to be a very, this is going to be definitely a crash course to Cocoa for you then. So, outlets, as people typically familiar within any Cocoa application for reaching out into the user interface, and it manages then, an array of bookmark objects.
So in the sample application, well you didn't actually write loads of code here, I wrote some code and you got to see the code at least, for doing fairly menial tasks. Moving data from the user interface down into the bookmark objects, as a user entered data, added bookmarks. Managing selection handling, so if they use a selected a new bookmark, make sure that that was displayed in the underlying detail field. And then the application also supported drag and drop, and it might, if I had actually been bothered, have also supported sorting and filtering.
So, although this revolved around a table view, really is a sort of recurring theme throughout almost any application that you write. You have data in your use interface, you have underlying model objects and you've got a shuffle information between the two as a user edit data or maybe something else, change let's say an AppleScript to whatever, changes the data underneath. That means to reflect it in your user interface. And anybody here actually enjoy writing that code? Oh, one, two people, two, two masochists in the audience, then excellent. You can ask them later to do that for you, for a fee doubtless.
It's typically some of the most uninteresting code that you have to write. It's fiddly and error-prone. How many people have had the situation where there's that one text field at the edge of the window that actually didn't update properly? I'm sure, or is it just, okay, it's just me. Typically, you have to remember, okay, what are all the different outlets that I've got, make sure everything is updated appropriately and it's an ideal candidate basically, for abstraction and refactoring make somebody else do all the work for you, ideally, the Cocoa Team.
So, if we're going to give this task to somebody else to perform for us, what are the things that we got to be able to do? We've got to be able to tell whoever's taking care of all this gnarly mess for us, first of all, what should be kept in sync with what.
We've obviously need a means to specifying that, we've also got to be able to provide a way of shuffling the data around both from the view down to the model and from the model out to the view. And we've got to be able to do things like keep track of the current selection, so if we're dealing with more than one object, if we're dealing with a collection of objects, whether it's an array, a tree of objects, whatever, we want to be able to make sure that we know what's currently selected so it can update any detail views and so on. And, we've got to do all of this without require you to adopt a particular root class. We want to make sure that this works with any class you throw at us.
So, that's a fairly steep collection of requirements. We'll come back to those later. For now though, what's the final, what's the answer to the final question, what is a binding? So, if you'll forgive me just reading this one, a binding is a specification that an attribute of one object is to be kept synchronized with a property of another, with options.
Hooray, good, right. Let's actually try to understand what that really meant, with a more simple example then, than the bookmarks examples. So, rather than looking at, its, although the bookmarks example is not particularly complicated, but we can make it simpler still and still address the fundamental issues, the fundamental ideas, that you need to understand bindings.
So, its actually incredibly difficult to come up with a convincing real world scenario for an example that's this simple. But, imagine you're sitting down to write a write, to write a mixing desk ap, desk application and you have track objects that know what their volume is and the title of the track. And you're just setting down to prototype this so you have your model object and all you have on your display is a single window that has a text field and a slider and for the sake of it, we'll have a mute button on it.
I hope everybody's convinced by this. To make it simpler still though, let's forget about the title, so all we're dealing with is a single window with a button slider and a text field. When the user enters a new value into the text field, it sends the control object, the my document, beg your pardon, the document then has outlets reaching out into the user interface, so its knows about the text field and the slider. When those are activated by the user, they send a message to the controller objects to update the appropriate volume.
The mute button, then, sets the volume to zero and the document knows about the single track object that its managing. So, that's a very simple application that we're going to consider during the hands on session, see how, may that work with bindings. Before we go on though, one of the criteria that we had for a collection of, for our abstraction refactoring, was that we're going to require this to work with any class.
So, let's consider that in the context of our very simple application. Typically, in a custom class, we have custom accessor methods for the various properties of those classes. So the properties, for example, are not going to include the IB actions so just the actual instance variables of the relevant classes, typically don't have accessor methods for the IB outlets, so you may imagine then, that the document class has an accessor method for track to get and set the track value. The track itself then has accessor methods to get and set its volume and so on.
But, if we're trying to generate a reusable technology, then making use of these custom accessor methods is going to make the technology not reusable, we wouldn't be able to reuse any of the abstraction if we used custom accessor methods. So instead, we're introducing this idea of keys and key paths.
Keys are simply strings that specify properties. Can I just see, who's come across this, who has, I should ask the negative. Who has not come across the idea of key, of key value, key value coding, that sort of thing before? Who's not come across that? Oh, okay, so I can just pretty much skip the rest of this.
People are aware that volume, as a string, is a key for the volume attributes and then who's come across the idea of key paths? So its volume, does track dot volume means something to everybody? Who has not come across that concept before? Okay, for those of you who have not come across that earlier, the idea of a key path is it allows you to traverse relationships between objects.
So a document instance has an instance variable track, the track itself has an instance variable volume. So for a given document, we can access the volume of its track using the key path track dot volume. So a key path is simply a concatenation of keys where the relationships limited by dots, by force dots or periods, depending on your locale.
Okay, so with that in mind then, what do we have to do if we want to set up bindings, given what our specifications of bindings it was? Its actually two parts that I want to go through. First of all, how do you set up bindings? And then, what are, what are the technologies that lie behind bindings? So, in part, with the set up technologies, first of all, is key value binding, we'll come back to that in terms of the definition of what is a binding in a moment. Then as part of the binding process you set up observation using key value observing.
And then there are these controller things. So let's address the controller things first. The controller things are set up to abstract the function of managing either a single object or in many cases, a collection of objects. So there's a number of different controller objects. First of all one for a single object, NS object controller. And then there are a variety of other ones for managing either a dictionary, new in Leopard, an array or a tree. And there's a couple of controller objects for managing user defaults.
The main one that we're gonna consider during the hands on session just is the single NS object controller. So we've got this, all these object controllers and what do they actually do? The main thing that they do is manage their content. You pass over to them responsibility for managing the object they have, or objects that they have as their content. That may be a little bit subtle. That's something else we'll come back to in a couple of slides' time.
If you have a collection of objects, if they manage a collection, one of the great things that the object controllers do for you is manage the current selection. That'll be more obvious in the headstart example. We're gonna come back to that at the end. They also provide a fair amount of other functionality like supporting filtering and sorting.
And if you want to allow deferred... partial editing of values within your user interface, they'll allow you to monitor more, more readily when something is being edited. So you can for example make sure that you don't allow a user to close a window before our change has been committed. So those are sort of benefits that a controller gives you.
Where would a controller fit into a very simple application? Well, basically right in the middle between user interface and the document. So again to interpose an instance of NS object controller to manage the single track. Currently the track is managed by the document instance. But we can tell the object controller, you should get your track from the document.
That's an important thing. Again we're going to come back to that in a couple of slides time. So if that's the controller part of the setting up bindings, another part of the set up was the key value binding protocol. There are a number of methods in the key value binding protocol. The only one that we're interested in at the moment as part of the getting started theme is the method that's used to actually establish the bindings. So the methods that's for that, that's used for that is bind to object with key path options.
What does that method do? Well, fundamentally you could state that as being a specification that an attribute of one object is to be kept synchronized with the property of another with options. So remember the answer to question three, what is a binding. In some respects, that method is a concrete manifestation of what is a binding.
It's what you set up using that method. One of the things I'd like to sort of, sort of get you to think of here is that a binding is established rather than created. If you think about the way that you set up target action in Interface Builder when you control drag from say a button down to an object that influences an instance, an action method. When you make that connection, you're actually setting two instance variables. The target and the action.
In a similar way, when you start out binding, you're not actually creating a binding, you're filling in information to say what should be kept synchronized with what. So if we set up the bindings for our very simple application, we know have our object controller into between our interface and the underlying document. And we want to specify, we want to bind the content object of the controller with the document track.
The important thing here and the reason for not binding directly to the track in the section is that if the track changes, then if the user interface elements are bound to the controller as the intermediary, if the track changes, then the user interface elements start following as it were the new track rather than continue to be bound to the old track.
So this is another reason why we have the object controller sitting there in the middle. If the track changes, we want to keep the user interface synchronized now with the new track. So for both the text field and then the slider, we want to make sure that their values are kept synchronized with the controllers selections value. So what is the content object or in the, the case of an object controller is just a single selection. But if you had an array or a tree controller, you may have multiple objects of which just one is selected. So the selection of that object controller.
So that's setting up bindings. But there's another part to setting up bindings. This comes of the key value observing heading. Key value observing, the set up is the first step in the notification process. Key value observing allows an object to tell other objects that one of its properties has changed. In order for it to tell those other objects, those other objects have gotta tell it that they're interested in that change. So registering that interest is performed in the registration side of KVO using the add observer for key path options context method.
I'm not gonna say anything more about that now It turns out we actually don't need to know anything more about that at this stage. It's just the first step in a change notification mechanism. So in the registration side of things, just in the same that we're setting up bindings, in fact at the same time that we're setting up bindings, the object controller registers as being an observer of the documents track, the text field registers an observer of the object controllers selections volume as does the slider. So with that, I'd like to go to the hands on stage. So I'm hoping that those who want to follow along have had an opportunity now to download the example. Still getting a 404 error.
( Inaudible audience comment. )
Okay for those of you who did not hear that, apparently if you Google for simple bindings adoption, you will get that as the first result. Thank you for the gentlemen in the Google T-shirt.
( Laughter )
I'm not joking, he is. I hope that at least you'll be able to follow, there are two parts then to the demo, to the hands on session.
First of all establishing bindings programmatically and then in Interface Builder. So if you haven't managed to download the application yet, perhaps you could just wait for the download to happen. Follow along with the code section of it, then the much more exciting is the doing stuff in IB anyway. So the application is very simple. If I just build and run it straight off, and then hide, opps. Hide Xcode.
This has got a trivial user interface. What I'd just like to draw your attention to straight off though, note that the user interface is as it were uninitialized at this stage. So when the thing launches, we've been a little bit lazy, well I think a little bit lazy. It doesn't show the current volume and the slide is at its default value.
However, if I press the mute button, the volume goes down to zero and I can type in a new value and it goes, the user interface is updated appropriately, the slider of course for volume goes up to 11. A good quarter of you got that, excellent.
( Laughter )
The ironically the track object on model object is probably the least interesting part of this, for this particular demonstration. So you got a simple track object that has, that it has a volume and a type as I already described with suitable access methods implemented. That's all I'm going to say about that at this stage though. The document class though is a little bit more interesting in this context. It has as I showed in the slides earlier, outlets to the slider into the text field and one thing that wasn't apparent in the original block diagram was method update user interface.
That is invoked both by the update volume from method and by the mute track method. So in response to any user interaction that changes the tracks volume, we must go out and update the user interface accordingly. Well, that's exactly what we don't want to do. We don't want to have to write the code and maintain the code for doing that ourselves. So let's throw caution to the wind and assume that as part of our new bindings based implementation we're not going to need that method. So let's delete that. That's always a good start of a code demo, delete stuff.
So given that we're deleting that method, let's delete it from both the mute track and from the update volume from. So hope since I'm deleting stuff here, people can keep up with that part of it at least. Save and let's just delete that method from the header file as well.
What I do need to do though, what we need to do though is make, set up the bindings now. And create any of the associated objects that, that I need to support the bindings. So the first thing that we said we wanted to do was to create a new object controller.
So we want to do this early on in the lifetime of the application, before the whole lot of user interfaces have been populated. So the document architecture gives us a convenient method for doing this, window controller load nib. For those of you who haven't used document architecture before, this is akin to awake from nib. For those of you who haven't heard of awake from nib before, it does pretty much what it says. So within this method, we want to create a new object controller.
So this is code that you now have to type in if you're following along. Sorry, there is gonna be some code. Remember we're doing this in two stage, code based and then Interface Builder based. Do remember the what is it, control, control full stop auto complete for Xcode? So create a new instance of object controller.
( Period of silence )
Then given this new instance of object controller, we want to tell it where it's going to get its data from. What's its content object? So we bind its content using the key value binding, binding establishing method. So we tell a controller to bind its content object, so be careful with the capitalization there. The O is capitalized. To object self.
Remember we're editing at the moment the code for the my document instance. This is my document class that has the track object. So it's my track object with key path track. Put that under a separate line so it, so it's more obvious. So the controllers objects contents is gonna be my track and to save writing additional lines of code at this stage, no options. So that set up the content for the controller object. We now want to make sure that the text field and the slider are set up appropriately.
So we pretty much replicate that code for first of all the text field, except we bind the text fields value, so whatever it is that it's displayed in the text field to the controller that we just created with a key path selection dot volume. So whatever the current selection in the object controller and because the object controller only has a single object, it's always the single track. Whatever its volume is. And then the easiest thing to do is actually just copy and paste but, we also then want to bind the slider. So either you can drag it in from your helpful assistant, or easier for you copy and paste and type in slider.
( Period of silence )
So four lines of code basically and there I hope everybody's kept up with that who wants to keep up with that. Not really very much, I hope that's not very complicated code either. If we build this and run. Low and behold our user interface is set up appropriately. Note that the volume is now set appropriately as well.
If I change the slider, the volume updates appropriately. If I type in a new value, the slider updates. If I hit mute, the volume goes down to zero. So for those of you who managed to follow along, congratulations, you, for those of you for who this is the first time, you now have created your first bindings based application. And you did it the hard way. So congratulations. Now let's do it the easy way.
So I'm hoping then that those of you who had to Google for the source code have now managed to at least download it and we get on to the second part of the example, we can just do, do exactly the same as this in Interface Builder. So the useful part here is that in addition to the other methods that we just had, that we just deleted, we can actually now delete that method that we just wrote. So sorry if that appeared like a waste of time, but we'll come back to see why that wasn't a waste of time in a moment.
So now let's establish those bindings in Interface Builder. So open then the my document class, I beg your pardon, the my document nib file. The first thing to do is to add an object controller. So who is not running Leopard? Anybody not running Leopard? So for those of you who are not running Leopard, you should see in your Interface Builder palette an icon that looks pretty much like this under the controllers button. Drag in a new instance of NS object controller into the nib file.
So that's the object controller that we created. We now want to tell it where it's going to get its information from, what's its content. So the easiest way to do that in both in Tiger and on Leopard is just to press command 4 to bring up the bindings inspector. We need to tell it where to get its content object from.
So its content object is going to come from the files owners. So the instance is my document. Click the bind to button. And the model key path is going to be let's say selection dot track. Press return. For those of you muttering, yes. Now we can set up the bindings for the text field and for the slider. So select the text field and slider. Select the text field first. Look at its value binding. We want to bind it to the object controllers selection.
Volume. Press return. And this same for the slider. So text field and slider both bound to the object controllers selection dot volume. So if I save the nib file now. Go back to Xcode. Build and run. The window didn't appear. Hm. What happened? Well, despite the fact that we're using bindings, let's try looking at the debugger console.
It seems there's a lot of error messages. One of them, well they all basically say the same thing. A message was sent to an object that is not KVC compliant for the selection property. And it was sent to an instance of my document. So it seems that somewhere along the line we've tried to access the property selection of an instance of document, and documents don't have a selection. So a couple of you, it sounded like a couple you had spotted the deliberate mistake.
The object controllers key path didn't want to be selection dot track, it just wanted to be track. The my document instance doesn't have an idea of selection. It's just managing its own tracks. If I change that,
( Period of silence )
The application now behaves correctly. So for those who managed to follow along in that section, congratulations, you now also managed to go through your first bindings debugging session as well.
I hope that everybody who wanted to go through that managed to download the sample then and go through that. That's the end of the hands on session, so or the hands on part of the session, so my apologies then to anybody who is still left behind there So if we go back to slides and let's just recap what we actually did in that.
First of all, in setting up the bindings programmatically, one of the great things we did straight off was remove a whole lot of code. So all of that gnarly code just for getting stuff from the user interface and shuffling it around went away, which is good. Because we were establishing the bindings programmatically first of all, we kept the IB outlets. Okay, fair enough.
And then used the key value binding method to set up the bindings. And as a side effect, remember what I said about more user interface polish with less code, the user interface was properly initialized when we, when the application was launched. Importantly also I mentioned that the model class ironically in this case was the least interesting part of the example. We didn't touch that at all. That's something else to come back to in a couple of slides time.
For those of you who are concerned about the object controller, we did alloc init auto release. In some respects, by rights it ought have disappeared at the end of the window controller did load nib method. But actually an important thing to bear in mind is that objects bind, sorry, retain objects to which they're bound. so given that we established a binding between the, sorry between the text field and the slider and the object controller, they made sure that the object controller didn't go away until they were finished with it. So there actually wasn't a memory management issue there at all.
In the second part of the demo, we removed all the bindings establishing code, removed, well actually I didn't remove the outlets, should have done. And we didn't change the mute track method. That stayed exactly as it was. So basically the second part was just removing code and setting things up in Interface Builder.
It's important to bear in mind though, this is, this getting to the bindings isn't the magic thing then, that those two parts were equivalent. Think about setting, establishing a binding. We had it in a, in one of the cases simply a text field whose value needed to be bound to the selection dot volume of the object controller. You did this in two ways. In Interface Builder and in code.
In Interface Builder the inspector looked pretty much like how it does on the left. And code that you wrote was as it is on the right. One of the key things that I want you to take away from this presentation is that these two are equivalent. These two are exactly the same thing.
One is done in Interface Builder, the other is done in code. If we work our way down what the parts are of the Interface Builder inspector, and the arguments are in the bind method, you can see exactly where the equivalences are. At the top of the bindings inspector for this particular binding, we have the binding name. That's the first argument to the bind method, what is it, what property is it that you want to bind? The second part of it is what's it going to be bound to? The object argument to the bind method, we specify that in the pop up.
The third part was well what's the key path to the object to which we're bound? That's specified by this combination of controller key and model key path in the Interface Builder inspector and by the single argument, the key path argument in the bind method. And then finally the options simply a dictionary of any other settings that you want for this particular binding.
And it just so happens that in this case, nil is the same as setting those defaults in Interface Builder. So key point here, this and this are the same. So for those of you who are confused by this, I hope you're not confused by this. Anyone can write a line of code.
So when you look at that and you're confused, think well I could have written that. So it's easy. If you really want to go away and write all your lines of code, all, establish all the bindings programmatically rather than doing it in Interface Builder, remember those two gentlemen earlier who said that they like doing that, so go and ask them.
So that's the first half of setting up, of looking at what bindings are and how they work. Actually setting up the bindings. Well, what's happening behind the scenes? What technologies do we have to understand? What technologies does Cocoa bindings use to do its work for you? The first part, we actually got a clue about in the error message that came back when we set up the binding incorrectly.
Key value coding, actually whose not, I think I asked before. Who's not heard of key value coding? Okay, so key value coding everybody familiar with, gives you a means of managing and accessing an objects properties by key, by extension key paths allow you to transverse relationships between objects. An important thing about key value coding as pertains to Cocoa bindings though is that it's really sort of unicast.
Key value coding is used to send, to update values from a single user interface element to the property to which it's bound. So it's as it were one object at a time. Key value coding with paths, everybody, I'm guessing then that everybody is familiar with, so don't need to say anything more.
Or much more at least. Key value something, using key value coding allows you traverse key paths to both get and set methods. So get and set value, get and set values. And key value coding continues to use custom accessing methods if they exist. So it gives you a convenient way of eliminating a fair amount of code.
To reiterate a point that I made earlier. Key value coding then is used to propagate changes from views to the controller or through the controller down to the model. So if the user changes a value either in slider or in a text field, in these particular cases, the selection dot volume of the controller is changed using key value coding.
The other side of this, we come back to key value observing. So remember when we were key value observing? We looked at the, key value observing registration part in binding set up, the reason for setting up key value observing was so that if a change was made to the value of a model object, that could be broadcast throughout, through the whole application to any object that had registered interest in that.
So we saw the registration part of it, key value observing noted, change notification is passed out by using the observe value for key path of object change context method. So any objects that are bound need to implement this similar line to receive notification that an objects value has changed. This is sort of multicast because there may be many observers. So in the case of our simple application, we have a change of a value going from the model to the controller to the views. You have to watch this one carefully.
So focus first of all on the mute button. If the user presses the mute button, the volume on the of the current track is set to zero. That then results in an observe message being sent to both of the objects that registered an interest in that value, namely this text field and the slider.
How come all of this just worked though? Because we didn't actually change the track class. We didn't do anything special with the text field or the slider or anything. We just used those in the bindings based versions of the application without any changes. Well, it turns out that using the appropriately named accessor methods, typically assures both KVC compliance as it sounds like most of you are aware but also KVO compliance. So provided you use appropriately named accessor method in your model classes, you will also get KVO compliance, key value observance compliance. Your model objects will automatically send out the appropriate KVO notifications if values change.
So the Cocoa team has done some additional work for you there as well. Let's summarize then what we've actually learned during this first three quarters or so. First of all we said that Cocoa bindings are a way to abstract and refactor some of the least interesting parts of your application. And we gave this set of requirements. We need first of all to specify what should be kept in sync with what. And so on.
And we also said that it's got to be able to work with any class. Well, we've now gone through and addressed all of these needs. To specify what should be kept in sync with what, we have key value binding. To move data between the view and the model, we have key value coding.
To move information the other way, we have key value observing. To keep track of the current selection and so on, we have NS controller and its subclasses. So NS object controller we've looked at and there are others which I'll turn to in a moment. And then in order to be able to work with any of your existing classes, we have key value technologies. So KVC, KVO and KVB.
I just want to turn this around quickly for a moment to look at things the other, from a different perspective to summarize the actual technologies themselves, key value bindings, the NS key value binding protocol used to establish bindings, but then key value observing used to actual register the dependency, key value coding is used to communicate from view to model, key value observing used to propagate changes in the other direction and then NS controller and subclasses used to, used to manage model objects.
So I'm not sure if that seems like an awful lot or not lot, but basically that's the fundamentals of all you need to know in order to figure out how bindings works. It's no more complicated in its fundamentals, in the basic technologies than that. Everything else basically is a permutation of those.
How though does it correspond, how does it work with the bookmarks example? So for those of you who looked at the, the headstart and for the example that I gave through the beginning, if I just go back and do a, do a quick comparison of two implementations of the bookmarks example.
For the headstart, this is pretty much the example that I gave before with a couple little tweaks. The data new data source code has been to away separate, data in a separate file. The first action uses standard Cocoa technologies, the second uses bindings. Rather than going through the source code to each of those, I just want to compare the two implementations.
So let's compare those in file launch. I'm not interested in the two things that are identical or that are being added or deleted. I'm just interested in the things that have changed and primarily actually the code that's changed. So we had a document instance that was managing the user interface.
I'm going to, another heads up here, the source code to this is also available in ADC, so you may have seen already the with and without binding sample code. So I'm just gonna go through this very quickly now so that you can have a look at this afterwards yourselves and do a direct comparison. But I just want to highlight a few things. so in the without bindings example, we had a whole lot of user interface outlets and a whole lot of methods for responding to user interface changes and propagating back to the user interface.
From the, just from the header files it looks like all of those disappeared. That's a rather good thing. From the implementation, there was only one change it turns out in the implementation but it was quite a big change.
( Period of silence )
So all that code dealing with the table view data source and propagating changes and so on, that all disappeared. So that's good too.
That's not a particularly profound thing though. The table view data source is a thing that I really want o concentrate on for, for this next section. The table view data source as you might hope for, the for a bindings based example, the table view data source method is pretty much all disappeared, but the ones that supported drag and drop didn't. Which is sort of a little bit interesting.
Let's have a look at the implementation. Opps. There are six differences. First of all the data source method has disappeared as I just said. But there a few other small changes that percolate through. Most of the table view drag and drop methods are the same though. There's a few little things that I'm actually creating new objects and figuring out where an object is gonna get inserted and so on.
But again, most of the stuff is still the same with the exception of the deleted methods. So why is that interesting?
( Period of silence )
Well first of all, a whole lot of source code went away. That's encouraging. But the thing that I didn't show you, the crucial thing that I didn't show you.
Whereas in the non bindings version, the table view data source was implemented in the document subclass as a category. In the bindings implementation, I had a subclass of NS array controller. So NS, a custom subclass of NS array controller had taken on the responsibility of a managing drag and drop in the table view.
So as I said this is available as sample code. Looking at that pictorially, what actually happened, all of the code that was written originally to move data around and so on disappeared and was replaced by an instance of NS array controller. But specifically a custom subclass thereof that still had the drag and drop code. That you still had to write.
The point behind this is to get you to think about the different roles that objects have in bindings. The controller, the NS controller objects are still controller objects, but they're still also changeable by subclassing. If they don't do exactly what you want, you now have a different locus of control that you can, to which you can give responsibility.
In a typical application that uses, uses a table view, your main controller object, in this case the document class is responsible for managing the table view, for managing the selection on so on. If that, if that responsibility is pushed out into another object, that object can also take reasonability for some of the other custom behavior such as managing drag and drop as well.
So what I'm hoping is that presenting the role of the controller or the NS controller objects in terms basically of refactoring will give you a different handle and possibly a more approachable handle on the way that Cocoa bindings works. It's the sort of thing that you might have done yourself, had you had enough time and inclination. So think about the controller objects just in terms of refactoring behavior. But it's still customizable.
So that then concludes the first of three quarters or so of the presentation in terms of what are bindings, how are they set up, what are the technologies that lie behind bindings and so on. Now I want to go onto the second section, stuff that may not make immediate sense now but I hope will make sense afterwards.
Particularly as you get to an especially painful moment in dealing with bindings. The first consideration I'd like to make, make for you is a rather personal one. How many people went to the, the getting started with core data session this morning as well? Okay. Please, for those of you who did, for those of you who are just starting with both core data and bindings, both these technologies give you a high level of abstraction away from your own code. They both use the same underlying technologies. Key value coding, key value observing.
If you try to learn the two at the same time and make a mistake somewhere along the line, it can be very difficult to determine where that mistake was. So please, I strongly encourage you choose one. I don't care whether its bindings or core data. Choose one of those and master that, or if you don't master it, at least become intimately familiar with it first and then go on to the other. It'll mean diagnosing problems is a lot easier for you.
I know it's very tempting to try to do both at the same time. But choose one, master that, then go on to the other. It'll make life easier. Second thing, the controller layer sometimes people think oh it's a bit easy isn't it? I mean that controller just sits there managing a single object. There's actually a lot to it. It manages to make some operations an awful lot more efficient as well. So don't overlook its capabilities.
The other thing, sometimes people see bindings or imagine the bindings is gonna be a complete panacea or a silver bullet. I hope that one of the things that the bookmarks example showed is that you still have to write code. There's gonna be some situations in which bindings doesn't do everything for you. You still have to add your own value to your application. Hopefully that you're adding it in a more interesting place than just in the data marshalling and glue code.
And as part of that, existing Cocoa pathways technologies is still valid. It's still entirely reasonable to use the target action pattern to connect the mute button up to a mute track method. You don't want to use bindings for it. It actually makes, if you tried to use bindings to set up that, that particular button, it would end up being a whole lot more complicated. So use the appropriate technology and use it wisely. Don't think that you can just plaster bindings all over your application and just you know suddenly have no code at all.
So some other problems that you might encounter. One of the more common ones that we see is that people say that oh, my, my values didn't update somewhere along the line. Why might that be? Well there are a number of reasons actually spreading over two slides. First of all, bear in mind that not everything is KVO compliant. Indeed actually most things are not.
When you consider the whole of foundation and the whole of app kit. One of the ones that people come into particularly when they're writing inspectors, so I'll just say that again. For those of you who are going to be trying to write inspectors at some stage, trying to get hold of the document controllers main document instance, that's not KVO compliant. So if you try to make a binding to that, its not gonna get kept updated.
If you have a look at the, in Leopard, sample code of the text edit application, you'll see there's actually an example there of how to make a binding, how to make a KVO compliant binding to the main document. Another thing that we see people doing fairly commonly is not changing values in a KVO compliant way.
We just saw in the trivial example that simply invoking an appropriate access method or using key value coding typically means that KVO notifications are sent. But it's also possible to changes things in a non KVO compliant way. Particularly if you have say collections of objects. If you're simply inserting the object directly into an array, that's not KVO compliant.
The key value observing programming guide goes through a number of different ways in which you can make sure that you can adhere or make sure you are key value observing compliant. So make sure you read that documentation as well. One of the other pleas that we have from the engineering is and this may not make sense until you read the programming guide, but bear it in mind.
Don't invoke the change notification methods unless you've actually changed a value. Sometimes people see things that aren't updating appropriately in the user interface and think well maybe if I just invoke will change value for key then did change value for key, that'll send out a notification and the value will be updated appropriately.
Most of the time it is and it works. At some stage though it is quite possible that your application will crash when you try to use that. And at some stage in the future it may not work at all. So please don't just invoke the KVO change notifications arbitrarily just to tickle the user interface update.
Other reasons why user interface might not update or might not appear to have updated. Do bear in mind the granularity of changes. Sometimes it may be that something just hasn't changed yet in, in your application. And you need to wait a little while before that change is propagated through. So sometimes if you try to ask a model object for its new value when you think that something has changed, it may not have had an opportunity to change yet.
Another important, another important consideration is the asymmetry between the different bindings technologies. That is something I sort of, I try to stress as we went through. You bind a view to a model not the other way around. With a possible rare exception of web view. So changing a view value does not cause that value to be propagated down to the underlying data objects.
So occasionally we see people saying well I sent a set flow value message to this text field and the data value didn't update. Well that's because that's not how bindings works. If you want to, if you want to update the data value, update the data value directly and the view will update correspondingly. So make sure you observe the asymmetry there.
A couple of things that are perhaps less than obvious. First of all, a binding name isn't necessarily the same as a property name. So we saw that the text field had a value binding. Well NS text field doesn't actually have a value instance variable or a value accessor method or whatever. It does have a value binding though.
So don't necessarily assume that just because a particular or a particular class that you're going to interact with has a, has a particular accessor method pair or a particular instance variable that you can bind that. Have a look in the bindings reference documentation to see what bindings are actually available. And then you'll also see what the various options are as well.
Final on in this section. I said there was no magic. The one thing that gets close to being magic but its not really in bindings is that there are a couple of bindings that are set up for you automatically in table views. And this tends to confuse people when they're not expecting it.
If you bind a table columns value, so bind a value of a table column in a table view, that automatically behind the scenes sets up three additional bindings for you for the table view itself. So if you happen to want to programmatically establish a binding for content, selection indexes, or source descriptors, you must actually set those three up, set up each of those three. You can't just programmatically bind one. You have to programmatically bind all of them.
So that catches some people out. Finally debugging. One of the things that comes up occasionally in the list is people say, well I got this back trace and I don't know what's gone wrong. Well I hope that again from the example that we went through during the hands on session, it'll now be a little bit more obvious that the debug message means exactly what it says.
It's usually pretty helpless. Well this one particular object that I'm trying to bind to is not KVC compliant for the key that you gave me. So your task then is to find well where did I make a mistake in trying to set that particular key? Things are actually a little bit better or will be a little bit better in Leopard.
When you get this error message in Leopard at some stage in the future, the near future, you'll actually get an object ID for the user interface element that was trying to set up the bindings. So you can actually go directly to Interface Builder to say okay which was the object that I was trying to bind that's got the incorrect key path.
So having gone through then setting up bindings, the technology behind bindings and learning from others, let's get on to the conclusion. What have we actually learned from this? First of all, what Cocoa bindings are, a collection of technologies for keeping model and view synchronized, the values therein. And then there are three technologies that Cocoa bindings relies on. First of all, key value binding itself, key value observing, and key value coding. And then controller objects manage either a single object or a collection of other objects.
If you want to know more, Derrick Horn is probably sitting in a room next door just waiting for your call. Feel free to contact Deric if you want to know more about Cocoa bindings or you have any feedback, any specific feedback about the technology or where you would like to see Cocoa bindings going in the future and so on. And then there is a fair amount of documentation on the subject. We are hoping that it will be improved in the future. We're aware of limitations therein.
There are though increasing numbers of, increasing pieces of sample code that are available. So a couple that have posted recently say, one in particular bound button. It said that you typically don't want to bind buttons. But for the sake of it I though it would be worth posting a, posting an example of one case binding a button does actually make a certain amount of sense.