Skip to content

Commit fc6074e

Browse files
authored
Expand the upload package capabilities (#2976)
* Add -isEqual method to GDTEvent * Expand the capabilities of the upload package * Style * More style * Change API usage for GDTUploadCoordinator test * Add a missing import * Consolidate methods * Style
1 parent 6dd834d commit fc6074e

File tree

11 files changed

+245
-32
lines changed

11 files changed

+245
-32
lines changed

GoogleDataTransport/GDTLibrary/GDTEvent.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ - (NSUInteger)hash {
5353
return mappingIDHash ^ _target ^ dataObjectTransportBytesHash ^ _qosTier ^ timeHash;
5454
}
5555

56+
- (BOOL)isEqual:(id)object {
57+
return [self hash] == [object hash];
58+
}
59+
5660
- (void)setDataObject:(id<GDTEventDataObject>)dataObject {
5761
// If you're looking here because of a performance issue in -transportBytes slowing the assignment
5862
// of -dataObject, one way to address this is to add a queue to this class,

GoogleDataTransport/GDTLibrary/GDTUploadPackage.m

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,36 @@
1616

1717
#import <GoogleDataTransport/GDTUploadPackage.h>
1818

19+
#import <GoogleDataTransport/GDTClock.h>
20+
1921
#import "GDTLibrary/Private/GDTStorage_Private.h"
2022
#import "GDTLibrary/Private/GDTUploadPackage_Private.h"
2123

22-
@implementation GDTUploadPackage
24+
@implementation GDTUploadPackage {
25+
/** If YES, is being handled by the handler. */
26+
BOOL _isHandled;
27+
28+
/** A timer that will regularly check to see whether this package has expired or not. */
29+
NSTimer *_expirationTimer;
30+
}
2331

24-
- (instancetype)init {
32+
- (instancetype)initWithTarget:(GDTTarget)target {
2533
self = [super init];
2634
if (self) {
35+
_target = target;
2736
_storage = [GDTStorage sharedInstance];
37+
_deliverByTime = [GDTClock clockSnapshotInTheFuture:180000];
38+
_expirationTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
39+
target:self
40+
selector:@selector(checkIfPackageIsExpired:)
41+
userInfo:nil
42+
repeats:YES];
2843
}
2944
return self;
3045
}
3146

3247
- (instancetype)copy {
33-
GDTUploadPackage *newPackage = [[GDTUploadPackage alloc] init];
48+
GDTUploadPackage *newPackage = [[GDTUploadPackage alloc] initWithTarget:_target];
3449
newPackage->_events = [_events copy];
3550
return newPackage;
3651
}
@@ -43,10 +58,84 @@ - (BOOL)isEqual:(id)object {
4358
return [self hash] == [object hash];
4459
}
4560

61+
- (void)dealloc {
62+
[_expirationTimer invalidate];
63+
}
64+
4665
- (void)setStorage:(GDTStorage *)storage {
4766
if (storage != _storage) {
4867
_storage = storage;
4968
}
5069
}
5170

71+
- (void)completeDelivery {
72+
if (!_isHandled && _handler &&
73+
[_handler respondsToSelector:@selector(packageDelivered:successful:)]) {
74+
[_expirationTimer invalidate];
75+
_isHandled = YES;
76+
[_handler packageDelivered:self successful:YES];
77+
}
78+
}
79+
80+
- (void)retryDeliveryInTheFuture {
81+
if (!_isHandled && _handler &&
82+
[_handler respondsToSelector:@selector(packageDelivered:successful:)]) {
83+
[_expirationTimer invalidate];
84+
_isHandled = YES;
85+
[_handler packageDelivered:self successful:NO];
86+
}
87+
}
88+
89+
- (void)checkIfPackageIsExpired:(NSTimer *)timer {
90+
if ([[GDTClock snapshot] isAfter:_deliverByTime]) {
91+
if (_handler && [_handler respondsToSelector:@selector(packageExpired:)]) {
92+
_isHandled = YES;
93+
[_expirationTimer invalidate];
94+
[_handler packageExpired:self];
95+
}
96+
}
97+
}
98+
99+
#pragma mark - NSSecureCoding
100+
101+
/** The keyed archiver key for the events property. */
102+
static NSString *const kEventsKey = @"GDTUploadPackageEventsKey";
103+
104+
/** The keyed archiver key for the _isHandled property. */
105+
static NSString *const kDeliverByTimeKey = @"GDTUploadPackageDeliveryByTimeKey";
106+
107+
/** The keyed archiver key for the _isHandled ivar. */
108+
static NSString *const kIsHandledKey = @"GDTUploadPackageIsHandledKey";
109+
110+
/** The keyed archiver key for the handler property. */
111+
static NSString *const kHandlerKey = @"GDTUploadPackageHandlerKey";
112+
113+
/** The keyed archiver key for the target property. */
114+
static NSString *const kTargetKey = @"GDTUploadPackageTargetKey";
115+
116+
+ (BOOL)supportsSecureCoding {
117+
return YES;
118+
}
119+
120+
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
121+
[aCoder encodeObject:_events forKey:kEventsKey];
122+
[aCoder encodeObject:_deliverByTime forKey:kDeliverByTimeKey];
123+
[aCoder encodeBool:_isHandled forKey:kIsHandledKey];
124+
[aCoder encodeObject:_handler forKey:kHandlerKey];
125+
[aCoder encodeInteger:_target forKey:kTargetKey];
126+
}
127+
128+
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
129+
GDTTarget target = [aDecoder decodeIntegerForKey:kTargetKey];
130+
self = [self initWithTarget:target];
131+
if (self) {
132+
_events = [aDecoder decodeObjectOfClass:[NSSet class] forKey:kEventsKey];
133+
_deliverByTime = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:kDeliverByTimeKey];
134+
_isHandled = [aDecoder decodeBoolForKey:kIsHandledKey];
135+
// Isn't technically NSSecureCoding, because we don't know the class of this object.
136+
_handler = [aDecoder decodeObjectForKey:kHandlerKey];
137+
}
138+
return self;
139+
}
140+
52141
@end

GoogleDataTransport/GDTLibrary/Private/GDTUploadPackage_Private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@
2323
/** The storage object this upload package will use to resolve event hashes to files. */
2424
@property(nonatomic) GDTStorage *storage;
2525

26+
/** A handler that will receive callbacks for certain events. */
27+
@property(nonatomic) id<NSSecureCoding, GDTUploadPackageProtocol> handler;
28+
2629
@end

GoogleDataTransport/GDTLibrary/Public/GDTUploadPackage.h

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,65 @@
1616

1717
#import <Foundation/Foundation.h>
1818

19+
#import <GoogleDataTransport/GDTTargets.h>
20+
21+
@class GDTClock;
1922
@class GDTStoredEvent;
23+
@class GDTUploadPackage;
24+
25+
/** A protocol that allows a handler to respond to package lifecycle events. */
26+
@protocol GDTUploadPackageProtocol <NSObject>
27+
28+
@optional
29+
30+
/** Indicates that the package has expired.
31+
*
32+
* @note Package expiration will only be checked every 5 seconds.
33+
*
34+
* @param package The package that has expired.
35+
*/
36+
- (void)packageExpired:(GDTUploadPackage *)package;
37+
38+
/** Indicates that the package was successfully delivered.
39+
*
40+
* @param package The package that was delivered.
41+
*/
42+
- (void)packageDelivered:(GDTUploadPackage *)package successful:(BOOL)successful;
43+
44+
@end
2045

2146
/** This class is a container that's handed off to uploaders. */
22-
@interface GDTUploadPackage : NSObject
47+
@interface GDTUploadPackage : NSObject <NSSecureCoding>
2348

2449
/** The set of stored events in this upload package. */
2550
@property(nonatomic) NSSet<GDTStoredEvent *> *events;
2651

52+
/** The expiration time. If [[GDTClock snapshot] isAfter:deliverByTime] this package has expired.
53+
*
54+
* @note By default, the expiration time will be 3 minutes from creation.
55+
*/
56+
@property(nonatomic) GDTClock *deliverByTime;
57+
58+
/** The target of this package. */
59+
@property(nonatomic, readonly) GDTTarget target;
60+
61+
/** Initializes a package instance.
62+
*
63+
* @param target The target/destination of this package.
64+
* @return An instance of this class.
65+
*/
66+
- (instancetype)initWithTarget:(GDTTarget)target NS_DESIGNATED_INITIALIZER;
67+
68+
// Please use the designated initializer.
69+
- (instancetype)init NS_UNAVAILABLE;
70+
71+
/** Completes delivery of the package.
72+
*
73+
* @note This *needs* to be called by an uploader for the package to not expire.
74+
*/
75+
- (void)completeDelivery;
76+
77+
/** Sends the package back, indicating that delivery should be attempted again in the future. */
78+
- (void)retryDeliveryInTheFuture;
79+
2780
@end

