Skip to content

Implement app lifecycle reactivity. #2801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions GoogleDataTransport.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ Shared library for iOS SDK data transport needs.
test_spec.source_files = ['GoogleDataTransport/Tests/Unit/**/*.{h,m}'] + common_test_sources
end

s.test_spec 'Tests-Lifecycle' do |test_spec|
test_spec.requires_app_host = false
test_spec.source_files = ['GoogleDataTransport/Tests/Lifecycle/**/*.{h,m}'] + common_test_sources
end

# Integration test specs
s.test_spec 'Tests-Integration' do |test_spec|
test_spec.requires_app_host = false
Expand Down
30 changes: 15 additions & 15 deletions GoogleDataTransport/GoogleDataTransport/Classes/GDTLifecycle.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,27 @@ - (void)dealloc {
}

- (void)applicationDidEnterBackground:(NSNotification *)notification {
// UIApplication *application = [UIApplication sharedApplication];
// [[GDTTransformer sharedInstance] appWillBackground:application];
// [[GDTStorage sharedInstance] appWillBackground:application];
// [[GDTUploadCoordinator sharedInstance] appWillBackground:application];
// [[GDTRegistrar sharedInstance] appWillBackground:application];
UIApplication *application = [UIApplication sharedApplication];
[[GDTTransformer sharedInstance] appWillBackground:application];
[[GDTStorage sharedInstance] appWillBackground:application];
[[GDTUploadCoordinator sharedInstance] appWillBackground:application];
[[GDTRegistrar sharedInstance] appWillBackground:application];
}

- (void)applicationWillEnterForeground:(NSNotification *)notification {
// UIApplication *application = [UIApplication sharedApplication];
// [[GDTTransformer sharedInstance] appWillForeground:application];
// [[GDTStorage sharedInstance] appWillForeground:application];
// [[GDTUploadCoordinator sharedInstance] appWillForeground:application];
// [[GDTRegistrar sharedInstance] appWillForeground:application];
UIApplication *application = [UIApplication sharedApplication];
[[GDTTransformer sharedInstance] appWillForeground:application];
[[GDTStorage sharedInstance] appWillForeground:application];
[[GDTUploadCoordinator sharedInstance] appWillForeground:application];
[[GDTRegistrar sharedInstance] appWillForeground:application];
}

- (void)applicationWillTerminate:(NSNotification *)notification {
// UIApplication *application = [UIApplication sharedApplication];
// [[GDTTransformer sharedInstance] appWillTerminate:application];
// [[GDTStorage sharedInstance] appWillTerminate:application];
// [[GDTUploadCoordinator sharedInstance] appWillTerminate:application];
// [[GDTRegistrar sharedInstance] appWillTerminate:application];
UIApplication *application = [UIApplication sharedApplication];
[[GDTTransformer sharedInstance] appWillTerminate:application];
[[GDTStorage sharedInstance] appWillTerminate:application];
[[GDTUploadCoordinator sharedInstance] appWillTerminate:application];
[[GDTRegistrar sharedInstance] appWillTerminate:application];
}

@end
35 changes: 35 additions & 0 deletions GoogleDataTransport/GoogleDataTransport/Classes/GDTRegistrar.m
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,39 @@ - (void)registerPrioritizer:(id<GDTPrioritizer>)prioritizer target:(GDTTarget)ta
return targetToPrioritizer;
}

#pragma mark - GDTLifecycleProtocol

- (void)appWillBackground:(nonnull UIApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
[uploader appWillBackground:app];
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
[prioritizer appWillBackground:app];
}
});
}

- (void)appWillForeground:(nonnull UIApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
[uploader appWillForeground:app];
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
[prioritizer appWillForeground:app];
}
});
}

- (void)appWillTerminate:(nonnull UIApplication *)app {
dispatch_sync(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
[uploader appWillTerminate:app];
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
[prioritizer appWillTerminate:app];
}
});
}

@end
4 changes: 3 additions & 1 deletion GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

#import <Foundation/Foundation.h>

#import <GoogleDataTransport/GDTLifecycle.h>

@class GDTEvent;
@class GDTStoredEvent;

NS_ASSUME_NONNULL_BEGIN

/** Manages the storage of events. This class is thread-safe. */
@interface GDTStorage : NSObject <NSSecureCoding>
@interface GDTStorage : NSObject <NSSecureCoding, GDTLifecycleProtocol>

