Skip to content

Instantly share code, notes, and snippets.

@robinkunde
Last active September 6, 2017 16:56
Show Gist options
  • Save robinkunde/b708d4980bc9359d1b4b29d6dc9b3caa to your computer and use it in GitHub Desktop.
Save robinkunde/b708d4980bc9359d1b4b29d6dc9b3caa to your computer and use it in GitHub Desktop.

When it comes to picking an analytics package for your mobile application, you have plenty of candidates to choose from: Google Firebase, Fabric, Hockeyapp, to just name a few.

These solutions vary in pricing and capabilities. However, the most important consideration for many clients is interoperability with existing analytics or business intelligence packages. For this reason, we often get requests to integrate the Google Analytics SDK into iOS and Android apps. If a mobile app’s structure hews closely to an existing website, the ability to use the same analytics suite for both allows us to easily unify reporting.

Google recently reorganized their Cocoapods offerings, moving components like their analytics package back into its own pod and deprecating the Google pod in the process.

It would have been a good time to redo their integration docs as well, but unfortunately, they are still outdated and incomplete.

I'd like to quickly go over how I incorporate Google Analyics into iOS apps.

Setup:

First, add the tracking ID to your info.plist.

A screenshot of Xcode's plist editor

<key>GoogleAnalytics</key>
<dict>
    <key>TRACKING_ID</key>
    <string>YOUR_ID_HERE</string>
</dict>

Next, add pod to your Podfile, run pod update, and then add the necessary includes to your bridging header file.

#import <GoogleAnalytics/GAI.h>
#import <GoogleAnalytics/GAIDictionaryBuilder.h>
#import <GoogleAnalytics/GAIFields.h>
#import <GoogleAnalytics/GAIEcommerceFields.h>

The existing docs tell you to guard against misconfiguration like this:

// wrong
guard let gai = GAI.sharedInstance() else {
  assert(false, "Google Analytics not configured correctly")
}

Unfortunately, this will break as soon as you do a release build, since assertions are removed in release configurations, and a guard block must end execution of the current scope. Here's a better solution:

if let gai = GAI.sharedInstance() {
    // configure GAI here
} else {
    assertionFailure("Google Analytics not configured correctly")
}

You still get the assertion helping you out while debugging, without running into problems later on.

Next you'll want to some basic configuration.

if
    let gai = GAI.sharedInstance(),
    let gaConfigValues = Bundle.main.infoDictionary?["GoogleAnalytics"] as? [String: String],
    let trackingId = gaConfigValues["TRACKING_ID"]
{
    gai.logger.logLevel = .error
    gai.trackUncaughtExceptions = false
    gai.tracker(withTrackingId: trackingId)
    // gai.dispatchInterval = 120.0
} else {
    assertionFailure("Google Analytics not configured correctly")
}

When you first start integration, I recommend setting the log level to verbose. You could even schemes or your build configurations to set it to different values as needed.

Similarly, I wouldn't change the dispatchInterval from the default, unless you're actively working on your analytics code and need events to show up quicker in the reporting dashboard.

If you want google analytics to record uncaught exceptions, you can enable this feature here. However, be aware that this will interfere with other crash reporting libraries like Crashlytics. If you use one of them or another library that registers exception handlers, set trackUncaughtExceptions to false or initialize them after Google Analytics so the exception handler can be reset.

That should cover the basics, but I've also included an Analytics helper struct below. It's similar to what I use in my apps. Using enums for actions and screen names helps to prevent typos from creeping in.

struct Analytics {
    static func trackEvent(withScreen screen: Screen, category: String, label: String, action: Actions, value: Int? = nil) {
        guard
            let tracker = GAI.sharedInstance().defaultTracker,
            let builder = GAIDictionaryBuilder.createEvent(withCategory: category, action: action.rawValue, label: label, value: NSNumber(integerLiteral: value ?? 0))
        else { return }
 
        tracker.set(kGAIScreenName, value: screen.rawValue)
        tracker.send(builder.build() as [NSObject : AnyObject])
    }
 
    static func trackPageView(withScreen screen: Screen) {
        guard
            let tracker = GAI.sharedInstance().defaultTracker,
            let builder = GAIDictionaryBuilder.createScreenView()
        else { return }
 
        tracker.set(kGAIScreenName, value: screen.rawValue)
        tracker.send(builder.build() as [NSObject : AnyObject])
    }
 
    enum Actions: String {
        case search = "Search"
        case tap = "Tap"
        case toggle = "Toggle"
    }
 
    enum Screen: String {
        case exampleScreenName = "exampleScreenName"
    }
}

In conclusion:

Feedback is an important part of every project. It helps you shape and evolve your product into something your users won't want to do without. Analytics allow you to establish a baseline for feedback that doesn't require much maintenance, so you can focus on the more important task: Make sense out of the data and decide how to act on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment