diff --git a/GoogleDataTransport/GDTLibrary/GDTStorage.m b/GoogleDataTransport/GDTLibrary/GDTStorage.m index ea73f1aebb4..623630c0df8 100644 --- a/GoogleDataTransport/GDTLibrary/GDTStorage.m +++ b/GoogleDataTransport/GDTLibrary/GDTStorage.m @@ -111,7 +111,16 @@ - (void)storeEvent:(GDTEvent *)event { // If running in the background, save state to disk and end the associated background task. if (bgID != GDTBackgroundIdentifierInvalid) { - [NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]]; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self + requiringSecureCoding:YES + error:nil]; + [data writeToFile:[GDTStorage archivePath] atomically:YES]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + [NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]]; +#endif + } [[GDTApplication sharedApplication] endBackgroundTask:bgID]; } }); @@ -193,13 +202,29 @@ - (void)addEventToTrackingCollections:(GDTStoredEvent *)event { #pragma mark - GDTLifecycleProtocol - (void)appWillForeground:(GDTApplication *)app { - [NSKeyedUnarchiver unarchiveObjectWithFile:[GDTStorage archivePath]]; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + NSData *data = [NSData dataWithContentsOfFile:[GDTStorage archivePath]]; + [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTStorage class] fromData:data error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + [NSKeyedUnarchiver unarchiveObjectWithFile:[GDTStorage archivePath]]; +#endif + } self->_runningInBackground = NO; } - (void)appWillBackground:(GDTApplication *)app { self->_runningInBackground = YES; - [NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]]; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self + requiringSecureCoding:YES + error:nil]; + [data writeToFile:[GDTStorage archivePath] atomically:YES]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + [NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]]; +#endif + } // Create an immediate background task to run until the end of the current queue of work. __block GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgID]; @@ -210,7 +235,16 @@ - (void)appWillBackground:(GDTApplication *)app { } - (void)appWillTerminate:(GDTApplication *)application { - [NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]]; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self + requiringSecureCoding:YES + error:nil]; + [data writeToFile:[GDTStorage archivePath] atomically:YES]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + [NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]]; +#endif + } } #pragma mark - NSSecureCoding @@ -232,11 +266,14 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { // Create the singleton and populate its ivars. GDTStorage *sharedInstance = [self.class sharedInstance]; dispatch_sync(sharedInstance.storageQueue, ^{ - sharedInstance->_storedEvents = [aDecoder decodeObjectOfClass:[NSMutableOrderedSet class] - forKey:kGDTStorageStoredEventsKey]; + NSSet *classes = + [NSSet setWithObjects:[NSMutableOrderedSet class], [GDTStoredEvent class], nil]; + sharedInstance->_storedEvents = [aDecoder decodeObjectOfClasses:classes + forKey:kGDTStorageStoredEventsKey]; + classes = [NSSet setWithObjects:[NSMutableDictionary class], [NSMutableSet class], + [GDTStoredEvent class], nil]; sharedInstance->_targetToEventSet = - [aDecoder decodeObjectOfClass:[NSMutableDictionary class] - forKey:kGDTStorageTargetToEventSetKey]; + [aDecoder decodeObjectOfClasses:classes forKey:kGDTStorageTargetToEventSetKey]; sharedInstance->_uploadCoordinator = [aDecoder decodeObjectOfClass:[GDTUploadCoordinator class] forKey:kGDTStorageUploadCoordinatorKey]; diff --git a/GoogleDataTransport/GDTLibrary/GDTUploadPackage.m b/GoogleDataTransport/GDTLibrary/GDTUploadPackage.m index 36164acdb8d..24d3b6e5441 100644 --- a/GoogleDataTransport/GDTLibrary/GDTUploadPackage.m +++ b/GoogleDataTransport/GDTLibrary/GDTUploadPackage.m @@ -18,6 +18,7 @@ #import #import +#import #import "GDTLibrary/Private/GDTStorage_Private.h" #import "GDTLibrary/Private/GDTUploadCoordinator.h" @@ -140,11 +141,12 @@ - (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder { GDTTarget target = [aDecoder decodeIntegerForKey:kTargetKey]; self = [self initWithTarget:target]; if (self) { - _events = [aDecoder decodeObjectOfClass:[NSSet class] forKey:kEventsKey]; + NSSet *classes = [NSSet setWithObjects:[NSSet class], [GDTStoredEvent class], nil]; + _events = [aDecoder decodeObjectOfClasses:classes forKey:kEventsKey]; _deliverByTime = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:kDeliverByTimeKey]; _isHandled = [aDecoder decodeBoolForKey:kIsHandledKey]; - // Isn't technically NSSecureCoding, because we don't know the class of this object. - _handler = [aDecoder decodeObjectForKey:kHandlerKey]; + // _handler isn't technically NSSecureCoding, because we don't know the class of this object. + // but it gets decoded anyway. } return self; } diff --git a/GoogleDataTransport/GDTTests/Unit/GDTClockTest.m b/GoogleDataTransport/GDTTests/Unit/GDTClockTest.m index 8f72775291c..a6127267732 100644 --- a/GoogleDataTransport/GDTTests/Unit/GDTClockTest.m +++ b/GoogleDataTransport/GDTTests/Unit/GDTClockTest.m @@ -51,8 +51,20 @@ - (void)testSupportsSecureEncoding { /** Tests encoding and decoding a clock using a keyed archiver. */ - (void)testEncoding { GDTClock *clock = [GDTClock snapshot]; - NSData *clockData = [NSKeyedArchiver archivedDataWithRootObject:clock]; - GDTClock *unarchivedClock = [NSKeyedUnarchiver unarchiveObjectWithData:clockData]; + GDTClock *unarchivedClock; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + NSData *clockData = [NSKeyedArchiver archivedDataWithRootObject:clock + requiringSecureCoding:YES + error:nil]; + unarchivedClock = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTClock class] + fromData:clockData + error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + NSData *clockData = [NSKeyedArchiver archivedDataWithRootObject:clock]; + unarchivedClock = [NSKeyedUnarchiver unarchiveObjectWithData:clockData]; +#endif + } XCTAssertEqual([clock hash], [unarchivedClock hash]); XCTAssertEqualObjects(clock, unarchivedClock); } diff --git a/GoogleDataTransport/GDTTests/Unit/GDTEventTest.m b/GoogleDataTransport/GDTTests/Unit/GDTEventTest.m index 890ffd009a3..3609e3aa50d 100644 --- a/GoogleDataTransport/GDTTests/Unit/GDTEventTest.m +++ b/GoogleDataTransport/GDTTests/Unit/GDTEventTest.m @@ -43,12 +43,28 @@ - (void)testArchiving { event.qosTier = GDTEventQoSTelemetry; event.clockSnapshot = clockSnapshot; - NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:event]; - + NSData *archiveData; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + archiveData = [NSKeyedArchiver archivedDataWithRootObject:event + requiringSecureCoding:YES + error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + archiveData = [NSKeyedArchiver archivedDataWithRootObject:event]; +#endif + } // To ensure that all the objects being retained by the original event are dealloc'd. event = nil; - - GDTEvent *decodedEvent = [NSKeyedUnarchiver unarchiveObjectWithData:archiveData]; + GDTEvent *decodedEvent; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + decodedEvent = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTEvent class] + fromData:archiveData + error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + decodedEvent = [NSKeyedUnarchiver unarchiveObjectWithData:archiveData]; +#endif + } XCTAssertEqualObjects(decodedEvent.mappingID, @"testID"); XCTAssertEqual(decodedEvent.target, 42); XCTAssertEqualObjects(decodedEvent.dataObjectTransportBytes, diff --git a/GoogleDataTransport/GDTTests/Unit/GDTStorageTest.m b/GoogleDataTransport/GDTTests/Unit/GDTStorageTest.m index fd05bffc9aa..32828e89fca 100644 --- a/GoogleDataTransport/GDTTests/Unit/GDTStorageTest.m +++ b/GoogleDataTransport/GDTTests/Unit/GDTStorageTest.m @@ -83,6 +83,7 @@ - (void)testStoreEvent { @autoreleasepool { GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target]; event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding]; + event.clockSnapshot = [GDTClock snapshot]; XCTAssertNoThrow([[GDTStorage sharedInstance] storeEvent:event]); } dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{ @@ -103,6 +104,7 @@ - (void)testRemoveEvent { @autoreleasepool { GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target]; event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding]; + event.clockSnapshot = [GDTClock snapshot]; XCTAssertNoThrow([[GDTStorage sharedInstance] storeEvent:event]); } __block NSURL *eventFile; @@ -223,7 +225,7 @@ - (void)testEventDeallocationIsEnforced { GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target]; weakEvent = event; event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding]; - + event.clockSnapshot = [GDTClock snapshot]; // Store the event and wait for the expectation. [[GDTStorage sharedInstance] storeEvent:event]; GDTDataFuture *dataFuture = @@ -256,10 +258,20 @@ - (void)testEventDeallocationIsEnforced { - (void)testNSSecureCoding { XCTAssertTrue([GDTStorage supportsSecureCoding]); GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target]; + event.clockSnapshot = [GDTClock snapshot]; event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding]; XCTAssertNoThrow([[GDTStorage sharedInstance] storeEvent:event]); event = nil; - NSData *storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance]]; + NSData *storageData; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance] + requiringSecureCoding:YES + error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance]]; +#endif + } dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{ XCTAssertNotNil([[GDTStorage sharedInstance].storedEvents lastObject]); }); @@ -267,8 +279,17 @@ - (void)testNSSecureCoding { dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{ XCTAssertNil([[GDTStorage sharedInstance].storedEvents lastObject]); }); - - GDTStorage *unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData]; + GDTStorage *unarchivedStorage; + NSError *error; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + unarchivedStorage = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTStorage class] + fromData:storageData + error:&error]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData]; +#endif + } XCTAssertNotNil([unarchivedStorage.storedEvents lastObject]); } @@ -276,9 +297,19 @@ - (void)testNSSecureCoding { - (void)testNSSecureCodingWithSharedInstance { GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target]; event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding]; + event.clockSnapshot = [GDTClock snapshot]; XCTAssertNoThrow([[GDTStorage sharedInstance] storeEvent:event]); event = nil; - NSData *storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance]]; + NSData *storageData; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance] + requiringSecureCoding:YES + error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTStorage sharedInstance]]; +#endif + } dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{ XCTAssertNotNil([[GDTStorage sharedInstance].storedEvents lastObject]); }); @@ -286,8 +317,16 @@ - (void)testNSSecureCodingWithSharedInstance { dispatch_sync([GDTStorage sharedInstance].storageQueue, ^{ XCTAssertNil([[GDTStorage sharedInstance].storedEvents lastObject]); }); - - GDTStorage *unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData]; + GDTStorage *unarchivedStorage; + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + unarchivedStorage = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTStorage class] + fromData:storageData + error:nil]; + } else { +#if !defined(TARGET_OS_MACCATALYST) + unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData]; +#endif + } XCTAssertNotNil([unarchivedStorage.storedEvents lastObject]); } @@ -298,6 +337,7 @@ - (void)testQoSTierFast { GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"404" target:target]; event.dataObjectTransportBytes = [@"testString" dataUsingEncoding:NSUTF8StringEncoding]; event.qosTier = GDTEventQoSFast; + event.clockSnapshot = [GDTClock snapshot]; XCTAssertFalse(self.uploaderFake.forceUploadCalled); XCTAssertNoThrow([[GDTStorage sharedInstance] storeEvent:event]); } diff --git a/GoogleDataTransport/GDTTests/Unit/GDTUploadPackageTest.m b/GoogleDataTransport/GDTTests/Unit/GDTUploadPackageTest.m index 45b425a1de0..cdaeb0dc118 100644 --- a/GoogleDataTransport/GDTTests/Unit/GDTUploadPackageTest.m +++ b/GoogleDataTransport/GDTTests/Unit/GDTUploadPackageTest.m @@ -110,9 +110,23 @@ - (void)testEncoding { NSMutableSet *set = [GDTEventGenerator generate3StoredEvents]; uploadPackage.events = set; uploadPackage.handler = self; - - NSData *packageData = [NSKeyedArchiver archivedDataWithRootObject:uploadPackage]; - GDTUploadPackage *recreatedPackage = [NSKeyedUnarchiver unarchiveObjectWithData:packageData]; + GDTUploadPackage *recreatedPackage; + NSError *error; + + if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { + NSData *packageData = [NSKeyedArchiver archivedDataWithRootObject:uploadPackage + requiringSecureCoding:YES + error:&error]; + recreatedPackage = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTUploadPackage class] + fromData:packageData + error:&error]; + XCTAssertNil(error); + } else { +#if !defined(TARGET_OS_MACCATALYST) + NSData *packageData = [NSKeyedArchiver archivedDataWithRootObject:uploadPackage]; + recreatedPackage = [NSKeyedUnarchiver unarchiveObjectWithData:packageData]; +#endif + } XCTAssertEqualObjects(uploadPackage, recreatedPackage); }