This is how we implemented metrics in recent apps I’ve worked on:
Right before the app shipped we’d go thru the app and anywhere something interesting happened, we’d add a metric by naming it and inserting a line of code to trigger the metric. Some header file would contain all the metric names.
Later after the app shipped we’d need to answer a question like: How many of our users are using a specific feature? To answer this, we’d open up the header file, find the right metric name. Often times the names are confusing and we’d dig into the code. With the metric name in hand, we’d then figure out how to lookup the metric data using the web interface of the metrics provider.
The whole process is clunky. Peppering the app with metrics calls is a laborious task. Inevitably we’d miss adding a metric call somewhere and our metrics would be blind to that area. I also never liked the web interface of any of the metrics providers. No matter what their marketing copy said, everything felt like web metrics bolted onto mobile apps.
When I started work on my latest app, I decided to rethink this clunky metrics approach. Here is the solution I ended up with:
- Use Keen.io as the backend provider of metrics
Keen’s tagline is “Analytics Backend as a Service”. They provide three basic parts:- A simple set of Data Collection APIs to record metrics with libraries for various platforms, including iOS.
- Data Analysis APIs to query the stored data.
- Data Visualization APIs to create charts and UI widgets from your data.
- Write a simple iOS library that auto handles most of the interesting iOS metrics
When you really think about it, how we interact with apps is pretty standard. Apps are launched, we navigate in and out of views and on those views we tap and touch things on the screen.
By thinking generically about these things and how to automatically record them, we can skip the entire laborious process of instrumenting an app for metrics.
PBMetrics
The PBMetrics library has two basic parts. The first is PBMetricsManager which handles the basics of calling the Keen iOS SDK to report metrics. It also handles the basic concept of a session.
Sessions
Here’s how PBMetrics handles sessions:
When the app comes to the foreground, the current time is saved locally as the start of the session.
When the app goes to the background, the current time is used as the end time and a metric session is sent to the server with the start time, end time and session duration.
Standard attributes
One of the nice things about Keen is that a metric/event is simply a JSON dictionary. What goes in that dictionary is up to you.
The Keen iOS library creates a “keen” entry with a timestamp, created_at and optionally a location attribute.
PBMetrics adds a “standard_properties” entry with lots of standard stuff about the device, the app and other global information
Here’s an example of an app session metric:
{ "keen": { "created_at": "2013-06-04T21:19:08.026000+00:00", "location": { "coordinates": [ -122.4183, 37.775 ] }, "timestamp": "2013-06-04T21:19:05+00:00" }, "session_duration": 23.580490946769714, "session_end": "2013-06-04T14:19:05-0700", "session_start": "2013-06-04T14:18:41-0700", "standard_properties": { "app_build_number": "1.0", "app_version": "1.0", "device": "x86_64", "device_name": "Simulator", "enabled_remote_notification_types": [], "first_time_app_launch": "1", "jb": "0", "language": "en", "location_services_enabled": "1", "logged_in": "1", "logged_in_user": { "email": "foo@foo.com", "name": "User" }, "model": "iPhone Simulator", "screen_scale": 2, "system": "iPhone OS", "system_version": "6.1" } }
Base Classes
The second part of the library is a set of base classes that record basic events.
For example a base class for UIViewController records when the view controller is on screen. A base class for a button records a metric when it is tapped. A base table view controller class records a metric when a cell is tapped.
Here is a metric recorded for a view controller appearing on screen:
{ "appear_time": "2013-06-04T14:18:39-0700", "appearance_duration": 6.953963994979858, "appeared_animated": false, "class_name": "FirstViewController", "disappear_time": "2013-06-04T14:18:46-0700", "disappeared_animated": false, "is_being_dismissed": false, "is_being_presented": false, "is_moving_from_parent_view_controller": false, "is_moving_to_parent_view_controller": false, "keen": { "created_at": "2013-06-04T21:19:08.033000+00:00", "location": { "coordinates": [ -122.4183, 37.775 ] }, "timestamp": "2013-06-04T21:18:39+00:00" }, "standard_properties": { "app_build_number": "1.0", "app_version": "1.0", "device": "x86_64", "device_name": "Simulator", "enabled_remote_notification_types": [], "first_time_app_launch": "1", "jb": "0", "language": "en", "location_services_enabled": "1", "logged_in": "1", "logged_in_user": { "email": "foo@foo.com", "name": "User" }, "model": "iPhone Simulator", "screen_scale": 2, "system": "iPhone OS", "system_version": "6.1" }, "was_popped": false, "was_pushed": false } }
Here’s a metric when a tab bar item is selected:
{ "keen": { "created_at": "2013-06-04T21:19:07.873000+00:00", "location": { "coordinates": [ -122.4183, 37.775 ] }, "timestamp": "2013-06-04T21:18:49+00:00" }, "standard_properties": { "app_build_number": "1.0", "app_version": "1.0", "device": "x86_64", "device_name": "Simulator", "enabled_remote_notification_types": [], "first_time_app_launch": "1", "jb": "0", "language": "en", "location_services_enabled": "1", "logged_in": "1", "logged_in_user": { "email": "foo@foo.com", "name": "User" }, "model": "iPhone Simulator", "screen_scale": 2, "system": "iPhone OS", "system_version": "6.1" }, "tab_bar_selected": "FirstViewController" }
Take a look at the PBMetrics source on github and let me know what you think:
https://github.com/boctor/pbmetrics