As a courtesy, this is a full free rendering of my book, Programming iOS 6, by Matt Neuburg. Copyright 2013 Matt Neuburg. Please note that this edition is outdated; the current books are iOS 13 Programming Fundamentals with Swift and Programming iOS 13. If my work has been of help to you, please consider purchasing one or both of them, or you can reward me through PayPal at http://www.paypal.me/mattneub. Thank you!

Chapter 13. Data Communication

As soon as an app grows to more than a few objects, puzzling questions can arise about how to send a message or communicate data between one object and another. The problem is essentially one of architecture. Constructing your code so that all the pieces fit together and key information can be shared is something of an art. But it isn’t difficult. This chapter presents some general considerations that may provide the needed clue.

Model–View–Controller

In Apple’s documentation and elsewhere, you’ll find references to the term model–view–controller, or MVC. This refers to an architectural goal of maintaining a distinction between three functional aspects of a program that displays information to the user and permits the user to alter that information. The notion goes back to the days of Smalltalk, and much has been written about it since then, but informally, here’s what the terms mean:

Model
The data and its management, often referred to as the program’s “business logic,” the hard-core stuff that the program is really all about.
View
What the user sees and interacts with.
Controller
The mediation between the model and the view.

Consider, for example, a game where the current score is displayed to the user:

  • A UILabel that shows the user the current score for the game in progress is view; it is effectively nothing but a pixel-maker, and its business is to know how to draw itself. The knowledge of what it should draw — the score, and the fact that this is a score — lies elsewhere. A rookie programmer might try to use the score displayed by the UILabel as the actual score: to increment the score, read the UILabel’s string, turn that string into a number, increment the number, turn the number back into a string, and present that string in place of the previous string. That is a gross violation of the MVC philosophy. The view presented to the user should reflect the score; it should not store the score.
  • The score is data being maintained internally; it is model. It could be as simple as an instance variable along with a public increment method or as complicated as a Score object with a raft of methods. The score is numeric, whereas a UILabel displays a string; this alone is enough to show that the view and the model are naturally different.
  • Telling the score when to change, and seeing that this fact is reflected in the user interface, is the work of the controller. This will be particularly clear if we imagine that the model’s numeric score needs to be transformed in some way for presentation to the user. For example, suppose the UILabel that presents the score reads: “Your current score is 20”. The model is presumably storing and providing the number 20, so what’s the source of the phrase “Your current score is…”? Whoever is deciding that this phrase should precede the score in the presentation of the score to the user, and is making it so, is a controller.
figs/pios_1301.png

Figure 13.1. Model–view–controller


Even this simplistic example (Figure 13.1) illustrates very well the advantages of MVC. By separating powers in this way, we allow the aspects of the program to evolve with a great degree of independence. Do you want a different font and size in the presentation of the score? Change the view; the model and controller need know nothing about it, but will just go on working exactly as they did before. Do you want to change the phrase that precedes the score? Change the controller; the model and view are unchanged.

Adherence to MVC is particularly appropriate in a Cocoa app, because Cocoa itself adheres to it. The very names of Cocoa classes reveal the MVC philosophy that underlies them. A UIView is a view. A UIViewController is a controller; its purpose is to embody the logic that tells the view what to display. In Chapter 11 we saw that a UIPickerView does not hold the data it displays; it gets that data from a data source. So the UIPickerView is a view; the data source is model.

A further distinction, found in Apple’s documentation, is this: true model material and true view material should be quite reusable, in the sense that they can be transferred wholesale into some other app; controller material is generally not reusable, because it is concerned with how this app mediates between the model and the view.

In one of my own apps, for example, we download an XML (RSS) news feed and present the article titles to the user as a table. The storage and parsing of the XML are pure model material, and are so reusable that I didn’t even write this part of the code (I used some code called FeedParser, by Kevin Ballard). The table is a UITableView, which is obviously reusable, seeing as I obtained it directly from Cocoa. But when the UITableView turns to me and asks what I’d like to display in this cell, and I turn to the XML and ask for the title of the article corresponding to this row of the table, that’s controller logic.

By keeping the MVC architectural philosophy in mind as you develop your app, you’ll implicitly solve one data communication problem. The data will live in the model, the view will be purely presentational in nature, and the communication between them will be handled by your own deliberately written controller code. You’ll be communicating between the view and the model because controller code is about communicating between the view and model.

Instance Visibility

