Skip to content

Commit e8e88d3

Browse files
authored
Implement app lifecycle reactivity. (#2801)
* Implement app lifecycle reactivity. * Re-enable the lifecycle code
1 parent 74d65b3 commit e8e88d3

23 files changed

+613
-26
lines changed

GoogleDataTransport.podspec

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ Shared library for iOS SDK data transport needs.
4141
test_spec.source_files = ['GoogleDataTransport/Tests/Unit/**/*.{h,m}'] + common_test_sources
4242
end
4343

44+
s.test_spec 'Tests-Lifecycle' do |test_spec|
45+
test_spec.requires_app_host = false
46+
test_spec.source_files = ['GoogleDataTransport/Tests/Lifecycle/**/*.{h,m}'] + common_test_sources
47+
end
48+
4449
# Integration test specs
4550
s.test_spec 'Tests-Integration' do |test_spec|
4651
test_spec.requires_app_host = false

GoogleDataTransport/GoogleDataTransport/Classes/GDTLifecycle.m

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,27 +69,27 @@ - (void)dealloc {
6969
}
7070

7171
- (void)applicationDidEnterBackground:(NSNotification *)notification {
72-
// UIApplication *application = [UIApplication sharedApplication];
73-
// [[GDTTransformer sharedInstance] appWillBackground:application];
74-
// [[GDTStorage sharedInstance] appWillBackground:application];
75-
// [[GDTUploadCoordinator sharedInstance] appWillBackground:application];
76-
// [[GDTRegistrar sharedInstance] appWillBackground:application];
72+
UIApplication *application = [UIApplication sharedApplication];
73+
[[GDTTransformer sharedInstance] appWillBackground:application];
74+
[[GDTStorage sharedInstance] appWillBackground:application];
75+
[[GDTUploadCoordinator sharedInstance] appWillBackground:application];
76+
[[GDTRegistrar sharedInstance] appWillBackground:application];
7777
}
7878

7979
- (void)applicationWillEnterForeground:(NSNotification *)notification {
80-
// UIApplication *application = [UIApplication sharedApplication];
81-
// [[GDTTransformer sharedInstance] appWillForeground:application];
82-
// [[GDTStorage sharedInstance] appWillForeground:application];
83-
// [[GDTUploadCoordinator sharedInstance] appWillForeground:application];
84-
// [[GDTRegistrar sharedInstance] appWillForeground:application];
80+
UIApplication *application = [UIApplication sharedApplication];
81+
[[GDTTransformer sharedInstance] appWillForeground:application];
82+
[[GDTStorage sharedInstance] appWillForeground:application];
83+
[[GDTUploadCoordinator sharedInstance] appWillForeground:application];
84+
[[GDTRegistrar sharedInstance] appWillForeground:application];
8585
}
8686

8787
- (void)applicationWillTerminate:(NSNotification *)notification {
88-
// UIApplication *application = [UIApplication sharedApplication];
89-
// [[GDTTransformer sharedInstance] appWillTerminate:application];
90-
// [[GDTStorage sharedInstance] appWillTerminate:application];
91-
// [[GDTUploadCoordinator sharedInstance] appWillTerminate:application];
92-
// [[GDTRegistrar sharedInstance] appWillTerminate:application];
88+
UIApplication *application = [UIApplication sharedApplication];
89+
[[GDTTransformer sharedInstance] appWillTerminate:application];
90+
[[GDTStorage sharedInstance] appWillTerminate:application];
91+
[[GDTUploadCoordinator sharedInstance] appWillTerminate:application];
92+
[[GDTRegistrar sharedInstance] appWillTerminate:application];
9393
}
9494

9595
@end

GoogleDataTransport/GoogleDataTransport/Classes/GDTRegistrar.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,39 @@ - (void)registerPrioritizer:(id<GDTPrioritizer>)prioritizer target:(GDTTarget)ta
8989
return targetToPrioritizer;
9090
}
9191

92+
#pragma mark - GDTLifecycleProtocol
93+
94+
- (void)appWillBackground:(nonnull UIApplication *)app {
95+
dispatch_async(_registrarQueue, ^{
96+
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
97+
[uploader appWillBackground:app];
98+
}
99+
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
100+
[prioritizer appWillBackground:app];
101+
}
102+
});
103+
}
104+
105+
- (void)appWillForeground:(nonnull UIApplication *)app {
106+
dispatch_async(_registrarQueue, ^{
107+
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
108+
[uploader appWillForeground:app];
109+
}
110+
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
111+
[prioritizer appWillForeground:app];
112+
}
113+
});
114+
}
115+
116+
- (void)appWillTerminate:(nonnull UIApplication *)app {
117+
dispatch_sync(_registrarQueue, ^{
118+
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
119+
[uploader appWillTerminate:app];
120+
}
121+
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
122+
[prioritizer appWillTerminate:app];
123+
}
124+
});
125+
}
126+
92127
@end

GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616

1717
#import <Foundation/Foundation.h>
1818

19+
#import <GoogleDataTransport/GDTLifecycle.h>
20+
1921
@class GDTEvent;
2022
@class GDTStoredEvent;
2123

2224
NS_ASSUME_NONNULL_BEGIN
2325

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

