Skip to content

Commit 74d65b3

Browse files
authored
Implement app-lifecycle groundwork (#2800)
* Create the lifecycle class * Add running in background properties and implement archive paths for the stateful classes. * Move -stopTimer into the upload coordinator * Add test for encoding and decoding the storage singleton. * Add a test transport target. * Style * Temporarily commenting out sending lifecycle events. * More style. * Demonstrate that I know what year I made this file
1 parent 6bc5f05 commit 74d65b3

File tree

11 files changed

+234
-8
lines changed

11 files changed

+234
-8
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "GDTLifecycle.h"
18+
19+
#import <GoogleDataTransport/GDTEvent.h>
20+
21+
#import "GDTRegistrar_Private.h"
22+
#import "GDTStorage_Private.h"
23+
#import "GDTTransformer_Private.h"
24+
#import "GDTUploadCoordinator_Private.h"
25+
26+
@implementation GDTLifecycle
27+
28+
+ (void)load {
29+
[self sharedInstance];
30+
}
31+
32+
/** Creates/returns the singleton instance of this class.
33+
*
34+
* @return The singleton instance of this class.
35+
*/
36+
+ (instancetype)sharedInstance {
37+
static GDTLifecycle *sharedInstance;
38+
static dispatch_once_t onceToken;
39+
dispatch_once(&onceToken, ^{
40+
sharedInstance = [[GDTLifecycle alloc] init];
41+
});
42+
return sharedInstance;
43+
}
44+
45+
- (instancetype)init {
46+
self = [super init];
47+
if (self) {
48+
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
49+
[notificationCenter addObserver:self
50+
selector:@selector(applicationDidEnterBackground:)
51+
name:UIApplicationDidEnterBackgroundNotification
52+
object:nil];
53+
[notificationCenter addObserver:self
54+
selector:@selector(applicationWillEnterForeground:)
55+
name:UIApplicationWillEnterForegroundNotification
56+
object:nil];
57+
58+
NSString *name = UIApplicationWillTerminateNotification;
59+
[notificationCenter addObserver:self
60+
selector:@selector(applicationWillTerminate:)
61+
name:name
62+
object:nil];
63+
}
64+
return self;
65+
}
66+
67+
- (void)dealloc {
68+
[[NSNotificationCenter defaultCenter] removeObserver:self];
69+
}
70+
71+
- (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];
77+
}
78+
79+
- (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];
85+
}
86+
87+
- (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];
93+
}
94+
95+
@end

GoogleDataTransport/GoogleDataTransport/Classes/GDTStorage.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@
4343

4444
@implementation GDTStorage
4545