The problem of communication often comes down to one object being able to see another: object Manny needs to be able to find object Jack repeatedly and reliably over the long term so as to be able to send Jack messages. (This is the same problem I spoke of in Chapter 2 as getting a reference to an object.)

An obvious solution is an instance variable of Manny whose value is Jack. This is appropriate particularly when Manny and Jack share certain responsibilities or supplement one another’s functionality, and when they will both persist, especially when they will both persist together. The application object and its delegate, a table view and its data source, a view controller and the view that it controls — these are cases where the former must have an instance variable pointing at the latter.

With instance variables comes the question of memory management policy (Chapter 12). Should Manny, which has an instance variable pointing to Jack, also retain Jack? Basically, it depends on how closely allied the objects are. An object does not typically retain its delegate or its data source; it can exist without a delegate or a data source, and the delegate and data source have lives of their own — it is none of this object’s business to say whether its delegate or data source should be allowed to go out of existence. This object is therefore always prepared for the possibility that its delegate or data source may be nil. Similarly, an object that implements the target–action pattern, such as a UIControl, does not retain its target. On the other hand, a view controller is useless without a view to control; its very job is to be coterminous with its view, and to release its view when it itself goes out of existence. Similarly, an object that owns a nib as the nib loads rules the lifetimes of that nib’s top-level objects.

Even when two objects go together closely, it will not necessarily be the case that each holds an instance variable pointing at the other. When each does point to the other, you must of course be careful not to let each retain the other; that’s a retain cycle, and will cause both objects to leak. But if one object is the instigator of communication between the two, it can simply pass along a reference to itself as a method argument, if it thinks the second object might need this in order to send back a message later on.

This behavior is conventional in a delegate message, for example. The parameter of the delegate message textFieldShouldBeginEditing: is a reference to the UITextField that sent the message. The same policy is followed by target–action messages in their fuller forms; the first parameter is a reference to the sender. You can follow a similar policy.

Visibility by Instantiation

The real question is how one object is to be introduced to the other in the first place. Much of the art of Cocoa programming (and of object-oriented programming generally) lies in getting a reference to a desired object. Every case is different and must be solved separately, but a major clue comes from the fact that every instance comes from somewhere. This means that some object commanded this instance to come into existence in the first place. That object therefore has a reference to the instance at that moment. That fact is always the starting point for establishment of future communication.

When Manny instantiates Jack, Manny has a reference to Jack, and can keep that reference if it will be needed later. Moreover, if Manny knows that Jack is going to need a reference to itself (Manny) or to some piece of data, Manny can hand Jack that reference early in Jack’s lifetime. Indeed, you might write Jack with an initializer that will take this reference as a parameter, so that Jack will possess it from the moment it comes into existence. (Compare the approach taken, for example, by UIActionSheet and UIAlertView, where the delegate is one of the initializer’s parameters, or by UIBarButtonItem, where the target is one of the initializer’s parameters.)

This example, from one of my apps, is from a table view controller. The user has tapped a row of the table. We create a secondary table view controller, a TrackViewController instance, handing it the data it will need, and display the secondary table view. I deliberately devised TrackViewController to have a designated initializer initWithMediaItemCollection: to make it virtually obligatory for a TrackViewController to have access to the data it needs:

- (void)showItemsForRow: (NSIndexPath*) indexPath {
    // create subtable of tracks and go there
    TrackViewController *t =
        [[TrackViewController alloc] initWithMediaItemCollection:
            (self.albums)[indexPath.row]];
    [self.navigationController pushViewController:t animated:YES];
}

The loading of a nib is also a case of visibility by instantiation. As I explained at length in Chapter 7, you will want to prepare your nib objects, including some proxy object such as the File’s Owner, with outlets corresponding to instance variables or accessors in that object’s class; as the nib loads, the object pointed to by each outlet will be handed to the corresponding instance variable as its value, or the setter as its argument, so that each instance that will need a reference to an object instantiated from the nib will in fact now have it and will be able to communicate as needed with the nib-instantiated object (Figure 7.5).

But what if two objects are conceptually distant from each other? A common case in point is when objects are going to be instantiated from different nibs. How can an instance from one nib get a reference to an instance from another nib? True, you can’t draw a connection between an object in nib A and an object in nib B (see Connections Between Nibs). But someone (Manny) is going to be the file’s owner when nib A loads, and someone (Jack) is going to be the file’s owner when nib B loads. Those two file’s owners might be able to see each other; if so, the problem is solved. Perhaps they are the same object. Perhaps Manny instantiated Jack in the first place. Perhaps they are both instantiated by some third object, which provides a communication path for them.