2729
/** Creates and/or returns the storage singleton.
2830
*

GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#import "GDTAssert.h"
2424
#import "GDTConsoleLogger.h"
2525
#import "GDTEvent_Private.h"
26+
#import "GDTLifecycle.h"
2627
#import "GDTRegistrar_Private.h"
2728
#import "GDTUploadCoordinator.h"
2829

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

79+
__block UIBackgroundTaskIdentifier bgID = UIBackgroundTaskInvalid;
80+
if (_runningInBackground) {
81+
bgID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
82+
[[UIApplication sharedApplication] endBackgroundTask:bgID];
83+
}];
84+
}
85+
7886
dispatch_async(_storageQueue, ^{
7987
// Check that a backend implementation is available for this target.
8088
NSInteger target = event.target;
@@ -99,6 +107,12 @@ - (void)storeEvent:(GDTEvent *)event {
99107
if (event.qosTier == GDTEventQoSFast) {
100108
[self.uploader forceUploadForTarget:target];
101109
}
110+
111+
// If running in the background, save state to disk and end the associated background task.
112+
if (bgID != UIBackgroundTaskInvalid) {
113+
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
114+
[[UIApplication sharedApplication] endBackgroundTask:bgID];
115+
}
102116
});
103117
}
104118

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

197+
#pragma mark - GDTLifecycleProtocol
198+
199+
- (void)appWillForeground:(UIApplication *)app {
200+
[NSKeyedUnarchiver unarchiveObjectWithFile:[GDTStorage archivePath]];
201+
self->_runningInBackground = NO;
202+
}
203+
204+
- (void)appWillBackground:(UIApplication *)app {
205+
self->_runningInBackground = YES;
206+
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
207+
// Create an immediate background task to run until the end of the current queue of work.
208+
__block UIBackgroundTaskIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
209+
[app endBackgroundTask:bgID];
210+
}];
211+
dispatch_async(_storageQueue, ^{
212+
[app endBackgroundTask:bgID];
213+
});
214+
}
215+
216+
- (void)appWillTerminate:(UIApplication *)application {
217+
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
218+
}
219+
183220
#pragma mark - NSSecureCoding
184221

185222
/** The NSKeyedCoder key for the storedEvents property. */

GoogleDataTransport/GoogleDataTransport/Classes/GDTTransformer.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#import <Foundation/Foundation.h>
1818

19+
#import <GoogleDataTransport/GDTLifecycle.h>
20+
1921
@class GDTEvent;
2022

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

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

3941
/** Writes the result of applying the given transformers' -transform method on the given event.
42+
*
43+
* @note If the app is suspended, a background task will be created to complete work in-progress,
44+
* but this method will not send any further events until the app is resumed.
4045
*
4146
* @param event The event to apply transformers on.
4247
* @param transformers The list of transformers to apply.

GoogleDataTransport/GoogleDataTransport/Classes/GDTTransformer.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#import "GDTAssert.h"
2323
#import "GDTConsoleLogger.h"
24+
#import "GDTLifecycle.h"
2425
#import "GDTStorage.h"
2526

2627
@implementation GDTTransformer
@@ -46,6 +47,13 @@ - (instancetype)init {
4647
- (void)transformEvent:(GDTEvent *)event
4748
withTransformers:(NSArray<id<GDTEventTransformer>> *)transformers {
4849
GDTAssert(event, @"You can't write a nil event");
50+
51+
__block UIBackgroundTaskIdentifier bgID = UIBackgroundTaskInvalid;
52+
if (_runningInBackground) {
53+
bgID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
54+
[[UIApplication sharedApplication] endBackgroundTask:bgID];
55+
}];
56+
}
4957
dispatch_async(_eventWritingQueue, ^{
5058
GDTEvent *transformedEvent = event;
5159
for (id<GDTEventTransformer> transformer in transformers) {
@@ -61,7 +69,34 @@ - (void)transformEvent:(GDTEvent *)event
6169
}
6270
}
6371
[self.storageInstance storeEvent:transformedEvent];
72+
if (self->_runningInBackground) {
73+
[[UIApplication sharedApplication] endBackgroundTask:bgID];
74+
}
75+
});
76+
}
77+
78+
#pragma mark - GDTLifecycleProtocol
79+
80+
- (void)appWillForeground:(UIApplication *)app {
81+
dispatch_async(_eventWritingQueue, ^{
82+
self->_runningInBackground = NO;
6483
});
6584
}
6685

86+
- (void)appWillBackground:(UIApplication *)app {
87+
// Create an immediate background task to run until the end of the current queue of work.
88+
__block UIBackgroundTaskIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
89+
[app endBackgroundTask:bgID];
90+
}];
91+
dispatch_async(_eventWritingQueue, ^{
92+
[app endBackgroundTask:bgID];
93+
});
94+
}
95+
96+
- (void)appWillTerminate:(UIApplication *)application {
97+
// Flush the queue immediately.
98+
dispatch_sync(_eventWritingQueue, ^{
99+
});
100+
}
101+
67102
@end

GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@
1616

1717
#import <Foundation/Foundation.h>
1818

19+
#import <GoogleDataTransport/GDTLifecycle.h>
20+
1921
#import "GDTRegistrar.h"
2022

2123
NS_ASSUME_NONNULL_BEGIN
2224

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

2830
/** Creates and/or returrns the singleton.
2931
*

0 commit comments

Comments
 (0)