2019-04-12 19:27:00 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright 2018 Google
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
#import "GDTCORLibrary/Private/GDTCORStorage.h"
|
|
|
|
|
#import "GDTCORLibrary/Private/GDTCORStorage_Private.h"
|
2019-04-12 19:27:00 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
#import <GoogleDataTransport/GDTCORAssert.h>
|
|
|
|
|
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
|
|
|
|
|
#import <GoogleDataTransport/GDTCORLifecycle.h>
|
|
|
|
|
#import <GoogleDataTransport/GDTCORPrioritizer.h>
|
|
|
|
|
#import <GoogleDataTransport/GDTCORStoredEvent.h>
|
2019-04-12 19:27:00 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
#import "GDTCORLibrary/Private/GDTCOREvent_Private.h"
|
|
|
|
|
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
|
|
|
|
|
#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
/** Creates and/or returns a singleton NSString that is the shared storage path.
|
|
|
|
|
*
|
|
|
|
|
* @return The SDK event storage path.
|
|
|
|
|
*/
|
2019-09-09 15:21:29 -07:00
|
|
|
static NSString *GDTCORStoragePath() {
|
2019-04-12 19:27:00 -07:00
|
|
|
static NSString *storagePath;
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
NSString *cachePath =
|
|
|
|
|
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
|
|
|
|
|
storagePath = [NSString stringWithFormat:@"%@/google-sdks-events", cachePath];
|
|
|
|
|
});
|
|
|
|
|
return storagePath;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
@implementation GDTCORStorage
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
+ (NSString *)archivePath {
|
|
|
|
|
static NSString *archivePath;
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
2019-09-09 15:21:29 -07:00
|
|
|
archivePath = [GDTCORStoragePath() stringByAppendingPathComponent:@"GDTCORStorageArchive"];
|
2019-04-12 19:27:00 -07:00
|
|
|
});
|
|
|
|
|
return archivePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (instancetype)sharedInstance {
|
2019-09-09 15:21:29 -07:00
|
|
|
static GDTCORStorage *sharedStorage;
|
2019-04-12 19:27:00 -07:00
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
2019-09-09 15:21:29 -07:00
|
|
|
sharedStorage = [[GDTCORStorage alloc] init];
|
2019-04-12 19:27:00 -07:00
|
|
|
});
|
|
|
|
|
return sharedStorage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (instancetype)init {
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self) {
|
2019-09-09 15:21:29 -07:00
|
|
|
_storageQueue = dispatch_queue_create("com.google.GDTCORStorage", DISPATCH_QUEUE_SERIAL);
|
2019-04-12 19:27:00 -07:00
|
|
|
_targetToEventSet = [[NSMutableDictionary alloc] init];
|
|
|
|
|
_storedEvents = [[NSMutableOrderedSet alloc] init];
|
2019-09-09 15:21:29 -07:00
|
|
|
_uploadCoordinator = [GDTCORUploadCoordinator sharedInstance];
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)storeEvent:(GDTCOREvent *)event {
|
2019-09-03 20:37:16 -07:00
|
|
|
if (event == nil) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-12 19:27:00 -07:00
|
|
|
[self createEventDirectoryIfNotExists];
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
__block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid;
|
2019-10-02 10:40:45 -04:00
|
|
|
bgID = [[GDTCORApplication sharedApplication]
|
|
|
|
|
beginBackgroundTaskWithName:@"GDTStorage"
|
|
|
|
|
expirationHandler:^{
|
|
|
|
|
// End the background task if it's still valid.
|
|
|
|
|
[[GDTCORApplication sharedApplication] endBackgroundTask:bgID];
|
|
|
|
|
bgID = GDTCORBackgroundIdentifierInvalid;
|
|
|
|
|
}];
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
dispatch_async(_storageQueue, ^{
|
|
|
|
|
// Check that a backend implementation is available for this target.
|
|
|
|
|
NSInteger target = event.target;
|
|
|
|
|
|
|
|
|
|
// Check that a prioritizer is available for this target.
|
2019-09-09 15:21:29 -07:00
|
|
|
id<GDTCORPrioritizer> prioritizer =
|
|
|
|
|
[GDTCORRegistrar sharedInstance].targetToPrioritizer[@(target)];
|
|
|
|
|
GDTCORAssert(prioritizer, @"There's no prioritizer registered for the given target.");
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
// Write the transport bytes to disk, get a filename.
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORAssert(event.dataObjectTransportBytes, @"The event should have been serialized to bytes");
|
2019-04-12 19:27:00 -07:00
|
|
|
NSURL *eventFile = [self saveEventBytesToDisk:event.dataObjectTransportBytes
|
|
|
|
|
eventHash:event.hash];
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORDataFuture *dataFuture = [[GDTCORDataFuture alloc] initWithFileURL:eventFile];
|
|
|
|
|
GDTCORStoredEvent *storedEvent = [event storedEventWithDataFuture:dataFuture];
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
// Add event to tracking collections.
|
|
|
|
|
[self addEventToTrackingCollections:storedEvent];
|
|
|
|
|
|
|
|
|
|
// Have the prioritizer prioritize the event.
|
|
|
|
|
[prioritizer prioritizeEvent:storedEvent];
|
|
|
|
|
|
|
|
|
|
// Check the QoS, if it's high priority, notify the target that it has a high priority event.
|
2019-09-09 15:21:29 -07:00
|
|
|
if (event.qosTier == GDTCOREventQoSFast) {
|
2019-05-16 12:58:42 -07:00
|
|
|
[self.uploadCoordinator forceUploadForTarget:target];
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 10:40:45 -04:00
|
|
|
// Write state to disk if we're in the background.
|
|
|
|
|
if ([[GDTCORApplication sharedApplication] isRunningInBackground]) {
|
2019-08-12 16:19:37 -07:00
|
|
|
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
|
2019-09-23 15:41:58 -07:00
|
|
|
NSError *error;
|
2019-08-12 16:19:37 -07:00
|
|
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
|
|
|
|
|
requiringSecureCoding:YES
|
2019-09-23 15:41:58 -07:00
|
|
|
error:&error];
|
2019-09-09 15:21:29 -07:00
|
|
|
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
|
2019-08-12 16:19:37 -07:00
|
|
|
} else {
|
2019-09-30 16:37:21 -07:00
|
|
|
#if !TARGET_OS_MACCATALYST
|
2019-09-09 15:21:29 -07:00
|
|
|
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
|
2019-08-12 16:19:37 -07:00
|
|
|
#endif
|
|
|
|
|
}
|
2019-08-19 21:00:53 -07:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 10:40:45 -04:00
|
|
|
// Cancel or end the associated background task if it's still valid.
|
|
|
|
|
[[GDTCORApplication sharedApplication] endBackgroundTask:bgID];
|
|
|
|
|
bgID = GDTCORBackgroundIdentifierInvalid;
|
2019-04-12 19:27:00 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)removeEvents:(NSSet<GDTCORStoredEvent *> *)events {
|
|
|
|
|
NSSet<GDTCORStoredEvent *> *eventsToRemove = [events copy];
|
2019-04-12 19:27:00 -07:00
|
|
|
dispatch_async(_storageQueue, ^{
|
2019-09-09 15:21:29 -07:00
|
|
|
for (GDTCORStoredEvent *event in eventsToRemove) {
|
2019-04-12 19:27:00 -07:00
|
|
|
// Remove from disk, first and foremost.
|
|
|
|
|
NSError *error;
|
2019-04-24 15:54:51 -07:00
|
|
|
if (event.dataFuture.fileURL) {
|
|
|
|
|
NSURL *fileURL = event.dataFuture.fileURL;
|
|
|
|
|
[[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORAssert(error == nil, @"There was an error removing an event file: %@", error);
|
2019-04-24 15:54:51 -07:00
|
|
|
}
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
// Remove from the tracking collections.
|
|
|
|
|
[self.storedEvents removeObject:event];
|
|
|
|
|
[self.targetToEventSet[event.target] removeObject:event];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Private helper methods
|
|
|
|
|
|
|
|
|
|
/** Creates the storage directory if it does not exist. */
|
|
|
|
|
- (void)createEventDirectoryIfNotExists {
|
|
|
|
|
NSError *error;
|
2019-09-09 15:21:29 -07:00
|
|
|
BOOL result = [[NSFileManager defaultManager] createDirectoryAtPath:GDTCORStoragePath()
|
2019-04-12 19:27:00 -07:00
|
|
|
withIntermediateDirectories:YES
|
|
|
|
|
attributes:0
|
|
|
|
|
error:&error];
|
|
|
|
|
if (!result || error) {
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORLogError(GDTCORMCEDirectoryCreationError, @"Error creating the directory: %@", error);
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Saves the event's dataObjectTransportBytes to a file using NSData mechanisms.
|
|
|
|
|
*
|
|
|
|
|
* @note This method should only be called from a method within a block on _storageQueue to maintain
|
|
|
|
|
* thread safety.
|
|
|
|
|
*
|
|
|
|
|
* @param transportBytes The transport bytes of the event.
|
|
|
|
|
* @param eventHash The hash value of the event.
|
|
|
|
|
* @return The filename
|
|
|
|
|
*/
|
|
|
|
|
- (NSURL *)saveEventBytesToDisk:(NSData *)transportBytes eventHash:(NSUInteger)eventHash {
|
2019-09-09 15:21:29 -07:00
|
|
|
NSString *storagePath = GDTCORStoragePath();
|
2019-04-12 19:27:00 -07:00
|
|
|
NSString *event = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash];
|
|
|
|
|
NSURL *eventFilePath = [NSURL fileURLWithPath:[storagePath stringByAppendingPathComponent:event]];
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORAssert(![[NSFileManager defaultManager] fileExistsAtPath:eventFilePath.path],
|
|
|
|
|
@"An event shouldn't already exist at this path: %@", eventFilePath.path);
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
BOOL writingSuccess = [transportBytes writeToURL:eventFilePath atomically:YES];
|
|
|
|
|
if (!writingSuccess) {
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORLogError(GDTCORMCEFileWriteError, @"An event file could not be written: %@",
|
|
|
|
|
eventFilePath);
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return eventFilePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Adds the event to internal tracking collections.
|
|
|
|
|
*
|
|
|
|
|
* @note This method should only be called from a method within a block on _storageQueue to maintain
|
|
|
|
|
* thread safety.
|
|
|
|
|
*
|
|
|
|
|
* @param event The event to track.
|
|
|
|
|
*/
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)addEventToTrackingCollections:(GDTCORStoredEvent *)event {
|
2019-04-12 19:27:00 -07:00
|
|
|
[_storedEvents addObject:event];
|
2019-09-09 15:21:29 -07:00
|
|
|
NSMutableSet<GDTCORStoredEvent *> *events = self.targetToEventSet[event.target];
|
2019-04-12 19:27:00 -07:00
|
|
|
events = events ? events : [[NSMutableSet alloc] init];
|
|
|
|
|
[events addObject:event];
|
|
|
|
|
_targetToEventSet[event.target] = events;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
#pragma mark - GDTCORLifecycleProtocol
|
2019-04-12 19:27:00 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)appWillForeground:(GDTCORApplication *)app {
|
2019-08-12 16:19:37 -07:00
|
|
|
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
|
2019-09-23 15:41:58 -07:00
|
|
|
NSError *error;
|
2019-09-09 15:21:29 -07:00
|
|
|
NSData *data = [NSData dataWithContentsOfFile:[GDTCORStorage archivePath]];
|
2019-10-10 13:30:34 -07:00
|
|
|
if (data) {
|
|
|
|
|
[NSKeyedUnarchiver unarchivedObjectOfClass:[GDTCORStorage class] fromData:data error:&error];
|
|
|
|
|
}
|
2019-08-12 16:19:37 -07:00
|
|
|
} else {
|
2019-09-30 16:37:21 -07:00
|
|
|
#if !TARGET_OS_MACCATALYST
|
2019-09-09 15:21:29 -07:00
|
|
|
[NSKeyedUnarchiver unarchiveObjectWithFile:[GDTCORStorage archivePath]];
|
2019-08-12 16:19:37 -07:00
|
|
|
#endif
|
|
|
|
|
}
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)appWillBackground:(GDTCORApplication *)app {
|
2019-04-12 19:27:00 -07:00
|
|
|
dispatch_async(_storageQueue, ^{
|
2019-10-02 10:40:45 -04:00
|
|
|
// Immediately request a background task to run until the end of the current queue of work, and
|
|
|
|
|
// cancel it once the work is done.
|
|
|
|
|
__block GDTCORBackgroundIdentifier bgID =
|
|
|
|
|
[app beginBackgroundTaskWithName:@"GDTStorage"
|
|
|
|
|
expirationHandler:^{
|
|
|
|
|
[app endBackgroundTask:bgID];
|
|
|
|
|
bgID = GDTCORBackgroundIdentifierInvalid;
|
|
|
|
|
}];
|
|
|
|
|
|
2019-08-19 14:23:03 -07:00
|
|
|
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
|
2019-09-23 15:41:58 -07:00
|
|
|
NSError *error;
|
2019-08-19 14:23:03 -07:00
|
|
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
|
|
|
|
|
requiringSecureCoding:YES
|
2019-09-23 15:41:58 -07:00
|
|
|
error:&error];
|
2019-09-09 15:21:29 -07:00
|
|
|
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
|
2019-08-19 14:23:03 -07:00
|
|
|
} else {
|
2019-09-30 16:37:21 -07:00
|
|
|
#if !TARGET_OS_MACCATALYST
|
2019-09-09 15:21:29 -07:00
|
|
|
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
|
2019-08-19 14:23:03 -07:00
|
|
|
#endif
|
|
|
|
|
}
|
2019-08-19 21:00:53 -07:00
|
|
|
|
2019-10-02 10:40:45 -04:00
|
|
|
// End the background task if it's still valid.
|
|
|
|
|
[app endBackgroundTask:bgID];
|
|
|
|
|
bgID = GDTCORBackgroundIdentifierInvalid;
|
2019-04-12 19:27:00 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)appWillTerminate:(GDTCORApplication *)application {
|
2019-08-12 16:19:37 -07:00
|
|
|
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
|
2019-09-23 15:41:58 -07:00
|
|
|
NSError *error;
|
2019-08-12 16:19:37 -07:00
|
|
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
|
|
|
|
|
requiringSecureCoding:YES
|
2019-09-23 15:41:58 -07:00
|
|
|
error:&error];
|
2019-09-09 15:21:29 -07:00
|
|
|
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
|
2019-08-12 16:19:37 -07:00
|
|
|
} else {
|
2019-09-30 16:37:21 -07:00
|
|
|
#if !TARGET_OS_MACCATALYST
|
2019-09-09 15:21:29 -07:00
|
|
|
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
|
2019-08-12 16:19:37 -07:00
|
|
|
#endif
|
|
|
|
|
}
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - NSSecureCoding
|
|
|
|
|
|
|
|
|
|
/** The NSKeyedCoder key for the storedEvents property. */
|
2019-09-09 15:21:29 -07:00
|
|
|
static NSString *const kGDTCORStorageStoredEventsKey = @"GDTCORStorageStoredEventsKey";
|
2019-04-12 19:27:00 -07:00
|
|
|
|
|
|
|
|
/** The NSKeyedCoder key for the targetToEventSet property. */
|
2019-09-09 15:21:29 -07:00
|
|
|
static NSString *const kGDTCORStorageTargetToEventSetKey = @"GDTCORStorageTargetToEventSetKey";
|
2019-04-12 19:27:00 -07:00
|
|
|
|
2019-05-16 12:58:42 -07:00
|
|
|
/** The NSKeyedCoder key for the uploadCoordinator property. */
|
2019-09-09 15:21:29 -07:00
|
|
|
static NSString *const kGDTCORStorageUploadCoordinatorKey = @"GDTCORStorageUploadCoordinatorKey";
|
2019-05-16 12:58:42 -07:00
|
|
|
|
2019-04-12 19:27:00 -07:00
|
|
|
+ (BOOL)supportsSecureCoding {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
|
|
|
|
// Create the singleton and populate its ivars.
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORStorage *sharedInstance = [self.class sharedInstance];
|
2019-04-12 19:27:00 -07:00
|
|
|
dispatch_sync(sharedInstance.storageQueue, ^{
|
2019-08-12 16:19:37 -07:00
|
|
|
NSSet *classes =
|
2019-09-09 15:21:29 -07:00
|
|
|
[NSSet setWithObjects:[NSMutableOrderedSet class], [GDTCORStoredEvent class], nil];
|
2019-08-12 16:19:37 -07:00
|
|
|
sharedInstance->_storedEvents = [aDecoder decodeObjectOfClasses:classes
|
2019-09-09 15:21:29 -07:00
|
|
|
forKey:kGDTCORStorageStoredEventsKey];
|
2019-08-12 16:19:37 -07:00
|
|
|
classes = [NSSet setWithObjects:[NSMutableDictionary class], [NSMutableSet class],
|
2019-09-09 15:21:29 -07:00
|
|
|
[GDTCORStoredEvent class], nil];
|
2019-04-12 19:27:00 -07:00
|
|
|
sharedInstance->_targetToEventSet =
|
2019-09-09 15:21:29 -07:00
|
|
|
[aDecoder decodeObjectOfClasses:classes forKey:kGDTCORStorageTargetToEventSetKey];
|
2019-05-16 12:58:42 -07:00
|
|
|
sharedInstance->_uploadCoordinator =
|
2019-09-09 15:21:29 -07:00
|
|
|
[aDecoder decodeObjectOfClass:[GDTCORUploadCoordinator class]
|
|
|
|
|
forKey:kGDTCORStorageUploadCoordinatorKey];
|
2019-04-12 19:27:00 -07:00
|
|
|
});
|
|
|
|
|
return sharedInstance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORStorage *sharedInstance = [self.class sharedInstance];
|
|
|
|
|
NSMutableOrderedSet<GDTCORStoredEvent *> *storedEvents = sharedInstance->_storedEvents;
|
2019-08-19 14:23:03 -07:00
|
|
|
if (storedEvents) {
|
2019-09-09 15:21:29 -07:00
|
|
|
[aCoder encodeObject:storedEvents forKey:kGDTCORStorageStoredEventsKey];
|
2019-08-19 14:23:03 -07:00
|
|
|
}
|
2019-09-09 15:21:29 -07:00
|
|
|
NSMutableDictionary<NSNumber *, NSMutableSet<GDTCORStoredEvent *> *> *targetToEventSet =
|
2019-08-19 14:23:03 -07:00
|
|
|
sharedInstance->_targetToEventSet;
|
|
|
|
|
if (targetToEventSet) {
|
2019-09-09 15:21:29 -07:00
|
|
|
[aCoder encodeObject:targetToEventSet forKey:kGDTCORStorageTargetToEventSetKey];
|
2019-08-19 14:23:03 -07:00
|
|
|
}
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORUploadCoordinator *uploadCoordinator = sharedInstance->_uploadCoordinator;
|
2019-08-19 14:23:03 -07:00
|
|
|
if (uploadCoordinator) {
|
2019-09-09 15:21:29 -07:00
|
|
|
[aCoder encodeObject:uploadCoordinator forKey:kGDTCORStorageUploadCoordinatorKey];
|
2019-08-12 11:41:40 -07:00
|
|
|
}
|
2019-04-12 19:27:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|