Newbie encapsulation lessons learned
Hi! I've been building my first iPhone application for a few weeks now and it's coming along splendidly. However, I realized my class design has some weaknesses because of the kinds of connections I was making across classes and some compromising I did with my NIBs in order to move forward. So let this be a lesson to all you newbies out there!
First off, I began building my application with a Tab Bar project, which was a great way to dive into the complexities of NIB-land. It was very easy to get started with and right away offers clues into how you're expected to set up your interface.
In deciding how to organize my data model in relation to the project I decided to put it into its own controller class, a simple subclass of NSObject, rather than incorporating it into the application delegate subclass. This object, CalcController, is the central hub of the program, so all the ways the data can be read or modified are represented by methods in this class.
This part I actually got right! Whenever some interface action happens it calls back (eventually) to CalcController which modifies the data and calls the active View controller to update its views appropriately. I was very happy to discover the tabBarController:didSelectViewController: delegate method for the purpose of knowing which ViewController it should be talking to.
One problem I have with this model is that each of the ViewController subclasses in my tab bar needs an outlet to the CalcController, and I have to remember to connect them all up in IB. If you're using multiple XIB files, as I was starting out to do, there's some additional confusion for the neophyte.
In relation to that, the second compromise I made was to jump ahead and incorporate the SecondView.xib stuff into MainWindow.xib. The problem was that I designed some of the sub-views of SecondView to call CalcController directly, so they all had outlets for it, and I thought, oh now I have to get into Proxy Objects or something... I think I'll just skip all that and put them all in one NIB!
Of course, the purpose of a ViewController is to encapsulate a view and its sub-views. In the world of view NIBs the ViewController is the main arbiter of everything inside. So my subviews should only know about, talk to, and receive messages from the ViewController that manages them. Only the ViewController should talk to CalcController as it needs to.
So here begins a lesson in badly-thought-out encapsulation and why it matters. I'm redesigning my whole view situation so I can parcel it out into its own NIB once more. Encapsulation is a good thing.
The thing that panicked me was just not knowing how I was going to connect MyViewController in its isolated little NIB with the CalcController instance sitting happily in the MainWindow NIB. I thought I'd need to set it explicitly in the awakeFromNib: method - but that's exactly the kind of thing the NIB loader is supposed to do on your behalf.
Later on I looked more closely at MainWindow.xib and saw that each tab in the Tab Bar consists of the ViewController subclasses I was worried about, and these were already connected to CalcController. Since the File's Owner of SecondView.xib is the very same ViewController subclass it would appear I didn't need to worry about that connection being lost.
Still, there are some things I like being able to do that seem to rely on having a direct connection from my subviews to the main controller. For example, one sub-view draws a colored grid in relation to information it gets directly from CalcController. It's a nested loop of 12 x 7 elements, and it's annoying to have to ask its owning controller to fetch this data from the main controller.
Naturally I don't like the overall arrangement much. I suppose I could use myViewController.theMainController.theData.theProp erty or grab a local copy of theMainController in my subview. I'm just grousing over the extra setup required.
Thinking this over a bit more I suppose the ideal thing (in the land of perfect encapsulation) would be to have the myViewController deal directly with the data it's going to use for the grid subview by querying the main controller, and there produce a display-level representation of the data appropriate to the sub-view, which can then just dumbly draw.
In working out how to implement a proper Cocoa Touch application I've run into the classic snags. But then this is just how a typical ad hoc application takes shape (and I'm an ad hoc style coder!) when you're first learning a new system.
This is my second Cocoa project, and my first for iPhone, so of course I'm cutting myself some slack. I'll most likely sort out the encapsulation issues before I release this thing to the world. But on my next project there's definitely going to be a lot more up-front planning.
One thing I hope to get clear after cleaning this up is how to take a sub-view I've designed and re-use it multiple times in my interface. I think I have the overall gist. First I encapsulate my reusable widget in a MyWidgetViewController subclass and get it working as a unit, nicely encapsulated of course. Then I'll take the whole unit and put it into its own NIB. Wherever I plan to use my widget I'll add a MyWidgetViewController and set its properties so that it loads my widget. Voilą!
Which is a good thing, because i have a widget I really want to re-use!
__________________
|
| Why wait? ChordCalc 1.0.4 ... Hundreds sold worldwide !
|
|