Architecting iOS Data Flow
July 16, 2013
One of the most important pieces to the Objective-C puzzle is realizing that most objects don’t really hold any data. Instead, they point to some other object that points to some other object which has actual data. This is how the iOS data flow works.
The majority of the time, classes like NSString contain – and data structures like bitmaps are – the only real data in an application. Sure, we have objects that represent a bigger things, like a Person or a Button, but these objects just contain references to real data. For example, a Person will have pointers to NSStrings that represent its name, phone number, etc.
The concept of a Person or a Button are just interesting formations of the same basic elements available in a computer, in the same way humans and trees are formations of the same elements available on earth.
But the real take-away here is that a Person isn’t just an object, it the root of an object graph. The idea of a “Person” object gives us a convenient way of addressing a collection of objects meant to represent something that we intrinsically understand in the real world.
This idea is why MVC is such a powerful architectural pattern for Objective-C: the same fundamental elements that make up a Button also make up a Person. We can extract one of these basic elements, like an NSString, from a Person and inject it into a Button. The same data can represent different things, depending on the formation around it.
Because of this, views don’t have to understand anything about a Person. Thus, we can reuse views like UIButtons and UILabels in any application, regardless of how the model objects are organized, because they are all made up of the same molecules. (Which reminds me for no particular reason, that if you haven’t see the movie Gattaca, your life is incomplete.)
I was recently asked by @htormey how to pass model objects between view controllers. And I think that what you’ve just read is necessary to understand the answer to this question.
While a Person is the root of an object graph made up of components that represent what a person is, a Person object can just be another component of some other object graph meant to represent something else, like a Company. A Company could hold many instances of a Person in an NSArray and alongside the array of Persons could be an array of Projects the company is working on.
In the same way we can break out an NSString component of a Person to display in a view, we can break out components of a Company into view controllers that know how to manage those particular components.
For example, in this theoretical application where there is a Company made up of Persons and Projects, there might be a view controller that can edit the contact information for a Person within that company. Does that view controller need to know about the Company that employs the Person to edit their contact information? No, in the same way that a view doesn’t need to know that an NSString came from a Person.
An object should only have access to the information it needs to know about to perform its job. In the case of a view object, this restriction of information is for reusability; the same button can exist in any application because it only deals with the fundamental objects available in all applications.
For view controllers, this restriction of information is for maintainability. For example, if you give a contact-editing view controller access to the whole company, you are opening yourself up for a problem.
One of the best possible ways to determine if your code is going to be difficult to maintain is to draw out an object graph of the important objects in your application and draw lines between objects for every pointer. Each line introduces a dependency. The more dependencies, the more code you will break when you make a change.
This problem typically doesn’t manifest itself right away. Instead, you give too much data access to a view controller because it is the easiest way to solve some problem quickly. Later on, someone else (like a co-worker) comes along and doesn’t realize that this view controller shouldn’t be accessing some data it does have access to, accesses it, and now you have dependencies. (Don’t work in a team? Still applies; you today is “someone else” than you 3 months from now as far as code is concerned.)
Thus, a controller should only have access to the subgraph of model objects that it is operating on. This means that a view controller that edits a Person’s contact info will have a property like this:
@property (nonatomic, strong) Person *editingPerson;
As far as that contact-editing view controller is concerned, the editingPerson is the root of the object graph in this application. It can’t see any higher than that, nor should it.
Of course, this all becomes a bit more fuzzy when you start talking about fetching things from web servers and the filesystem and when to save, but that is something I will talk about in another post soon.
Published July 16, 2013
Tags:ios data flow