9

I've been working with Google's Analytics SDK v2beta3 and have everything working except I can't get manual dispatch to work when the app leaves the active state. (fyi, for my app I need to reserve battery power so am only using '[[GAI sharedinstance] dispatch]' to dispatch my event data when the user is finished with the app.)

I've tried several things, but while the dispatch call is reached and run during tracing, it doesn't seem to do anything... no log output (I have debug mode on) and no data uploaded. It should minimally report "GoogleAnalytics 2.0b3 -[GAIDispatcher initiateDispatch:retryNumber:] (GAIDispatcher.m:479) DEBUG: No pending hits." or something of that sort I would think. But nothing in the log and no data sent.

Instead, when the app resumes from background, the hits are then transmitted and I see all the debug statements on my console and data is sent successfully to my Google Analytics account.

Below is my code...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    // set up Google Analytics tracker
    [GAI sharedInstance].trackUncaughtExceptions = YES; // automatically track uncaught exceptions with Google Analytics - sent with stack trace.
    [GAI sharedInstance].dispatchInterval = -1;         // set Google Analytics dispatch off, will do manually when app goes into background.
    [GAI sharedInstance].debug = YES;                   // set debug to YES for extra debugging information.
    id<GAITracker> tracker = [[GAI sharedInstance] trackerWithTrackingId:GOOGLE_ANALYTICS_TRACKING_ID_FOR_TEST];    // Create tracker instance.
    [tracker setSessionTimeout:600];                     // set min time between sessions to be 10 minutes
    [GAI sharedInstance].defaultTracker = tracker;   // reset the default tracker if needed
    [[GAI sharedInstance] setOptOut:NO];

    ...
}

 - (void)applicationWillResignActive:(UIApplication *)application
{
    UIDevice * device = [UIDevice currentDevice];
    BOOL backgroundTasksSupported = NO;

    if ([device respondsToSelector:@selector(isMultitaskingSupported)]) {
        backgroundTasksSupported = device.multitaskingSupported;
    }

    if (backgroundTasksSupported) {
        self.uploadAnalyticsBackgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{[self endBackgroundUploadTask];}];

        // Start the long-running task and return immediately.
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        // put block on Global run queue...

            [[GAI sharedInstance] dispatch];                                                    // for dispatch of collected metric/analysis data...

            [self endBackgroundUploadTask];
        });
    }
    else {
        [[GAI sharedInstance] dispatch];
    }

}

- (void) endBackgroundUploadTask
{
     if(self.uploadAnalyticsBackgroundTask != UIBackgroundTaskInvalid) {          // if background task running, end it
        [[UIApplication sharedApplication] endBackgroundTask: self.uploadAnalyticsBackgroundTask];
        self.uploadAnalyticsBackgroundTask = UIBackgroundTaskInvalid;
     }
}

The app reaches and executes the '[[GAI sharedInstance] dispatch];' line, but does nothing. I'm quite a newbie with background tasks when the app goes to the background so maybe I'm doing something wrong. But as part of my investigations I've even simplified applicationWillResignActive to this (which is/should be blocking)... but I get the same thing: no debug info and no transmitted data.

 - (void)applicationWillResignActive:(UIApplication *)application
{
    [[GAI sharedInstance] dispatch];
}

I've tried it with the dispatch interval non-negative (say 15 seconds) and I get regular transmission at the requested interval, but the call to manually dispatch doesn't work.

Calls to manual dispatch in other parts of my code do work. It just seems that it doesn't work when the app is closing down.

Any thoughts advice on what I might have done wrong and how I can fix this?

Steve Mason
  • 191
  • 3

3 Answers3

2

Unlike previous Google Analytics SDK versions, since v2 the manual dispatch mechanism doesn't run synchronously, i.e. instead of waiting for dispatching to be done, it returns immediately. So if you use dispatch right before becoming inactive, the dispatch won't happen since the application's run queue will be paused before the actual dispatch happens. As a workaround, I'm starting a background task which sleeps for a few seconds and keeps the app running long enough for the dispatch to complete.

Hopefully Google will re-add a dispatchSynchronously method in future versions of the SDK.

Also, be aware that on applicationWillTerminate, you only have 5 seconds until the application is killed, so don't wait too long.

- (void)dispatchAndWait:(NSTimeInterval)waitTime {
  UIBackgroundTaskIdentifier backgroundTask = 0;
  backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
  }];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // GAI dispatch runs asynchronously and dispatches on the main
    // thread, so we're giving it some time to dispatch and keep the
    // app running in background by sleeping on a background thread
    [[GAI sharedInstance] dispatch];
    [NSThread sleepForTimeInterval:waitTime];

    [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
  });
}
Zargony
  • 9,615
  • 3
  • 44
  • 44
  • I confirmed this method, as put in `applicationWillResignActive:`, works like a charm with both v.2 and v.3 of Google Analytics SDK. Many thanks! – Nimit Pattanasri Dec 27 '13 at 11:23
1

This, as of August 2013, is the correct answer.

As long as the app was closed normally, i.e. it received a UIApplicationWillResignActiveNotification, then the hits will be dispatched when the app is next opened.

The reason you aren't seeing this is probably that quitting the app from Xcode does not close it normally. Pressing the home button, shortly followed by Xcode's stop, will close it normally, and it will work.

The issue here is that even though the hits are dispatched, they are discarded by Analytics if they arrive after 4am the following day in the timezone of the Analytics profile. That is a bit thorny, and might justify a solution like Andreas's.

Community
  • 1
  • 1
Max
  • 2,760
  • 1
  • 28
  • 47
0

We have this issue but were able to raise our success rate to 50%. Here is how we did it.

We put in "insurance code" and with this about 50% of the time our events are now dispatched as the app transitions to the background. The other 50% of the time the events are not dispatched until you return to the app.

The insurance code that got us to 50% is putting the dispatch call inside the button press code:

- (IBAction)goToAppStore:(id)sender
{    
    ...
    // Tracking
    // Using events (pressing on buttons)

    id <GAITracker> tracker = [[GAI sharedInstance] defaultTracker];

    [tracker sendEventWithCategory:@"App Checkout"
                        withAction:@"Checkout Button Pressed"
                        withLabel:nameApp.text
                        withValue:nil];

    [[GAI sharedInstance] dispatch];
    ...
}

Also, looking at the sample code in the main quesiton - Apple states to put the code for doing background tasks in

applicationDidEnterBackground:

instead of

applicationWillResignActive:

but it doesn't seem that has had a huge effect for us. Here is Apple's documentation on the subject (see Listing 3-3)

http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html

Also, I put up a a longer post from about what we did with code samples if you want to see more closely how we got to roughly 50% success: How do we dispatch Google Analytics events when iOS app goes to the background?

At this point though I would still like a solution that got to 100% if anyone has found one.

Community
  • 1
  • 1
Will
  • 111
  • 1
  • 3