GoogleDataTransport/GDTLibrary/Public/GDTUploader.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
#import <GoogleDataTransport/GDTClock.h>
2020
#import <GoogleDataTransport/GDTLifecycle.h>
2121
#import <GoogleDataTransport/GDTTargets.h>
22-
23-
@class GDTUploadPackage;
22+
#import <GoogleDataTransport/GDTUploadPackage.h>
2423

2524
NS_ASSUME_NONNULL_BEGIN
2625

GoogleDataTransport/GDTTests/Integration/Helpers/GDTIntegrationTestPrioritizer.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ - (void)unprioritizeEvents:(NSSet<GDTStoredEvent *> *)events {
6969

7070
- (GDTUploadPackage *)uploadPackageWithConditions:(GDTUploadConditions)conditions {
7171
__block GDTIntegrationTestUploadPackage *uploadPackage =
72-
[[GDTIntegrationTestUploadPackage alloc] init];
72+
[[GDTIntegrationTestUploadPackage alloc] initWithTarget:kGDTIntegrationTestTarget];
7373
dispatch_sync(_queue, ^{
7474
if ((conditions & GDTUploadConditionWifiData) == GDTUploadConditionWifiData) {
7575
uploadPackage.events = [self.wifiOnlyEvents setByAddingObjectsFromSet:self.nonWifiEvents];

GoogleDataTransport/GDTTests/Lifecycle/Helpers/GDTLifecycleTestPrioritizer.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ - (void)unprioritizeEvents:(NSSet<GDTStoredEvent *> *)events {
5555
}
5656

5757
- (GDTUploadPackage *)uploadPackageWithConditions:(GDTUploadConditions)conditions {
58-
__block GDTUploadPackage *uploadPackage = [[GDTUploadPackage alloc] init];
58+
__block GDTUploadPackage *uploadPackage =
59+
[[GDTUploadPackage alloc] initWithTarget:kGDTTargetTest];
5960
dispatch_sync(_queue, ^{
6061
uploadPackage.events = self.events;
6162
});

GoogleDataTransport/GDTTests/Unit/GDTUploadCoordinatorTest.m

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,18 @@ @interface GDTUploadCoordinatorTest : GDTTestCase
4040
/** A test uploader. */
4141
@property(nonatomic) GDTTestUploader *uploader;
4242

43-
/** A target for the prioritizer and uploader to use. */
44-
@property(nonatomic) GDTTarget target;
45-
4643
@end
4744

4845
@implementation GDTUploadCoordinatorTest
4946

5047
- (void)setUp {
5148
[super setUp];
5249
self.storageFake = [[GDTStorageFake alloc] init];
53-
self.target = 42;
5450
self.prioritizer = [[GDTTestPrioritizer alloc] init];
5551
self.uploader = [[GDTTestUploader alloc] init];
5652

57-
[[GDTRegistrar sharedInstance] registerPrioritizer:_prioritizer target:_target];
58-
[[GDTRegistrar sharedInstance] registerUploader:_uploader target:_target];
53+
[[GDTRegistrar sharedInstance] registerPrioritizer:_prioritizer target:kGDTTargetTest];
54+
[[GDTRegistrar sharedInstance] registerUploader:_uploader target:kGDTTargetTest];
5955

6056
GDTUploadCoordinator *uploadCoordinator = [GDTUploadCoordinator sharedInstance];
6157
uploadCoordinator.storage = self.storageFake;
@@ -81,15 +77,16 @@ - (void)testSharedInstance {
8177

8278
/** Tests that forcing a event upload works. */
8379
- (void)testForceUploadEvents {
84-
GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init];
80+
GDTTestUploadPackage *uploadPackage =
81+
[[GDTTestUploadPackage alloc] initWithTarget:kGDTTargetTest];
8582
uploadPackage.events = [GDTEventGenerator generate3StoredEvents];
8683
self.prioritizer.uploadPackage = uploadPackage;
8784
XCTestExpectation *expectation = [self expectationWithDescription:@"uploader will upload"];
8885
self.uploader.uploadEventsBlock =
8986
^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) {
9087
[expectation fulfill];
9188
};
92-
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadForTarget:_target]);
89+
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadForTarget:kGDTTargetTest]);
9390
[self waitForExpectations:@[ expectation ] timeout:0.1];
9491
}
9592

@@ -102,18 +99,19 @@ - (void)testForceUploadEventsEnqueuesIftargetAlreadyHasEventsInFlight {
10299
^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) {
103100
[expectation fulfill];
104101
};
105-
GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init];
102+
GDTTestUploadPackage *uploadPackage =
103+
[[GDTTestUploadPackage alloc] initWithTarget:kGDTTargetTest];
106104
uploadPackage.events = [GDTEventGenerator generate3StoredEvents];
107105
self.prioritizer.uploadPackage = uploadPackage;
108106
dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{
109-
[GDTUploadCoordinator sharedInstance].targetToInFlightEventSet[@(self->_target)] =
107+
[GDTUploadCoordinator sharedInstance].targetToInFlightEventSet[@(kGDTTargetTest)] =
110108
[[NSSet alloc] init];
111109
});
112-
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadForTarget:_target]);
110+
XCTAssertNoThrow([[GDTUploadCoordinator sharedInstance] forceUploadForTarget:kGDTTargetTest]);
113111
dispatch_sync([GDTUploadCoordinator sharedInstance].coordinationQueue, ^{
114112
XCTAssertEqual([GDTUploadCoordinator sharedInstance].forcedUploadQueue.count, 1);
115113
[GDTUploadCoordinator sharedInstance].onCompleteBlock(
116-
self.target, [GDTClock clockSnapshotInTheFuture:1000], nil);
114+
kGDTTargetTest, [GDTClock clockSnapshotInTheFuture:1000], nil);
117115
});
118116
[self waitForExpectations:@[ expectation ] timeout:1.0];
119117
}
@@ -144,14 +142,13 @@ - (void)testTimerIsRunningAtDesiredFrequency {
144142
/** Tests uploading events via the coordinator timer. */
145143
- (void)testUploadingEventsViaTimer {
146144
__block int uploadAttempts = 0;
147-
__weak GDTUploadCoordinatorTest *weakSelf = self;
148-
GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init];
145+
GDTTestUploadPackage *uploadPackage =
146+
[[GDTTestUploadPackage alloc] initWithTarget:kGDTTargetTest];
149147
uploadPackage.events = [GDTEventGenerator generate3StoredEvents];
150148
self.prioritizer.uploadPackage = uploadPackage;
151149
self.uploader.uploadEventsBlock =
152150
^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) {
153-
GDTUploadCoordinatorTest *strongSelf = weakSelf;
154-
completionBlock(strongSelf->_target, [GDTClock clockSnapshotInTheFuture:100], nil);
151+
completionBlock(kGDTTargetTest, [GDTClock clockSnapshotInTheFuture:100], nil);
155152
uploadAttempts++;
156153
};
157154
[GDTUploadCoordinator sharedInstance].timerInterval = NSEC_PER_SEC / 10;
@@ -169,15 +166,14 @@ - (void)testUploadingEventsViaTimer {
169166
/** Tests the situation in which the uploader failed to upload the events for some reason. */
170167
- (void)testThatAFailedUploadResultsInAnEventualRetry {
171168
__block int uploadAttempts = 0;
172-
__weak GDTUploadCoordinatorTest *weakSelf = self;
173-
GDTTestUploadPackage *uploadPackage = [[GDTTestUploadPackage alloc] init];
169+
GDTTestUploadPackage *uploadPackage =
170+
[[GDTTestUploadPackage alloc] initWithTarget:kGDTTargetTest];
174171
uploadPackage.events = [GDTEventGenerator generate3StoredEvents];
175172
self.prioritizer.uploadPackage = uploadPackage;
176173
self.uploader.uploadEventsBlock =
177174
^(GDTUploadPackage *_Nonnull package, GDTUploaderCompletionBlock _Nonnull completionBlock) {
178-
GDTUploadCoordinatorTest *strongSelf = weakSelf;
179175
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:1337 userInfo:nil];
180-
completionBlock(strongSelf->_target, [GDTClock clockSnapshotInTheFuture:100], error);
176+
completionBlock(kGDTTargetTest, [GDTClock clockSnapshotInTheFuture:100], error);
181177
uploadAttempts++;
182178
};
183179
[GDTUploadCoordinator sharedInstance].timerInterval = NSEC_PER_SEC / 10;

0 commit comments

Comments
 (0)