Visibility by Relationship

Objects may acquire the ability to see one another automatically by virtue of their position in a containing structure. Before worrying about how to supply one object with a reference to another, consider whether there may already be a chain of references leading from one to another.

For example, a subview can see its superview, through its superview property. A superview can see all its subviews, through its subviews property, and can pick out a specific subview through that subview’s tag property, by calling viewWithTag:. A subview in a window can see its window, through its window property. (There will be more about all that in Chapter 14.) A responder (Chapter 11) can see the next responder in the responder chain, through the nextResponder method — which also means, because of the structure of the responder chain, that a view can see the view controller that manages it.

Similarly, view controllers are part of a containment hierarchy (Chapter 19) and therefore can see one another. If a view controller is currently presenting a view through a second view controller, the latter is the former’s presentedViewController, and the former is the latter’s presentingViewController. If a view controller is contained by a UINavigationController, the latter is its navigationController. A UINavigationController’s visible view is controlled by its visibleViewController. And from any of these, you can reach the view controller’s view through its view property, and so forth.

All of these relationships are public. So if you can get a reference to just one object within any of these structures or a similar structure, you can effectively navigate the whole structure through a chain of references and lay your hands on any other object within the structure.

Global Visibility

Some objects are globally visible (that is, visible to all other objects). In general, these are singletons vended by a class method. Some of these objects have properties pointing to other objects, making those other objects likewise globally visible.

For example, any object can see the singleton UIApplication instance by calling [UIApplication sharedApplication]. So any object can also see the app’s primary window, because that is its keyWindow property, and any object can see the app delegate, because that is its delegate property. And the chain continues: any object can see the app’s root view controller, because that is the primary window’s rootViewController — and from there, as I said in the previous section, we can navigate the view controller hierarchy and the view hierarchy. You can take advantage of this to make objects globally visible by attaching them to a globally visible object. A public property of the app delegate, which you are free to create, is globally visible by virtue of the app delegate being globally visible (by virtue of the shared application being globally visible).

Another globally visible object is the shared defaults object obtained by calling [NSUserDefaults standardUserDefaults]. This object is the gateway to storage and retrieval of user defaults, which is similar to a dictionary (a collection of values named by keys). The user defaults are automatically saved when your application quits and are automatically available when your application is launched again later, so they are one of the ways in which your app maintains state between launches. But, being globally visible, they are also a conduit for communicating values within your app.

For example, in one of my apps there’s a setting I call @"hazyStripy". This determines whether a certain visible interface object is drawn with a hazy fill or a stripy fill. This is a setting that the user can change, so there is a preferences interface allowing the user to make this change. When the user displays this preferences interface, I examine the @"hazyStripy" setting in the user defaults to configure the interface to reflect it; if the user interacts with the preferences interface to change the @"hazyStripy" setting, I respond by changing the actual @"hazyStripy" setting in the user defaults.

But the preferences interface is not the only object that uses the @"hazyStripy" setting in the user defaults; the drawing code that actually draws the hazy-or-stripy-filled object also uses it, so as to know which way to draw itself. Thus there is no need for the object that draws the hazy-or-stripy-filled object and the object that manages the preferences interface to be able to see one another! They can both see this common object, the @"hazyStripy" user default (Figure 13.2). Indeed, it is not uncommon to “misuse” the user defaults storage to hold information that is not used to maintain state between runs of the app, but is placed there just because this is a location globally visible to all objects.

figs/pios_1302.png

Figure 13.2. The global visibility of user defaults


Notifications

Notifications (Chapter 11) can be a way to communicate between objects that are conceptually distant from one another without bothering to provide any way for one to see the other. All they really need to have in common is a knowledge of the name of the notification. Using a notification in this way may seem lazy, an evasion of your responsibility to architect your objects sensibly. But sometimes one object doesn’t need to know, and indeed shouldn’t know, what object (or objects) it is sending a message to.

For example, in one of my apps, the app delegate may detect a need to tear down the interface and built it back up again from scratch. If this is to happen without causing memory leaks (and all sorts of other havoc), every view controller that is currently running a repeating NSTimer needs to invalidate that timer (Chapter 12). Rather than my having to work out what view controllers those might be, and endowing every view controller with a method that can be called, I simply have the app delegate shout “Everybody stop timers!” — by posting a notification. My view controllers that run timers have all registered for this notification, and they know what to do when they receive it.

