When working with Objective C you may come across a situation where using a placeholder object can save you complexity when an actual instance is not available or required. By using protocols you can retain type-safety while defining objects that behave like instances of other classes.
Just Picture It
Imagine we're building an app that allows your to filter items based on tags. This app has two views: The main view displays a list of items. The second view displays a list of tags. When a tag is selected by the user the second view controller passes the selected tag back to the first view controller (its delegate) which uses that object to filter the list of items. If the Tag returned is
nil we don't filter the list at all. Assuming we're using Core Data, we probably have a managed object subclass for both Items and Tags and each of our two view controllers use a fetch request to fetch an array of objects to display.
It's Never That Simple
This sort of setup should be very familiar to anyone who's spent some time in UIKit. But what if we want the user to be able to filter the list to show only objects that have no tags at all? The initial delegate method we defined for when a user selects a tag might looks something like this:
- (void)userDidSelectTag:(Tag *)aTag;
We could change the return type to
id and return a string, or an arbitrary
NSNumber in this case but that would require using some sort of informal (and hard to enforce) protocol.
Instead, let's define a "Tag" protocol that both our managed objects and our placeholder object can conform to. By defining a protocol we can safely let our delegate method accept any of these objects.
- (void)userDidSelectTag:(NSObject <Tag> *)tagLikeObject;
Now when the user selects the "Untagged" row in our app we just return a singleton object that conforms to the Tag protocol and implements any methods it needs to behave like a tag in this situation.
[self.delegate userDidSelectTag:[Untagged sharedInstance]];
An Exercise for the Reader
I've left out the actual design and implementation of the singleton class but the important part is that it can fill in for the model object and we can still be fairly strict about what our delegate method is allowed to receive. This post was inspired by how I ended up implementing 'Uncategorized' places in the new version of Rego but I can this technique being useful in other situations. For instance (no pun intended), where you need default filters or even sample data.