46+
+ (NSString *)archivePath {
47+
static NSString *archivePath;
48+
static dispatch_once_t onceToken;
49+
dispatch_once(&onceToken, ^{
50+
archivePath = [GDTStoragePath() stringByAppendingPathComponent:@"GDTStorageArchive"];
51+
});
52+
return archivePath;
53+
}
54+
4655
+ (instancetype)sharedInstance {
4756
static GDTStorage *sharedStorage;
4857
static dispatch_once_t onceToken;

GoogleDataTransport/GoogleDataTransport/Classes/GDTUploadCoordinator.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ + (instancetype)sharedInstance {
3636
return sharedUploader;
3737
}
3838

39+
+ (NSString *)archivePath {
40+
static NSString *archivePath;
41+
static dispatch_once_t onceToken;
42+
dispatch_once(&onceToken, ^{
43+
NSString *cachePath =
44+
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
45+
NSString *storagePath = [NSString stringWithFormat:@"%@/google-sdks-events", cachePath];
46+
archivePath = [storagePath stringByAppendingPathComponent:@"GDTUploadCoordinator"];
47+
});
48+
return archivePath;
49+
}
50+
3951
- (instancetype)init {
4052
self = [super init];
4153
if (self) {
@@ -149,6 +161,13 @@ - (void)startTimer {
149161
});
150162
}
151163

164+
/** Stops the currently running timer. */
165+
- (void)stopTimer {
166+
if (_timer) {
167+
dispatch_source_cancel(_timer);
168+
}
169+
}
170+
152171
/** Checks the next upload time for each target and makes a determination on whether to upload
153172
* events for that target or not. If so, queries the prioritizers
154173
*/

GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTStorage_Private.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ NS_ASSUME_NONNULL_BEGIN
3535
/** The upload coordinator instance to use. */
3636
@property(nonatomic) GDTUploadCoordinator *uploader;
3737

38+
/** If YES, every call to -storeLog results in background task and serializes the singleton to disk.
39+
*/
40+
@property(nonatomic, readonly) BOOL runningInBackground;
41+
42+
/** Returns the path to the keyed archive of the singleton. This is where the singleton is saved
43+
* to disk during certain app lifecycle events.
44+
*
45+
* @return File path to serialized singleton.
46+
*/
47+
+ (NSString *)archivePath;
48+
3849
@end
3950

4051
NS_ASSUME_NONNULL_END

GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTTransformer_Private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ NS_ASSUME_NONNULL_BEGIN
2828
/** The storage instance used to store events. Should only be used to inject a testing fake. */
2929
@property(nonatomic) GDTStorage *storageInstance;
3030

31+
/** If YES, every call to -transformEvent will result in a background task. */
32+
@property(nonatomic, readonly) BOOL runningInBackground;
33+
3134
@end
3235

3336
NS_ASSUME_NONNULL_END

GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTUploadCoordinator_Private.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,22 @@ NS_ASSUME_NONNULL_BEGIN
5858
/** The registrar object the coordinator will use. Generally used for testing. */
5959
@property(nonatomic) GDTRegistrar *registrar;
6060

61+
/** If YES, completion and other operations will result in serializing the singleton to disk. */
62+
@property(nonatomic, readonly) BOOL runningInBackground;
63+
64+
/** Returns the path to the keyed archive of the singleton. This is where the singleton is saved
65+
* to disk during certain app lifecycle events.
66+
*
67+
* @return File path to serialized singleton.
68+
*/
69+
+ (NSString *)archivePath;
70+
6171
/** Starts the upload timer. */
6272
- (void)startTimer;
6373

74+
/** Stops the upload timer from running. */
75+
- (void)stopTimer;
76+
6477
@end
6578

6679
NS_ASSUME_NONNULL_END
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
#import <UIKit/UIKit.h>
19+
20+
@class GDTEvent;
21+
22+
NS_ASSUME_NONNULL_BEGIN
23+
24+
/** A protocol defining the lifecycle events objects in the library must respond to immediately. */
25+
@protocol GDTLifecycleProtocol <NSObject>
26+
27+
@required
28+
29+
/** Indicates an imminent app termination in the rare occurrence when -applicationWillTerminate: has
30+
* been called.
31+
*
32+
* @param app The UIApplication instance.
33+
*/
34+
- (void)appWillTerminate:(UIApplication *)app;
35+
36+
/** Indicates that the app is moving to background and eventual suspension.
37+
*
38+
* @param app The UIApplication instance.
39+
*/
40+
- (void)appWillBackground:(UIApplication *)app;
41+
42+
/** Indicates that the app is resuming operation.
43+
*
44+
* @param app The UIApplication instance.
45+
*/
46+
- (void)appWillForeground:(UIApplication *)app;
47+
48+
@end
49+
50+
/** This class manages the library's response to app lifecycle events.
51+
*
52+
* When backgrounding, the library doesn't stop processing events, it's just that several background
53+
* tasks will end up being created for every event that's sent, and the stateful objects of the
54+
* library (GDTStorage and GDTUploadCoordinator singletons) will deserialize themselves from and to
55+
* disk before and after every operation, respectively.
56+
*/
57+
@interface GDTLifecycle : NSObject <UIApplicationDelegate>
58+
59+
@end
60+
61+
NS_ASSUME_NONNULL_END

GoogleDataTransport/GoogleDataTransport/Classes/Public/GDTTargets.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
*/
2222
typedef NS_ENUM(NSInteger, GDTTarget) {
2323

24+
/** A target only used in testing. */
25+
kGDTTargetTest = 999,
26+
2427
/** The CCT target. */
2528
kGDTTargetCCT = 1000,
2629
};

GoogleDataTransport/Tests/Common/Categories/GDTUploadCoordinator+Testing.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#import "GDTUploadCoordinator.h"
18+
#import "GDTUploadCoordinator_Private.h"
1819

1920
NS_ASSUME_NONNULL_BEGIN
2021

@@ -23,9 +24,6 @@ NS_ASSUME_NONNULL_BEGIN
2324
/** Resets the properties of the singleton, but does not reallocate a new singleton. */
2425
- (void)reset;
2526

26-
/** Stops the upload timer from running. */
27-
- (void)stopTimer;
28-
2927
/** The time interval, in nanoseconds, that the time should be called on. */
3028
@property(nonatomic, readwrite) uint64_t timerInterval;
3129

GoogleDataTransport/Tests/Common/Categories/GDTUploadCoordinator+Testing.m

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ - (void)reset {
3232
self.registrar = [GDTRegistrar sharedInstance];
3333
}
3434

35-
- (void)stopTimer {
36-
dispatch_source_cancel(self.timer);
37-
}
38-
3935
- (void)setTimerInterval:(uint64_t)timerInterval {
4036
[self setValue:@(timerInterval) forKey:@"_timerInterval"];
4137
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, timerInterval, self.timerLeeway);

GoogleDataTransport/Tests/Unit/GDTStorageTest.m

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,25 @@ - (void)testNSSecureCoding {
264264
XCTAssertNil([[GDTStorage sharedInstance].storedEvents lastObject]);
265265
});
266266

267-
// TODO(mikehaney24): Ensure that the object created by alloc is discarded?
267+
GDTStorage *unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData];
268+
XCTAssertNotNil([unarchivedStorage.storedEvents lastObject]);
269+
}
270+
271+
/** Tests encoding and decoding the storage singleton when calling -sharedInstance. */
272+
- (void)testNSSecureCodingWithSharedInstance {
273+
GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target];
274+
event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding];
275+
XCTAssertNoThrow([[GDTStorage sharedInstance] storeEvent:event]);
276+
event = nil;
277+
NSData *storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance]];
278+
dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{
279+
XCTAssertNotNil([[GDTStorage sharedInstance].storedEvents lastObject]);
280+
});
281+
[[GDTStorage sharedInstance] removeEvents:[GDTStorage sharedInstance].storedEvents.set];
282+
dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{
283+
XCTAssertNil([[GDTStorage sharedInstance].storedEvents lastObject]);
284+
});
285+
268286
GDTStorage *unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData];
269287
XCTAssertNotNil([unarchivedStorage.storedEvents lastObject]);
270288
}

0 commit comments

Comments
 (0)