Key–Value Observing

Key–value observing, or KVO, is a mechanism somewhat similar to the target–action mechanism, except that it is not limited to controls. (The KVO mechanism is provided through an informal protocol, NSKeyValueObserving, which is actually a set of categories on NSObject and other classes.) The similarity is that objects register with a particular object to be notified when something happens. The “something” is that a certain value in that object is changed.

Mac OS X Programmer Alert

Mac OS X bindings don’t exist on iOS, but you can sometimes use KVO to achieve similar aims.

KVO can be broken down into three stages:

Registration
To hear about a change in a value belonging to object A, object B must be registered with object A.
Change
The change takes place in the value belonging to object A, and it must take place in a special way — a KVO compliant way.
Notification
Object B is automatically notified that the value in object A has changed and can respond as desired.

Here’s a simple complete example — a rather silly example, but sufficient to demonstrate the KVO mechanism in action. We have a class MyClass1; this will be the class of object A. We also have a class MyClass2; this will be the class of object B. Finally, we have code that creates a MyClass1 instance called objectA and a MyClass2 instance called objectB; this code registers objectB to hear about changes in an instance variable of objectA called value, and then changes value, and sure enough, objectB is automatically notified of the change:

// MyClass1.h:
@interface MyClass1 : NSObject
@property (nonatomic, copy) NSString* value;
@end

// MyClass2.m:
- (void) observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                        context:(void *)context {
    NSLog(@"I heard about the change!");
}

// Somewhere else entirely:
MyClass1* objectA = [MyClass1 new];
MyClass2* objectB = [MyClass2 new];
// register for KVO
[objectA addObserver:objectB forKeyPath:@"value" options:0 context:nil]; ❶
// change the value in a KVO compliant way
objectA.value = @"Hello, world!"; ❷
// result: objectB's observeValueForKeyPath:... is called 

We call addObserver:forKeyPath:options:context: to register objectB to hear about changes in objectA’s value. We didn’t use the options: or context: parameters for anything; I’ll talk about the options: parameter in a moment. (The context: parameter is for handing in a value to be provided as part of the notification; see Chapter 12.)

We change objectA’s value, and we do it in a KVO compliant way, namely, by passing through the setter (because setting a property is equivalent to passing through the setter). This is another reason why, as I said in Chapter 12, accessors (and properties) are a good thing: they help you guarantee KVO compliance when changing a value.

When we change objectA’s value, the third stage takes place automatically: a call is made to objectB’s observeValueForKeyPath:.... We have implemented this method in MyClass2 in order to receive the notification. In this simple example, we expect to receive only one notification, so we just log to indicate that we did indeed receive it. In real life, where a single object might be registered to receive more than one KVO notification, you’d use the incoming parameters to distinguish between different notifications and decide what to do.

At the very least, you’ll probably want to know, when observeValueForKeyPath:... is called, what the new value is. We can find that out easily, because we are handed a reference to the object that changed, along with the key path for the value within that object. Thus we can use KVC to query the changed object in the most general way:

- (void) observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                        context:(void *)context {
    id newValue = [object valueForKeyPath:keyPath];
    NSLog(@"The key path %@ changed to %@", keyPath, newValue);
}

It is also possible to request that the new value be included as part of the notification. This depends upon the options passed with the original registration. Here, we’ll request that both the old and new values be included with the notification:

objectA.value = @"Hello";
[objectA addObserver:objectB forKeyPath:@"value"
    options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
    context: nil];
objectA.value = @"Goodbye"; // notification is triggered

When we receive the notification, we fetch the old and new values out of the change dictionary:

- (void) observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                        context:(void *)context {
    id newValue = change[NSKeyValueChangeNewKey];
    id oldValue = change[NSKeyValueChangeOldKey];
    NSLog(@"The key path %@ changed from %@ to %@",
          keyPath, oldValue, newValue);
}

No memory management happens as part of the registration process, so it is incumbent upon you to unregister object B before it is destroyed. Otherwise, object A may later attempt to send a notification to a dangling pointer (see Chapter 12). This is done by sending object A the removeObserver:forKeyPath: message; you must explicitly unregister the observer for every key path for which it is registered; you can’t use nil as the second argument to mean “all key paths”.

