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

Location Services Settings Not Deleted With App

I was working away on my iOS app today and I noticed a bug that only appeared to happen on my phone (the simulator was fine). What was happening was that when a MKMapView stopped tracking the user’s location the GPS was staying on. In fact I had to uninstall the app for it to switch off.

Now I was pretty sure that this had been working in the past but I’d just upgraded to iOS 5.1 so perhaps this was a bug? I quickly slapped together a test app with a map view that toggled tracking a user and the GPS switched off and on when expected.

My next idea was to start commented out big chunks of my apps code to see if this fixed things. It didn’t and I had got to the point where there was little different code left. Still the GPS stayed on…

As this problem only occurred on the phone I started to think that the phone was keeping some location services state around so I tried deleting and reinstalling the app, restarting the phone and various combinations of this and it still didn’t work. To further test my theory that the phone was keeping some location services state I renamed my app (bundle name to be precise) and voilà! it started working.

Now I wasn’t keen to have to rename my app to fix this problem so I did a little searching and found that you can reset all location services data from your phone via:

General -> Reset -> Reset Location Warnings

This fixed the problem, my app’s location services state must’ve got corrupted somehow (I still haven’t worked out how) and deleting/reinstalling the app wasn’t clearing it.

Of course this wipes the location settings for ALL apps so you’ll get a prompt to use location services the next time you open other apps, but it beats re-naming the app.

Region Monitoring in iOS

(or how turning off Wi-Fi broke my app)

I’m currently in the process of writing an app for iOS that uses the new(ish) region monitoring API introduced in iOS 4.

Initially I wrote a small ‘spike’ app to test the API and ensure that it would do what I wanted and to check how reliable it was I was pleasantly surprised. After I was convinced that it could do what I needed, I started to test its accuracy and how battery life was affected. Here are a few things I found:

Don’t rely on using the simulator when it comes to location and map kit testing there are a few bugs that only appear in the simulator such as, maps not tracking correctly, random location failures and the location icon appearing in the status bar even though the GPS is not running. Going for a walk or getting on a bus is the best way to test.

When monitoring regions the GPS is not switched on it only relies on cell tower and Wi-Fi information to get your location. This is despite the location arrow being displayed in the status bar. I tested this by running the energy diagnostic instrument in Xcode to see when the GPS was enabled in my app and it never was.

This was further confirmed when I was testing my app and suddenly I stopped getting region updates altogether. What had happened was that I had turned off Wi-Fi on my phone which meant that region monitoring fell back to mobile cell towers and because I had never moved outside of a cell area no update ever occurred.

Further testing of location accuracy (using startMonitoringSignificantLocationChanges in CLLocationManager) I found that the accuracy of your location via a cell tower could be up to 1.2 km out which is quite poor.

So my conclusion is that monitoring regions in iOS is not a drain on the device’s battery as the GPS is not used however with Wi-Fi disabled the accuracy of detecting the entering and exiting of regions will be quite poor as it only relies on cell tower data.