Search code examples
iosobjective-canalyticsusage-statistics

Tracking user usage time and time on UIViewController like Google Analytics


What is the best way to track App usage time and time a user spends viewing a Screen (or interacting with a UIView) for use within the App itself? Google Analytics seems to do a wonderful job, but the numbers I want to use inside the App itself to unlock items and areas of the App.


Solution

  • You could probably roll your own solution based on Core Data, or if your data is small you could even think of using NSDefaults. Here's a good start. It involves having a base view controller which you should inherit from in each view controller you want to measure the time spent:

    @interface BaseViewController : UIViewController
    
    - (NSString *)screenKey;
    + (NSInteger)secondsInScreen:(NSString *)screenKey;
    
    @end
    

    The implementation simply measures the seconds between the appearance of the screen until it disappears. It's very important to notice the appDidEnterForeground and appDidEnterBackground notifications. When you send your app to the background or it comes back to the foreground, viewDidAppear and viewDidDisappear are not called.

    #import "BaseViewController.h"
    
    @implementation BaseViewController {
        NSDate *_startDate;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
    }
    
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self startMeasuring];
    }
    
    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
        [self stopMeasuring];
    }
    
    - (void)appDidEnterBackground:(NSNotification *)not {
        [self stopMeasuring];    
    }
    
    - (void)appDidEnterForeground:(NSNotification *)not {
        [self startMeasuring];
    }
    
    - (void)startMeasuring {
        _startDate = [NSDate date];
    }
    
    - (void)stopMeasuring {
        NSInteger secondsInScreen = ABS([_startDate timeIntervalSinceNow]);
        [self addSecondsToScreen:secondsInScreen];
    }
    
    - (NSString *)screenKey {
        // Subclasses must override this method
        return @"";
    }
    
    - (void)addSecondsToScreen:(NSInteger)seconds {
        NSString *key = [self screenKey];
        if (key.length > 0) {
            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
            NSNumber *s = [defaults objectForKey:key];
            NSInteger addedSeconds = s.integerValue + seconds;
            [defaults setObject:[NSNumber numberWithInteger:addedSeconds] forKey:[self screenKey]];
            [defaults synchronize];
        }
    }
    
    + (NSInteger)secondsInScreen:(NSString *)screenKey {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSNumber *s = [defaults objectForKey:screenKey];
        return s.integerValue;
    }
    
    @end
    

    Your subclasses must override screenKey retuning a unique identifier for each screen you want to measure. There's a static method in BaseViewController that allows you to get the sum of seconds spent in each screen.

    This is a simple way of doing it. From a design point of view, it would be better to store this information in Core Data and to separate the view controller logic from the storage of that data (the static method secondsInScreen should probably be in another class). But it's a good, working start.