/** Creates and/or returns the storage singleton.
*
Expand Down
37 changes: 37 additions & 0 deletions GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "GDTAssert.h"
#import "GDTConsoleLogger.h"
#import "GDTEvent_Private.h"
#import "GDTLifecycle.h"
#import "GDTRegistrar_Private.h"
#import "GDTUploadCoordinator.h"

Expand Down Expand Up @@ -75,6 +76,13 @@ - (instancetype)init {
- (void)storeEvent:(GDTEvent *)event {
[self createEventDirectoryIfNotExists];

__block UIBackgroundTaskIdentifier bgID = UIBackgroundTaskInvalid;
if (_runningInBackground) {
bgID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgID];
}];
}

dispatch_async(_storageQueue, ^{
// Check that a backend implementation is available for this target.
NSInteger target = event.target;
Expand All @@ -99,6 +107,12 @@ - (void)storeEvent:(GDTEvent *)event {
if (event.qosTier == GDTEventQoSFast) {
[self.uploader forceUploadForTarget:target];
}

// If running in the background, save state to disk and end the associated background task.
if (bgID != UIBackgroundTaskInvalid) {
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
[[UIApplication sharedApplication] endBackgroundTask:bgID];
}
});
}

Expand Down Expand Up @@ -180,6 +194,29 @@ - (void)addEventToTrackingCollections:(GDTStoredEvent *)event {
_targetToEventSet[event.target] = events;
}

#pragma mark - GDTLifecycleProtocol

- (void)appWillForeground:(UIApplication *)app {
[NSKeyedUnarchiver unarchiveObjectWithFile:[GDTStorage archivePath]];
self->_runningInBackground = NO;
}

- (void)appWillBackground:(UIApplication *)app {
self->_runningInBackground = YES;
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
// Create an immediate background task to run until the end of the current queue of work.
__block UIBackgroundTaskIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgID];
}];
dispatch_async(_storageQueue, ^{
[app endBackgroundTask:bgID];
});
}

- (void)appWillTerminate:(UIApplication *)application {
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
}

#pragma mark - NSSecureCoding

/** The NSKeyedCoder key for the storedEvents property. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#import <Foundation/Foundation.h>

#import <GoogleDataTransport/GDTLifecycle.h>

@class GDTEvent;

@protocol GDTEventTransformer;
Expand All @@ -28,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
* maintain state (or at least, there's nothing to stop them from doing that) and the same instances
* may be used across multiple instances.
*/
@interface GDTTransformer : NSObject
@interface GDTTransformer : NSObject <GDTLifecycleProtocol>

/** Instantiates or returns the event transformer singleton.
*
Expand All @@ -37,6 +39,9 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)sharedInstance;

/** Writes the result of applying the given transformers' -transform method on the given event.
*
* @note If the app is suspended, a background task will be created to complete work in-progress,
* but this method will not send any further events until the app is resumed.
*
* @param event The event to apply transformers on.
* @param transformers The list of transformers to apply.
Expand Down
35 changes: 35 additions & 0 deletions GoogleDataTransport/GoogleDataTransport/Classes/GDTTransformer.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#import "GDTAssert.h"
#import "GDTConsoleLogger.h"
#import "GDTLifecycle.h"
#import "GDTStorage.h"

@implementation GDTTransformer
Expand All @@ -46,6 +47,13 @@ - (instancetype)init {
- (void)transformEvent:(GDTEvent *)event
withTransformers:(NSArray<id<GDTEventTransformer>> *)transformers {
GDTAssert(event, @"You can't write a nil event");

__block UIBackgroundTaskIdentifier bgID = UIBackgroundTaskInvalid;
if (_runningInBackground) {
bgID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgID];
}];
}
dispatch_async(_eventWritingQueue, ^{
GDTEvent *transformedEvent = event;
for (id<GDTEventTransformer> transformer in transformers) {
Expand All @@ -61,7 +69,34 @@ - (void)transformEvent:(GDTEvent *)event
}
}
[self.storageInstance storeEvent:transformedEvent];
if (self->_runningInBackground) {
[[UIApplication sharedApplication] endBackgroundTask:bgID];
}
});
}

#pragma mark - GDTLifecycleProtocol

- (void)appWillForeground:(UIApplication *)app {
dispatch_async(_eventWritingQueue, ^{
self->_runningInBackground = NO;
});
}

- (void)appWillBackground:(UIApplication *)app {
// Create an immediate background task to run until the end of the current queue of work.
__block UIBackgroundTaskIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgID];
}];
dispatch_async(_eventWritingQueue, ^{
[app endBackgroundTask:bgID];
});
}

- (void)appWillTerminate:(UIApplication *)application {
// Flush the queue immediately.
dispatch_sync(_eventWritingQueue, ^{
});
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@

#import <Foundation/Foundation.h>

#import <GoogleDataTransport/GDTLifecycle.h>

#import "GDTRegistrar.h"

NS_ASSUME_NONNULL_BEGIN

/** This class connects storage and uploader implementations, providing events to an uploader
* and informing the storage what events were successfully uploaded or not.
*/
@interface GDTUploadCoordinator : NSObject
@interface GDTUploadCoordinator : NSObject <NSSecureCoding, GDTLifecycleProtocol>

/** Creates and/or returrns the singleton.
*
Expand Down
Loading