More Cohesive iOS code using an Observer

The Problem

When writing an iOS app it’s pretty easy to end up with some rather large view controllers that do a lot of stuff. There are lots of iOS tutorials, templates and code samples out there that demonstrate a particular piece of functionality or API and they tend to put all the code in the one controller class.

That’s cool for a sample or a starting point but in the real world as you add functionality to your app you can end up with an enormous class that does everything except make you a cup of tea in the morning and implements 57 delegates. In short you end up with controllers that have low cohesion.

On top of this the whole delegate idea doesn’t make it that easy to reuse code between controllers. It’s pretty common for two views to display the same data in the same or slightly different ways. So you either live with some duplication in your code or try moving it to a common class, which can be messy.

While this certainly doesn’t apply to all kinds of apps the ones that I’ve written have a certain style. Usually data is being provided from and external source such as a web site, web service, ad server or push notification…etc. and / or internally from the phone such as core data, location services, user defaults, phone settings and the address book. This data is then displayed in the app in various views.

Enter the Observer

The best way I’ve found to get around these problems is to introduce a pattern called the Observer (I’ve also heard it called document/view in the Microsoft world). While patterns can be abused this is one that I’ve used extensively in the past and can be quite useful.

Rather than going into the detail of the pattern I’d rather talk about how to apply it, if you need some more information check out http://en.wikipedia.org/wiki/Observer_pattern

So now how do we apply the Observer to our iOS app?

So I’ve mentioned that a lot of apps obtain their data from a number of internal and external sources in pattern speak this is the Subject and then as it’s data changes it would notify the views that need to be updated these are the Observers in the pattern.

So we could go ahead and create Subject classes for each data type and hook them up with an Observer delegate that would get called back when the data changes. While that is an improvement in itself but there is a better way.

Add Some Messaging

The Observer patterns major power is being able to handle multiple Observers for a single Subject and while we could write code to allow registering of multiple delegates and then call them all back iOS provides a mechanism for registering and notifying multiple objects at once namely using the Notification Center.

So what does an app look like when following this pattern?

So for each of the data sources regardless of where it comes from we have a Subject class which holds the current data values to be displayed. When a view is displayed it simply displays the values for the subjects that it cares about. The view should also register for notifications for when the Subject data changes so it can update itself.

But what does this notification?

In the GoF pattern the subject updates itself, but I’ve split this functionality out so that the Subject only holds the data we want and it has an associated Updater (for want of a better name), which is responsible for updating the subject with fresh data.

While the Subject and Observer classes for different data sources are very similar the Updater may not be, for example one Updater may call a web service to get sports scores externally and be triggered manually while another may use the location services to get the phones current location and happen automatically.

One note about Updaters, they may take a long time to complete getting a large amount of data from an external source over a slow connection. So typically I don’t do this update on the main thread but create an NSOperation to do this operation and put it on a queue (I may post more about this at some point). This ensures that the UI of you app remains smooth while doing updates and also allows you to handle multiple update requests for the same data

Wiring it all up

While it can depend on the type of app being developed I typically create the Subject and Updater classes when the app is started. Then as views are displayed they will get data from the Subject and display it and register for any updates they need. When the views are no longer displayed all they need to do is remember to unregister.

For example, if you have an app that gets the phone’s location from location services and weather forecasts using a web service from a weather bureau the structure would look like.

In this example there is one view (observer) that displays the location and one that displays the weather and one that displays both. As the location is updated the LocationUpdater class updates the data in the LocationSubject and send notifications via the notification center to the views that need updating. Similarly for the weather but the WeatherUpdater would get data from the external web service (in the background using NSOperation).

Advantages

Hopefully it is reasonably obvious that the code using this style would be more cohesive as each of the class types have clear responsibilities

  • Subject: holds the data that we wish to display in the app
  • Observer: there can be multiple of these and they display the data (typically view controllers in IOS)
  • Updater: fetches data and updates the subject with new values and notifies the Observers that have registered for updates.

Naturally the view controllers (Observers) should be much smaller now and only do what they should i.e. display data from the subject(s) and then do an update when notified.

Finally as the update of data doesn’t take place on the main thread and the views get updated via notifications the UI should be very smooth when updating data and scrolling unlike some clunky apps out there (I’m looking at you Facebook!). It will even handle refreshing from one view and then switching to another.

Advertisements