Warning

The runtime will send you a nice warning in the log if an object being observed under KVO goes out of existence. But you get no warning if an observer object goes out of existence. It is crucial that the observer should be unregistered with the observed as the observer goes out of existence, to prevent the observed from trying to send it any notifications later. In real life, where an object will typically register itself as an observer, it will usually unregister itself in its dealloc implementation (and this could be yet another reason to implement dealloc under ARC).

Beginners are often confused about how to use KVO to observe changes to a mutable array, to be notified when an object is added to, removed from, or replaced within the array. You can’t add an observer to an array itself; you have to observe through an object that has a key path to the array (through accessors, for example). The simple-minded solution is then to access the array using mutableArrayValueForKey:, which provides an observable proxy object.

For example, as in Chapter 12, let’s posit an object with a property theData which is an array of dictionaries:

(
    {
        description = "The one with glasses.";
        name = Manny;
    },
    {
        description = "Looks a little like Governor Dewey.";
        name = Moe;
    },
    {
        description = "The one without a mustache.";
        name = Jack;
    }
)

Suppose this is an NSMutableArray. Then we can register with our object to observe the key path @"theData":

[objectA addObserver:objectB forKeyPath:@"theData" options:0 context:nil];

Now object B will be notified of changes to this mutable array, but only if those changes are performed through the mutableArrayValueForKey: proxy object:

[[objectA mutableArrayValueForKeyPath:@"theData"] removeObjectAtIndex:0];
// notification is triggered

But it seems onerous to require clients to know that they must call mutableArrayValueForKey:. The simple solution is for our object A itself to provide a getter that calls mutableArrayValueForKey:. Here’s a possible implementation:

// MyClass1.h:
@interface MyClass1 : NSObject
@property (nonatomic, strong, getter=theDataGetter) NSMutableArray* theData;
@end

// MyClass1.m:
- (NSMutableArray*) theDataGetter {
    return [self mutableArrayValueForKey:@"theData"];
}

The result is that, as far as any client knows, this object has a key @"theData" and a property theData, and we can register to observe with the key and then access the mutable array through the property:

[objectA addObserver:objectB forKeyPath:@"theData"
    options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
    context:nil];
[objectA.theData removeObjectAtIndex:0]; // notification is triggered

If you’re going to take this approach, you should really also implement (in MyClass1) the four KVC compliance methods for a mutable array façade (see Chapter 12). Although things will appear to work just fine without them, and although they appear trivial (they are merely delegating to self->_theData the equivalent calls), they will be called by the vended proxy object, which increases its efficiency (and, some would argue, its safety). Without these methods, the proxy object resorts to setting the instance variable directly, replacing the entire mutable array, every time a client changes the mutable array:

- (NSUInteger) countOfTheData {
    return [self->_theData count];
}

- (id) objectInTheDataAtIndex: (NSUInteger) ix {
    return self->_theData[ix];
}

- (void) insertObject: (id) val inTheDataAtIndex: (NSUInteger) ix {
    [self->_theData insertObject:val atIndex:ix];
}

- (void) removeObjectFromTheDataAtIndex: (NSUInteger) ix {
    [self->_theData removeObjectAtIndex: ix];
}

If what you want to observe are mutations within an individual element of an array, things are more complicated. Suppose our array of dictionaries is an array of mutable dictionaries. To observe changes to the value of the @"description" key of any dictionary in the array, you’d need to register for that key with each dictionary in the array, separately. You can do that efficiently with NSArray’s instance method addObserver:toObjectsAtIndexes:forKeyPath:options:context:, but if the array itself is mutable then you’re also going to have to register for that key with any new dictionaries that are subsequently added to the array (and unregister when a dictionary is removed from the array). This is doable but daunting, and I’m not going to go into the details here.

Key–value observing is a deep mechanism; consult Apple’s Key-Value Observing Guide for full information. It does have some unfortunate shortcomings — for one thing, it’s a pity that all notifications arrive by calling the same bottleneck method, observeValueForKeyPath:... — but in general, KVO is useful for keeping values coordinated in different objects.

Warning

The properties of Apple’s built-in classes are typically KVO compliant. Indeed, so are many classes that don’t use properties per se; for example, NSUserDefaults is KVO compliant. Unfortunately, Apple warns that undocumented KVO compliance can’t necessarily be counted on.