Managing tracking code in iOS using delegation pattern

Kunal Balani posted January 30, 2017

Tracking is ubiquitous in mobile engineering. At TripAdvisor we measure the success metric associated with every feature. This also means that a good percentage of our code base consists of tracking related code. Most of the tracking changes are usually embedded in UIViewController subclass, making it rigid and difficult to test. The article introduces handling and managing, tracking changes using delegation. Let’s take an example of how a tracking code within the app looks like

Similarly, you might have such a call at multiple places. Most of the tracking events are normally queued and dispatched together in buckets for performance reasons. We use a module called dispatcher which takes care of this. Before dispatching we also add several devices and session related information with every instance of tracking call, these are mainly for bookkeeping reasons and to compute certain product metrics.

Problems with tracking code  

  • Dependency on UIViewController : It’s hard to isolate tracking calls from UIVIewController. Most of the tracking calls are properties of UIViewController like page view events. This assumes one to one correspondence between a visible screen and UIViewController. However UIViewController in certain instance, can be a container rather than a screen like UINavigationController or UITabBarController.
  • Too much tracking code : The default place for tracking in Apple’s MVC looks like a UIViewController. This means as the tracking code grows the size of UIViewController grows too. Here is one example where we do multiple tracking calls associated with a single event.
  • Testing : It’s hard to test tracking code in general. The primary reason is that it’s embedded in a UIViewController and not really an isolated module. In the past we have used FLEX to sniff network packets using run time API’s to validate tracking payloads. This isn’t of course the ideal way, but has served us well in the past

  

 Tracking Delegate 

 We proposed adding a new delegate to UIViewController shown as below. UIViewController subclass own a tracking delegate. All tracking calls are redirected to this delegate.

We started with creating a protocol TrackingDelegate. The interface mainly depends what type of logs do you track. At TripAdvisor we track page view, user events, background events and so on. This protocol has a concrete implementation TrackerUIViewController contains this tracker and delegates all the assigned tasks to this delegate.

Tracking Delegate to the rescue

  • Dependency on UIViewController : Since we have tracking code isolated in a delegate  we don’t need to subclass from any view controller as done in GAITrackedViewController.
    All a view controller needs is an assigned tracking delegate.
  • UIViewController Size :  There are plenty of instances where we have to log multiple tracking calls from a single event. Providing a tracking delegate means that all those events could be handled by tracking delegate instead of view controller eliminating the needs for knowing anything about tracking client.

    We can easily see in the above example, how tracking can easily grow in size over the time if not isolated from its controller.

  • Testing : Testing, tracking code is very tricky. Now that we have got all the tracking code isolated in a delegate. We just need to need to substitute TrackingDelegate with a test class instead of Tracker. This class helps to mock and collect all the tracking calls.

Summary

Using a tracking delegate might be an overkill for small application, but isolating tracking code in big application has huge payoffs. It helps to manage tracking code by removing all the tracking calls which mainly live in a UIViewController and gives it a dedicated place to live in. There might be other ways to get the same results, but since delegation is a native ubiquitous pattern in iOS this might be the easiest solution for other developers who can start using it without having any explanations.

Leave a Reply

Your email address will not be published. Required fields are marked *