Configured colours and added additional images to ready app for iOS 13 dark mode

This commit is contained in:
Deniz Duezgoeren
2019-10-08 21:37:42 +02:00
parent b7bc097664
commit 85efe3955d
310 changed files with 6512 additions and 4492 deletions
@@ -0,0 +1,36 @@
/*
* Copyright 2019 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.
*/
#import "GDTCORLibrary/Public/GDTCORAssert.h"
GDTCORAssertionBlock GDTCORAssertionBlockToRunInstead(void) {
// This class is only compiled in by unit tests, and this should fail quickly in optimized builds.
Class GDTCORAssertClass = NSClassFromString(@"GDTCORAssertHelper");
if (__builtin_expect(!!GDTCORAssertClass, 0)) {
SEL assertionBlockSEL = NSSelectorFromString(@"assertionBlock");
if (assertionBlockSEL) {
IMP assertionBlockIMP = [GDTCORAssertClass methodForSelector:assertionBlockSEL];
if (assertionBlockIMP) {
GDTCORAssertionBlock assertionBlock = ((GDTCORAssertionBlock(*)(id, SEL))assertionBlockIMP)(
GDTCORAssertClass, assertionBlockSEL);
if (assertionBlock) {
return assertionBlock;
}
}
}
}
return NULL;
}
@@ -0,0 +1,164 @@
/*
* 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.
*/
#import "GDTCORLibrary/Public/GDTCORClock.h"
#import <sys/sysctl.h>
// Using a monotonic clock is necessary because CFAbsoluteTimeGetCurrent(), NSDate, and related all
// are subject to drift. That it to say, multiple consecutive calls do not always result in a
// time that is in the future. Clocks may be adjusted by the user, NTP, or any number of external
// factors. This class attempts to determine the wall-clock time at the time of the event by
// capturing the kernel start and time since boot to determine a wallclock time in UTC.
//
// Timezone offsets at the time of a snapshot are also captured in order to provide local-time
// details. Other classes in this library depend on comparing times at some time in the future to
// a time captured in the past, and this class needs to provide a mechanism to do that.
//
// TL;DR: This class attempts to accomplish two things: 1. Provide accurate event times. 2. Provide
// a monotonic clock mechanism to accurately check if some clock snapshot was before or after
// by using a shared reference point (kernel boot time).
//
// Note: Much of the mach time stuff doesn't work properly in the simulator. So this class can be
// difficult to unit test.
/** Returns the kernel boottime property from sysctl.
*
* Inspired by https://stackoverflow.com/a/40497811
*
* @return The KERN_BOOTTIME property from sysctl, in nanoseconds.
*/
static int64_t KernelBootTimeInNanoseconds() {
// Caching the result is not possible because clock drift would not be accounted for.
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
int rc = sysctl(mib, 2, &boottime, &size, NULL, 0);
if (rc != 0) {
return 0;
}
return (int64_t)boottime.tv_sec * NSEC_PER_MSEC + (int64_t)boottime.tv_usec;
}
/** Returns value of gettimeofday, in nanoseconds.
*
* Inspired by https://stackoverflow.com/a/40497811
*
* @return The value of gettimeofday, in nanoseconds.
*/
static int64_t UptimeInNanoseconds() {
int64_t before_now;
int64_t after_now;
struct timeval now;
before_now = KernelBootTimeInNanoseconds();
// Addresses a race condition in which the system time has updated, but the boottime has not.
do {
gettimeofday(&now, NULL);
after_now = KernelBootTimeInNanoseconds();
} while (after_now != before_now);
return (int64_t)now.tv_sec * NSEC_PER_MSEC + (int64_t)now.tv_usec - before_now;
}
// TODO: Consider adding a 'trustedTime' property that can be populated by the response from a BE.
@implementation GDTCORClock
- (instancetype)init {
self = [super init];
if (self) {
_kernelBootTime = KernelBootTimeInNanoseconds();
_uptime = UptimeInNanoseconds();
_timeMillis =
(int64_t)((CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) * NSEC_PER_USEC);
CFTimeZoneRef timeZoneRef = CFTimeZoneCopySystem();
_timezoneOffsetSeconds = CFTimeZoneGetSecondsFromGMT(timeZoneRef, 0);
CFRelease(timeZoneRef);
}
return self;
}
+ (GDTCORClock *)snapshot {
return [[GDTCORClock alloc] init];
}
+ (instancetype)clockSnapshotInTheFuture:(uint64_t)millisInTheFuture {
GDTCORClock *snapshot = [self snapshot];
snapshot->_timeMillis += millisInTheFuture;
return snapshot;
}
- (BOOL)isAfter:(GDTCORClock *)otherClock {
// These clocks are trivially comparable when they share a kernel boot time.
if (_kernelBootTime == otherClock->_kernelBootTime) {
int64_t timeDiff = (_timeMillis + _timezoneOffsetSeconds) -
(otherClock->_timeMillis + otherClock->_timezoneOffsetSeconds);
return timeDiff > 0;
} else {
int64_t kernelBootTimeDiff = otherClock->_kernelBootTime - _kernelBootTime;
// This isn't a great solution, but essentially, if the other clock's boot time is 'later', NO
// is returned. This can be altered by changing the system time and rebooting.
return kernelBootTimeDiff < 0 ? YES : NO;
}
}
- (NSUInteger)hash {
return [@(_kernelBootTime) hash] ^ [@(_uptime) hash] ^ [@(_timeMillis) hash] ^
[@(_timezoneOffsetSeconds) hash];
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
#pragma mark - NSSecureCoding
/** NSKeyedCoder key for timeMillis property. */
static NSString *const kGDTCORClockTimeMillisKey = @"GDTCORClockTimeMillis";
/** NSKeyedCoder key for timezoneOffsetMillis property. */
static NSString *const kGDTCORClockTimezoneOffsetSeconds = @"GDTCORClockTimezoneOffsetSeconds";
/** NSKeyedCoder key for _kernelBootTime ivar. */
static NSString *const kGDTCORClockKernelBootTime = @"GDTCORClockKernelBootTime";
/** NSKeyedCoder key for _uptime ivar. */
static NSString *const kGDTCORClockUptime = @"GDTCORClockUptime";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
// TODO: If the kernelBootTime is more recent, we need to change the kernel boot time and
// uptimeMillis ivars
_timeMillis = [aDecoder decodeInt64ForKey:kGDTCORClockTimeMillisKey];
_timezoneOffsetSeconds = [aDecoder decodeInt64ForKey:kGDTCORClockTimezoneOffsetSeconds];
_kernelBootTime = [aDecoder decodeInt64ForKey:kGDTCORClockKernelBootTime];
_uptime = [aDecoder decodeInt64ForKey:kGDTCORClockUptime];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInt64:_timeMillis forKey:kGDTCORClockTimeMillisKey];
[aCoder encodeInt64:_timezoneOffsetSeconds forKey:kGDTCORClockTimezoneOffsetSeconds];
[aCoder encodeInt64:_kernelBootTime forKey:kGDTCORClockKernelBootTime];
[aCoder encodeInt64:_uptime forKey:kGDTCORClockUptime];
}
@end
@@ -0,0 +1,36 @@
/*
* 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.
*/
#import "GDTCORLibrary/Public/GDTCORConsoleLogger.h"
/** The console logger prefix. */
static NSString *kGDTCORConsoleLogger = @"[GoogleDataTransport]";
NSString *GDTCORMessageCodeEnumToString(GDTCORMessageCode code) {
return [[NSString alloc] initWithFormat:@"I-GDTCOR%06ld", (long)code];
}
void GDTCORLog(GDTCORMessageCode code, NSString *format, ...) {
// Don't log anything in not debug builds.
#ifndef NDEBUG
NSString *logFormat = [NSString stringWithFormat:@"%@[%@] %@", kGDTCORConsoleLogger,
GDTCORMessageCodeEnumToString(code), format];
va_list args;
va_start(args, format);
NSLogv(logFormat, args);
va_end(args);
#endif // NDEBUG
}
@@ -0,0 +1,64 @@
/*
* Copyright 2019 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.
*/
#import <GoogleDataTransport/GDTCORDataFuture.h>
@implementation GDTCORDataFuture
- (instancetype)initWithFileURL:(NSURL *)fileURL {
self = [super init];
if (self) {
_fileURL = fileURL;
}
return self;
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
- (NSUInteger)hash {
// In reality, only one of these should be populated.
return [_fileURL hash] ^ [_originalData hash];
}
#pragma mark - NSSecureCoding
/** Coding key for _fileURL ivar. */
static NSString *kGDTCORDataFutureFileURLKey = @"GDTCORDataFutureFileURLKey";
/** Coding key for _data ivar. */
static NSString *kGDTCORDataFutureDataKey = @"GDTCORDataFutureDataKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_fileURL forKey:kGDTCORDataFutureFileURLKey];
[aCoder encodeObject:_originalData forKey:kGDTCORDataFutureDataKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
self = [self init];
if (self) {
_fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kGDTCORDataFutureFileURLKey];
_originalData = [aDecoder decodeObjectOfClass:[NSData class] forKey:kGDTCORDataFutureDataKey];
}
return self;
}
@end
@@ -0,0 +1,119 @@
/*
* 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.
*/
#import <GoogleDataTransport/GDTCOREvent.h>
#import <GoogleDataTransport/GDTCORAssert.h>
#import <GoogleDataTransport/GDTCORStoredEvent.h>
#import "GDTCORLibrary/Private/GDTCOREvent_Private.h"
@implementation GDTCOREvent
- (instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target {
GDTCORAssert(mappingID.length > 0, @"Please give a valid mapping ID");
GDTCORAssert(target > 0, @"A target cannot be negative or 0");
if (mappingID == nil || mappingID.length == 0 || target <= 0) {
return nil;
}
self = [super init];
if (self) {
_mappingID = mappingID;
_target = target;
_qosTier = GDTCOREventQosDefault;
}
return self;
}
- (instancetype)copy {
GDTCOREvent *copy = [[GDTCOREvent alloc] initWithMappingID:_mappingID target:_target];
copy.dataObject = _dataObject;
copy.dataObjectTransportBytes = _dataObjectTransportBytes;
copy.qosTier = _qosTier;
copy.clockSnapshot = _clockSnapshot;
copy.customPrioritizationParams = _customPrioritizationParams;
return copy;
}
- (NSUInteger)hash {
// This loses some precision, but it's probably fine.
NSUInteger mappingIDHash = [_mappingID hash];
NSUInteger timeHash = [_clockSnapshot hash];
NSUInteger dataObjectTransportBytesHash = [_dataObjectTransportBytes hash];
return mappingIDHash ^ _target ^ dataObjectTransportBytesHash ^ _qosTier ^ timeHash;
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
- (void)setDataObject:(id<GDTCOREventDataObject>)dataObject {
// If you're looking here because of a performance issue in -transportBytes slowing the assignment
// of -dataObject, one way to address this is to add a queue to this class,
// dispatch_(barrier_ if concurrent)async here, and implement the getter with a dispatch_sync.
if (dataObject != _dataObject) {
_dataObject = dataObject;
_dataObjectTransportBytes = [dataObject transportBytes];
}
}
- (GDTCORStoredEvent *)storedEventWithDataFuture:(GDTCORDataFuture *)dataFuture {
return [[GDTCORStoredEvent alloc] initWithEvent:self dataFuture:dataFuture];
}
#pragma mark - NSSecureCoding and NSCoding Protocols
/** NSCoding key for mappingID property. */
static NSString *mappingIDKey = @"_mappingID";
/** NSCoding key for target property. */
static NSString *targetKey = @"_target";
/** NSCoding key for dataObjectTransportBytes property. */
static NSString *dataObjectTransportBytesKey = @"_dataObjectTransportBytesKey";
/** NSCoding key for qosTier property. */
static NSString *qosTierKey = @"_qosTier";
/** NSCoding key for clockSnapshot property. */
static NSString *clockSnapshotKey = @"_clockSnapshot";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
NSString *mappingID = [aDecoder decodeObjectOfClass:[NSObject class] forKey:mappingIDKey];
NSInteger target = [aDecoder decodeIntegerForKey:targetKey];
self = [self initWithMappingID:mappingID target:target];
if (self) {
_dataObjectTransportBytes = [aDecoder decodeObjectOfClass:[NSData class]
forKey:dataObjectTransportBytesKey];
_qosTier = [aDecoder decodeIntegerForKey:qosTierKey];
_clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:clockSnapshotKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_mappingID forKey:mappingIDKey];
[aCoder encodeInteger:_target forKey:targetKey];
[aCoder encodeObject:_dataObjectTransportBytes forKey:dataObjectTransportBytesKey];
[aCoder encodeInteger:_qosTier forKey:qosTierKey];
[aCoder encodeObject:_clockSnapshot forKey:clockSnapshotKey];
}
@end
@@ -0,0 +1,119 @@
/*
* Copyright 2019 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.
*/
#import "GDTCORLibrary/Public/GDTCORLifecycle.h"
#import <GoogleDataTransport/GDTCOREvent.h>
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
#import "GDTCORLibrary/Private/GDTCORStorage_Private.h"
#import "GDTCORLibrary/Private/GDTCORTransformer_Private.h"
#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
@implementation GDTCORLifecycle
+ (void)load {
[self sharedInstance];
}
/** Creates/returns the singleton instance of this class.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance {
static GDTCORLifecycle *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTCORLifecycle alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:kGDTCORApplicationDidEnterBackgroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:kGDTCORApplicationWillEnterForegroundNotification
object:nil];
NSString *name = kGDTCORApplicationWillTerminateNotification;
[notificationCenter addObserver:self
selector:@selector(applicationWillTerminate:)
name:name
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification {
GDTCORApplication *application = [GDTCORApplication sharedApplication];
if ([[GDTCORTransformer sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTCORTransformer sharedInstance] appWillBackground:application];
}
if ([[GDTCORStorage sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTCORStorage sharedInstance] appWillBackground:application];
}
if ([[GDTCORUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTCORUploadCoordinator sharedInstance] appWillBackground:application];
}
if ([[GDTCORRegistrar sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTCORRegistrar sharedInstance] appWillBackground:application];
}
}
- (void)applicationWillEnterForeground:(NSNotification *)notification {
GDTCORApplication *application = [GDTCORApplication sharedApplication];
if ([[GDTCORTransformer sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTCORTransformer sharedInstance] appWillForeground:application];
}
if ([[GDTCORStorage sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTCORStorage sharedInstance] appWillForeground:application];
}
if ([[GDTCORUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTCORUploadCoordinator sharedInstance] appWillForeground:application];
}
if ([[GDTCORRegistrar sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTCORRegistrar sharedInstance] appWillForeground:application];
}
}
- (void)applicationWillTerminate:(NSNotification *)notification {
GDTCORApplication *application = [GDTCORApplication sharedApplication];
if ([[GDTCORTransformer sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTCORTransformer sharedInstance] appWillTerminate:application];
}
if ([[GDTCORStorage sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTCORStorage sharedInstance] appWillTerminate:application];
}
if ([[GDTCORUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTCORUploadCoordinator sharedInstance] appWillTerminate:application];
}
if ([[GDTCORRegistrar sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTCORRegistrar sharedInstance] appWillTerminate:application];
}
}
@end
@@ -0,0 +1,176 @@
/*
* Copyright 2019 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.
*/
#import <GoogleDataTransport/GDTCORPlatform.h>
#import <GoogleDataTransport/GDTCORAssert.h>
const GDTCORBackgroundIdentifier GDTCORBackgroundIdentifierInvalid = 0;
NSString *const kGDTCORApplicationDidEnterBackgroundNotification =
@"GDTCORApplicationDidEnterBackgroundNotification";
NSString *const kGDTCORApplicationWillEnterForegroundNotification =
@"GDTCORApplicationWillEnterForegroundNotification";
NSString *const kGDTCORApplicationWillTerminateNotification =
@"GDTCORApplicationWillTerminateNotification";
BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) {
#if TARGET_OS_IOS
return (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN;
#else
return NO;
#endif // TARGET_OS_IOS
}
@implementation GDTCORApplication
+ (void)load {
#if TARGET_OS_IOS || TARGET_OS_TV
// If this asserts, please file a bug at https://github.com/firebase/firebase-ios-sdk/issues.
GDTCORFatalAssert(
GDTCORBackgroundIdentifierInvalid == UIBackgroundTaskInvalid,
@"GDTCORBackgroundIdentifierInvalid and UIBackgroundTaskInvalid should be the same.");
#endif
[self sharedApplication];
}
+ (nullable GDTCORApplication *)sharedApplication {
static GDTCORApplication *application;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
application = [[GDTCORApplication alloc] init];
});
return application;
}
- (instancetype)init {
self = [super init];
if (self) {
#if TARGET_OS_IOS || TARGET_OS_TV
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(iOSApplicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(iOSApplicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
NSString *name = UIApplicationWillTerminateNotification;
[notificationCenter addObserver:self
selector:@selector(iOSApplicationWillTerminate:)
name:name
object:nil];
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13, tvOS 13.0, *)) {
[notificationCenter addObserver:self
selector:@selector(iOSApplicationWillEnterForeground:)
name:UISceneWillEnterForegroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(iOSApplicationDidEnterBackground:)
name:UISceneWillDeactivateNotification
object:nil];
}
#endif // defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
#elif TARGET_OS_OSX
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(macOSApplicationWillTerminate:)
name:NSApplicationWillTerminateNotification
object:nil];
#endif // TARGET_OS_IOS || TARGET_OS_TV
}
return self;
}
- (GDTCORBackgroundIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
return
[[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithExpirationHandler:handler];
}
- (void)endBackgroundTask:(GDTCORBackgroundIdentifier)bgID {
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[[self sharedApplicationForBackgroundTask] endBackgroundTask:bgID];
}
}
#pragma mark - App environment helpers
- (BOOL)isAppExtension {
#if TARGET_OS_IOS || TARGET_OS_TV
BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
return appExtension;
#elif TARGET_OS_OSX
return NO;
#endif
}
/** Returns a UIApplication instance if on the appropriate platform.
*
* @return The shared UIApplication if on the appropriate platform.
*/
#if TARGET_OS_IOS || TARGET_OS_TV
- (nullable UIApplication *)sharedApplicationForBackgroundTask {
#else
- (nullable id)sharedApplicationForBackgroundTask {
#endif
if ([self isAppExtension]) {
return nil;
}
id sharedApplication = nil;
Class uiApplicationClass = NSClassFromString(@"UIApplication");
if (uiApplicationClass &&
[uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) {
sharedApplication = [uiApplicationClass sharedApplication];
}
return sharedApplication;
}
#pragma mark - UIApplicationDelegate
#if TARGET_OS_IOS || TARGET_OS_TV
- (void)iOSApplicationDidEnterBackground:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTCORApplicationDidEnterBackgroundNotification object:nil];
}
- (void)iOSApplicationWillEnterForeground:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTCORApplicationWillEnterForegroundNotification object:nil];
}
- (void)iOSApplicationWillTerminate:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil];
}
#endif // TARGET_OS_IOS || TARGET_OS_TV
#pragma mark - NSApplicationDelegate
#if TARGET_OS_OSX
- (void)macOSApplicationWillTerminate:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil];
}
#endif // TARGET_OS_OSX
@end
@@ -0,0 +1,112 @@
/*
* Copyright 2019 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.
*/
#import "GDTCORLibrary/Private/GDTCORReachability.h"
#import "GDTCORLibrary/Private/GDTCORReachability_Private.h"
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
#import <netinet/in.h>
/** Sets the _callbackFlag ivar whenever the network changes.
*
* @param reachability The reachability object calling back.
* @param flags The new flag values.
* @param info Any data that might be passed in by the callback.
*/
static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability,
SCNetworkReachabilityFlags flags,
void *info);
@implementation GDTCORReachability {
/** The reachability object. */
SCNetworkReachabilityRef _reachabilityRef;
/** The queue on which callbacks and all work will occur. */
dispatch_queue_t _reachabilityQueue;
/** Flags specified by reachability callbacks. */
SCNetworkConnectionFlags _callbackFlags;
}
+ (void)load {
[self sharedInstance];
}
+ (instancetype)sharedInstance {
static GDTCORReachability *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTCORReachability alloc] init];
});
return sharedInstance;
}
+ (SCNetworkReachabilityFlags)currentFlags {
__block SCNetworkReachabilityFlags currentFlags;
dispatch_sync([GDTCORReachability sharedInstance] -> _reachabilityQueue, ^{
GDTCORReachability *reachability = [GDTCORReachability sharedInstance];
currentFlags = reachability->_flags ? reachability->_flags : reachability->_callbackFlags;
});
return currentFlags;
}
- (instancetype)init {
self = [super init];
if (self) {
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
_reachabilityQueue =
dispatch_queue_create("com.google.GDTCORReachability", DISPATCH_QUEUE_SERIAL);
_reachabilityRef = SCNetworkReachabilityCreateWithAddress(
kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
Boolean success = SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _reachabilityQueue);
if (!success) {
GDTCORLogWarning(GDTCORMCWReachabilityFailed, @"%@", @"The reachability queue wasn't set.");
}
success = SCNetworkReachabilitySetCallback(_reachabilityRef, GDTCORReachabilityCallback, NULL);
if (!success) {
GDTCORLogWarning(GDTCORMCWReachabilityFailed, @"%@",
@"The reachability callback wasn't set.");
}
// Get the initial set of flags.
dispatch_async(_reachabilityQueue, ^{
Boolean valid = SCNetworkReachabilityGetFlags(self->_reachabilityRef, &self->_flags);
if (!valid) {
self->_flags = 0;
}
});
}
return self;
}
- (void)setCallbackFlags:(SCNetworkReachabilityFlags)flags {
if (_callbackFlags != flags) {
self->_callbackFlags = flags;
}
}
@end
static void GDTCORReachabilityCallback(SCNetworkReachabilityRef reachability,
SCNetworkReachabilityFlags flags,
void *info) {
[[GDTCORReachability sharedInstance] setCallbackFlags:flags];
}
@@ -0,0 +1,140 @@
/*
* 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.
*/
#import "GDTCORLibrary/Public/GDTCORRegistrar.h"
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
@implementation GDTCORRegistrar {
/** Backing ivar for targetToUploader property. */
NSMutableDictionary<NSNumber *, id<GDTCORUploader>> *_targetToUploader;
/** Backing ivar for targetToPrioritizer property. */
NSMutableDictionary<NSNumber *, id<GDTCORPrioritizer>> *_targetToPrioritizer;
}
+ (instancetype)sharedInstance {
static GDTCORRegistrar *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTCORRegistrar alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_registrarQueue =
dispatch_queue_create("com.google.GDTCORRegistrar", DISPATCH_QUEUE_CONCURRENT);
_targetToPrioritizer = [[NSMutableDictionary alloc] init];
_targetToUploader = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)registerUploader:(id<GDTCORUploader>)backend target:(GDTCORTarget)target {
__weak GDTCORRegistrar *weakSelf = self;
dispatch_barrier_async(_registrarQueue, ^{
GDTCORRegistrar *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_targetToUploader[@(target)] = backend;
}
});
}
- (void)registerPrioritizer:(id<GDTCORPrioritizer>)prioritizer target:(GDTCORTarget)target {
__weak GDTCORRegistrar *weakSelf = self;
dispatch_barrier_async(_registrarQueue, ^{
GDTCORRegistrar *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_targetToPrioritizer[@(target)] = prioritizer;
}
});
}
- (NSMutableDictionary<NSNumber *, id<GDTCORUploader>> *)targetToUploader {
__block NSMutableDictionary<NSNumber *, id<GDTCORUploader>> *targetToUploader;
__weak GDTCORRegistrar *weakSelf = self;
dispatch_sync(_registrarQueue, ^{
GDTCORRegistrar *strongSelf = weakSelf;
if (strongSelf) {
targetToUploader = strongSelf->_targetToUploader;
}
});
return targetToUploader;
}
- (NSMutableDictionary<NSNumber *, id<GDTCORPrioritizer>> *)targetToPrioritizer {
__block NSMutableDictionary<NSNumber *, id<GDTCORPrioritizer>> *targetToPrioritizer;
__weak GDTCORRegistrar *weakSelf = self;
dispatch_sync(_registrarQueue, ^{
GDTCORRegistrar *strongSelf = weakSelf;
if (strongSelf) {
targetToPrioritizer = strongSelf->_targetToPrioritizer;
}
});
return targetToPrioritizer;
}
#pragma mark - GDTCORLifecycleProtocol
- (void)appWillBackground:(nonnull GDTCORApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTCORUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillBackground:)]) {
[uploader appWillBackground:app];
}
}
for (id<GDTCORPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillBackground:)]) {
[prioritizer appWillBackground:app];
}
}
});
}
- (void)appWillForeground:(nonnull GDTCORApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTCORUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillForeground:)]) {
[uploader appWillForeground:app];
}
}
for (id<GDTCORPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillForeground:)]) {
[prioritizer appWillForeground:app];
}
}
});
}
- (void)appWillTerminate:(nonnull GDTCORApplication *)app {
dispatch_sync(_registrarQueue, ^{
for (id<GDTCORUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillTerminate:)]) {
[uploader appWillTerminate:app];
}
}
for (id<GDTCORPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillTerminate:)]) {
[prioritizer appWillTerminate:app];
}
}
});
}
@end
@@ -0,0 +1,323 @@
/*
* 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.
*/
#import "GDTCORLibrary/Private/GDTCORStorage.h"
#import "GDTCORLibrary/Private/GDTCORStorage_Private.h"
#import <GoogleDataTransport/GDTCORAssert.h>
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
#import <GoogleDataTransport/GDTCORPrioritizer.h>
#import <GoogleDataTransport/GDTCORStoredEvent.h>
#import "GDTCORLibrary/Private/GDTCOREvent_Private.h"
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
/** Creates and/or returns a singleton NSString that is the shared storage path.
*
* @return The SDK event storage path.
*/
static NSString *GDTCORStoragePath() {
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;
}
@implementation GDTCORStorage
+ (NSString *)archivePath {
static NSString *archivePath;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
archivePath = [GDTCORStoragePath() stringByAppendingPathComponent:@"GDTCORStorageArchive"];
});
return archivePath;
}
+ (instancetype)sharedInstance {
static GDTCORStorage *sharedStorage;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedStorage = [[GDTCORStorage alloc] init];
});
return sharedStorage;
}
- (instancetype)init {
self = [super init];
if (self) {
_storageQueue = dispatch_queue_create("com.google.GDTCORStorage", DISPATCH_QUEUE_SERIAL);
_targetToEventSet = [[NSMutableDictionary alloc] init];
_storedEvents = [[NSMutableOrderedSet alloc] init];
_uploadCoordinator = [GDTCORUploadCoordinator sharedInstance];
}
return self;
}
- (void)storeEvent:(GDTCOREvent *)event {
if (event == nil) {
return;
}
[self createEventDirectoryIfNotExists];
__block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid;
if (_runningInBackground) {
bgID = [[GDTCORApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[[GDTCORApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
}];
}
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.
id<GDTCORPrioritizer> prioritizer =
[GDTCORRegistrar sharedInstance].targetToPrioritizer[@(target)];
GDTCORAssert(prioritizer, @"There's no prioritizer registered for the given target.");
// Write the transport bytes to disk, get a filename.
GDTCORAssert(event.dataObjectTransportBytes, @"The event should have been serialized to bytes");
NSURL *eventFile = [self saveEventBytesToDisk:event.dataObjectTransportBytes
eventHash:event.hash];
GDTCORDataFuture *dataFuture = [[GDTCORDataFuture alloc] initWithFileURL:eventFile];
GDTCORStoredEvent *storedEvent = [event storedEventWithDataFuture:dataFuture];
// 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.
if (event.qosTier == GDTCOREventQoSFast) {
[self.uploadCoordinator forceUploadForTarget:target];
}
// Write state to disk.
if (self->_runningInBackground) {
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
requiringSecureCoding:YES
error:nil];
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
#endif
}
}
// If running in the background, save state to disk and end the associated background task.
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[[GDTCORApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
});
}
- (void)removeEvents:(NSSet<GDTCORStoredEvent *> *)events {
NSSet<GDTCORStoredEvent *> *eventsToRemove = [events copy];
dispatch_async(_storageQueue, ^{
for (GDTCORStoredEvent *event in eventsToRemove) {
// Remove from disk, first and foremost.
NSError *error;
if (event.dataFuture.fileURL) {
NSURL *fileURL = event.dataFuture.fileURL;
[[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
GDTCORAssert(error == nil, @"There was an error removing an event file: %@", error);
}
// 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;
BOOL result = [[NSFileManager defaultManager] createDirectoryAtPath:GDTCORStoragePath()
withIntermediateDirectories:YES
attributes:0
error:&error];
if (!result || error) {
GDTCORLogError(GDTCORMCEDirectoryCreationError, @"Error creating the directory: %@", error);
}
}
/** 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 {
NSString *storagePath = GDTCORStoragePath();
NSString *event = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash];
NSURL *eventFilePath = [NSURL fileURLWithPath:[storagePath stringByAppendingPathComponent:event]];
GDTCORAssert(![[NSFileManager defaultManager] fileExistsAtPath:eventFilePath.path],
@"An event shouldn't already exist at this path: %@", eventFilePath.path);
BOOL writingSuccess = [transportBytes writeToURL:eventFilePath atomically:YES];
if (!writingSuccess) {
GDTCORLogError(GDTCORMCEFileWriteError, @"An event file could not be written: %@",
eventFilePath);
}
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.
*/
- (void)addEventToTrackingCollections:(GDTCORStoredEvent *)event {
[_storedEvents addObject:event];
NSMutableSet<GDTCORStoredEvent *> *events = self.targetToEventSet[event.target];
events = events ? events : [[NSMutableSet alloc] init];
[events addObject:event];
_targetToEventSet[event.target] = events;
}
#pragma mark - GDTCORLifecycleProtocol
- (void)appWillForeground:(GDTCORApplication *)app {
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSData dataWithContentsOfFile:[GDTCORStorage archivePath]];
[NSKeyedUnarchiver unarchivedObjectOfClass:[GDTCORStorage class] fromData:data error:nil];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedUnarchiver unarchiveObjectWithFile:[GDTCORStorage archivePath]];
#endif
}
}
- (void)appWillBackground:(GDTCORApplication *)app {
self->_runningInBackground = YES;
dispatch_async(_storageQueue, ^{
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
requiringSecureCoding:YES
error:nil];
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
#endif
}
});
// Create an immediate background task to run until the end of the current queue of work.
__block GDTCORBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
}];
dispatch_async(_storageQueue, ^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
});
}
- (void)appWillTerminate:(GDTCORApplication *)application {
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
requiringSecureCoding:YES
error:nil];
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
#endif
}
}
#pragma mark - NSSecureCoding
/** The NSKeyedCoder key for the storedEvents property. */
static NSString *const kGDTCORStorageStoredEventsKey = @"GDTCORStorageStoredEventsKey";
/** The NSKeyedCoder key for the targetToEventSet property. */
static NSString *const kGDTCORStorageTargetToEventSetKey = @"GDTCORStorageTargetToEventSetKey";
/** The NSKeyedCoder key for the uploadCoordinator property. */
static NSString *const kGDTCORStorageUploadCoordinatorKey = @"GDTCORStorageUploadCoordinatorKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
// Create the singleton and populate its ivars.
GDTCORStorage *sharedInstance = [self.class sharedInstance];
dispatch_sync(sharedInstance.storageQueue, ^{
NSSet *classes =
[NSSet setWithObjects:[NSMutableOrderedSet class], [GDTCORStoredEvent class], nil];
sharedInstance->_storedEvents = [aDecoder decodeObjectOfClasses:classes
forKey:kGDTCORStorageStoredEventsKey];
classes = [NSSet setWithObjects:[NSMutableDictionary class], [NSMutableSet class],
[GDTCORStoredEvent class], nil];
sharedInstance->_targetToEventSet =
[aDecoder decodeObjectOfClasses:classes forKey:kGDTCORStorageTargetToEventSetKey];
sharedInstance->_uploadCoordinator =
[aDecoder decodeObjectOfClass:[GDTCORUploadCoordinator class]
forKey:kGDTCORStorageUploadCoordinatorKey];
});
return sharedInstance;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
GDTCORStorage *sharedInstance = [self.class sharedInstance];
NSMutableOrderedSet<GDTCORStoredEvent *> *storedEvents = sharedInstance->_storedEvents;
if (storedEvents) {
[aCoder encodeObject:storedEvents forKey:kGDTCORStorageStoredEventsKey];
}
NSMutableDictionary<NSNumber *, NSMutableSet<GDTCORStoredEvent *> *> *targetToEventSet =
sharedInstance->_targetToEventSet;
if (targetToEventSet) {
[aCoder encodeObject:targetToEventSet forKey:kGDTCORStorageTargetToEventSetKey];
}
GDTCORUploadCoordinator *uploadCoordinator = sharedInstance->_uploadCoordinator;
if (uploadCoordinator) {
[aCoder encodeObject:uploadCoordinator forKey:kGDTCORStorageUploadCoordinatorKey];
}
}
@end
@@ -0,0 +1,95 @@
/*
* Copyright 2019 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.
*/
#import <GoogleDataTransport/GDTCORStoredEvent.h>
#import <GoogleDataTransport/GDTCORClock.h>
#import "GDTCORLibrary/Private/GDTCORStorage_Private.h"
@implementation GDTCORStoredEvent
- (instancetype)initWithEvent:(GDTCOREvent *)event
dataFuture:(nonnull GDTCORDataFuture *)dataFuture {
self = [super init];
if (self) {
_dataFuture = dataFuture;
_mappingID = event.mappingID;
_target = @(event.target);
_qosTier = event.qosTier;
_clockSnapshot = event.clockSnapshot;
_customPrioritizationParams = event.customPrioritizationParams;
}
return self;
}
#pragma mark - NSSecureCoding
/** Coding key for the dataFuture ivar. */
static NSString *kDataFutureKey = @"GDTCORStoredEventDataFutureKey";
/** Coding key for mappingID ivar. */
static NSString *kMappingIDKey = @"GDTCORStoredEventMappingIDKey";
/** Coding key for target ivar. */
static NSString *kTargetKey = @"GDTCORStoredEventTargetKey";
/** Coding key for qosTier ivar. */
static NSString *kQosTierKey = @"GDTCORStoredEventQosTierKey";
/** Coding key for clockSnapshot ivar. */
static NSString *kClockSnapshotKey = @"GDTCORStoredEventClockSnapshotKey";
/** Coding key for customPrioritizationParams ivar. */
static NSString *kCustomPrioritizationParamsKey = @"GDTCORStoredEventcustomPrioritizationParamsKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_dataFuture forKey:kDataFutureKey];
[aCoder encodeObject:_mappingID forKey:kMappingIDKey];
[aCoder encodeObject:_target forKey:kTargetKey];
[aCoder encodeObject:@(_qosTier) forKey:kQosTierKey];
[aCoder encodeObject:_clockSnapshot forKey:kClockSnapshotKey];
[aCoder encodeObject:_customPrioritizationParams forKey:kCustomPrioritizationParamsKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
self = [self init];
if (self) {
_dataFuture = [aDecoder decodeObjectOfClass:[GDTCORDataFuture class] forKey:kDataFutureKey];
_mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kMappingIDKey];
_target = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kTargetKey];
NSNumber *qosTier = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kQosTierKey];
_qosTier = [qosTier intValue];
_clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:kClockSnapshotKey];
_customPrioritizationParams = [aDecoder decodeObjectOfClass:[NSDictionary class]
forKey:kCustomPrioritizationParamsKey];
}
return self;
}
- (BOOL)isEqual:(GDTCORStoredEvent *)other {
return [self hash] == [other hash];
}
- (NSUInteger)hash {
return [_dataFuture hash] ^ [_mappingID hash] ^ [_target hash] ^ [_clockSnapshot hash] ^ _qosTier;
}
@end
@@ -0,0 +1,113 @@
/*
* 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.
*/
#import "GDTCORLibrary/Private/GDTCORTransformer.h"
#import "GDTCORLibrary/Private/GDTCORTransformer_Private.h"
#import <GoogleDataTransport/GDTCORAssert.h>
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
#import <GoogleDataTransport/GDTCOREventTransformer.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
#import "GDTCORLibrary/Private/GDTCORStorage.h"
@implementation GDTCORTransformer
+ (instancetype)sharedInstance {
static GDTCORTransformer *eventTransformer;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
eventTransformer = [[self alloc] init];
});
return eventTransformer;
}
- (instancetype)init {
self = [super init];
if (self) {
_eventWritingQueue =
dispatch_queue_create("com.google.GDTCORTransformer", DISPATCH_QUEUE_SERIAL);
_storageInstance = [GDTCORStorage sharedInstance];
}
return self;
}
- (void)transformEvent:(GDTCOREvent *)event
withTransformers:(NSArray<id<GDTCOREventTransformer>> *)transformers {
GDTCORAssert(event, @"You can't write a nil event");
__block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid;
if (_runningInBackground) {
bgID = [[GDTCORApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[[GDTCORApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
}];
}
dispatch_async(_eventWritingQueue, ^{
GDTCOREvent *transformedEvent = event;
for (id<GDTCOREventTransformer> transformer in transformers) {
if ([transformer respondsToSelector:@selector(transform:)]) {
transformedEvent = [transformer transform:transformedEvent];
if (!transformedEvent) {
return;
}
} else {
GDTCORLogError(GDTCORMCETransformerDoesntImplementTransform,
@"Transformer doesn't implement transform: %@", transformer);
return;
}
}
[self.storageInstance storeEvent:transformedEvent];
if (self->_runningInBackground) {
[[GDTCORApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
});
}
#pragma mark - GDTCORLifecycleProtocol
- (void)appWillForeground:(GDTCORApplication *)app {
dispatch_async(_eventWritingQueue, ^{
self->_runningInBackground = NO;
});
}
- (void)appWillBackground:(GDTCORApplication *)app {
// Create an immediate background task to run until the end of the current queue of work.
__block GDTCORBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
}];
dispatch_async(_eventWritingQueue, ^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
});
}
- (void)appWillTerminate:(GDTCORApplication *)application {
// Flush the queue immediately.
dispatch_sync(_eventWritingQueue, ^{
});
}
@end
@@ -0,0 +1,68 @@
/*
* 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.
*/
#import <GoogleDataTransport/GDTCORTransport.h>
#import "GDTCORLibrary/Private/GDTCORTransport_Private.h"
#import <GoogleDataTransport/GDTCORAssert.h>
#import <GoogleDataTransport/GDTCORClock.h>
#import <GoogleDataTransport/GDTCOREvent.h>
#import "GDTCORLibrary/Private/GDTCORTransformer.h"
@implementation GDTCORTransport
- (instancetype)initWithMappingID:(NSString *)mappingID
transformers:(nullable NSArray<id<GDTCOREventTransformer>> *)transformers
target:(NSInteger)target {
GDTCORAssert(mappingID.length > 0, @"A mapping ID cannot be nil or empty");
GDTCORAssert(target > 0, @"A target cannot be negative or 0");
if (mappingID == nil || mappingID.length == 0 || target <= 0) {
return nil;
}
self = [super init];
if (self) {
_mappingID = mappingID;
_transformers = transformers;
_target = target;
_transformerInstance = [GDTCORTransformer sharedInstance];
}
return self;
}
- (void)sendTelemetryEvent:(GDTCOREvent *)event {
// TODO: Determine if sending an event before registration is allowed.
GDTCORAssert(event, @"You can't send a nil event");
GDTCOREvent *copiedEvent = [event copy];
copiedEvent.qosTier = GDTCOREventQoSTelemetry;
copiedEvent.clockSnapshot = [GDTCORClock snapshot];
[self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers];
}
- (void)sendDataEvent:(GDTCOREvent *)event {
// TODO: Determine if sending an event before registration is allowed.
GDTCORAssert(event, @"You can't send a nil event");
GDTCORAssert(event.qosTier != GDTCOREventQoSTelemetry, @"Use -sendTelemetryEvent, please.");
GDTCOREvent *copiedEvent = [event copy];
copiedEvent.clockSnapshot = [GDTCORClock snapshot];
[self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers];
}
- (GDTCOREvent *)eventForTransport {
return [[GDTCOREvent alloc] initWithMappingID:_mappingID target:_target];
}
@end
@@ -0,0 +1,274 @@
/*
* 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.
*/
#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
#import <GoogleDataTransport/GDTCORAssert.h>
#import <GoogleDataTransport/GDTCORClock.h>
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
#import "GDTCORLibrary/Private/GDTCORReachability.h"
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
#import "GDTCORLibrary/Private/GDTCORStorage.h"
@implementation GDTCORUploadCoordinator
+ (instancetype)sharedInstance {
static GDTCORUploadCoordinator *sharedUploader;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedUploader = [[GDTCORUploadCoordinator alloc] init];
[sharedUploader startTimer];
});
return sharedUploader;
}
- (instancetype)init {
self = [super init];
if (self) {
_coordinationQueue =
dispatch_queue_create("com.google.GDTCORUploadCoordinator", DISPATCH_QUEUE_SERIAL);
_registrar = [GDTCORRegistrar sharedInstance];
_timerInterval = 30 * NSEC_PER_SEC;
_timerLeeway = 5 * NSEC_PER_SEC;
_targetToInFlightPackages = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)forceUploadForTarget:(GDTCORTarget)target {
dispatch_async(_coordinationQueue, ^{
GDTCORUploadConditions conditions = [self uploadConditions];
conditions |= GDTCORUploadConditionHighPriority;
[self uploadTargets:@[ @(target) ] conditions:conditions];
});
}
#pragma mark - Property overrides
// GDTCORStorage and GDTCORUploadCoordinator +sharedInstance methods call each other, so this breaks
// the loop.
- (GDTCORStorage *)storage {
if (!_storage) {
_storage = [GDTCORStorage sharedInstance];
}
return _storage;
}
#pragma mark - Private helper methods
/** Starts a timer that checks whether or not events can be uploaded at regular intervals. It will
* check the next-upload clocks of all targets to determine if an upload attempt can be made.
*/
- (void)startTimer {
dispatch_sync(_coordinationQueue, ^{
self->_timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self->_coordinationQueue);
dispatch_source_set_timer(self->_timer, DISPATCH_TIME_NOW, self->_timerInterval,
self->_timerLeeway);
dispatch_source_set_event_handler(self->_timer, ^{
if (!self->_runningInBackground) {
GDTCORUploadConditions conditions = [self uploadConditions];
[self uploadTargets:[self.registrar.targetToUploader allKeys] conditions:conditions];
}
});
dispatch_resume(self->_timer);
});
}
/** Stops the currently running timer. */
- (void)stopTimer {
if (_timer) {
dispatch_source_cancel(_timer);
}
}
/** Triggers the uploader implementations for the given targets to upload.
*
* @param targets An array of targets to trigger.
* @param conditions The set of upload conditions.
*/
- (void)uploadTargets:(NSArray<NSNumber *> *)targets conditions:(GDTCORUploadConditions)conditions {
dispatch_async(_coordinationQueue, ^{
if ((conditions & GDTCORUploadConditionNoNetwork) == GDTCORUploadConditionNoNetwork) {
return;
}
for (NSNumber *target in targets) {
// Don't trigger uploads for targets that have an in-flight package already.
if (self->_targetToInFlightPackages[target]) {
continue;
}
// Ask the uploader if they can upload and do so, if it can.
id<GDTCORUploader> uploader = self.registrar.targetToUploader[target];
if ([uploader readyToUploadWithConditions:conditions]) {
id<GDTCORPrioritizer> prioritizer = self.registrar.targetToPrioritizer[target];
GDTCORUploadPackage *package = [prioritizer uploadPackageWithConditions:conditions];
if (package.events.count) {
self->_targetToInFlightPackages[target] = package;
[uploader uploadPackage:package];
} else {
[package completeDelivery];
}
}
}
});
}
/** Returns the current upload conditions after making determinations about the network connection.
*
* @return The current upload conditions.
*/
- (GDTCORUploadConditions)uploadConditions {
SCNetworkReachabilityFlags currentFlags = [GDTCORReachability currentFlags];
BOOL reachable =
(currentFlags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable;
BOOL connectionRequired = (currentFlags & kSCNetworkReachabilityFlagsConnectionRequired) ==
kSCNetworkReachabilityFlagsConnectionRequired;
BOOL networkConnected = reachable && !connectionRequired;
if (!networkConnected) {
return GDTCORUploadConditionNoNetwork;
}
BOOL isWWAN = GDTCORReachabilityFlagsContainWWAN(currentFlags);
if (isWWAN) {
return GDTCORUploadConditionMobileData;
} else {
return GDTCORUploadConditionWifiData;
}
}
#pragma mark - NSSecureCoding support
/** The NSKeyedCoder key for the targetToInFlightPackages property. */
static NSString *const ktargetToInFlightPackagesKey =
@"GDTCORUploadCoordinatortargetToInFlightPackages";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
GDTCORUploadCoordinator *sharedCoordinator = [GDTCORUploadCoordinator sharedInstance];
@try {
sharedCoordinator->_targetToInFlightPackages =
[aDecoder decodeObjectOfClass:[NSMutableDictionary class]
forKey:ktargetToInFlightPackagesKey];
} @catch (NSException *exception) {
sharedCoordinator->_targetToInFlightPackages = [NSMutableDictionary dictionary];
}
return sharedCoordinator;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
// All packages that have been given to uploaders need to be tracked so that their expiration
// timers can be called.
if (_targetToInFlightPackages.count > 0) {
[aCoder encodeObject:_targetToInFlightPackages forKey:ktargetToInFlightPackagesKey];
}
}
#pragma mark - GDTCORLifecycleProtocol
- (void)appWillForeground:(GDTCORApplication *)app {
// Not entirely thread-safe, but it should be fine.
self->_runningInBackground = NO;
[self startTimer];
}
- (void)appWillBackground:(GDTCORApplication *)app {
// Not entirely thread-safe, but it should be fine.
self->_runningInBackground = YES;
// Should be thread-safe. If it ends up not being, put this in a dispatch_sync.
[self stopTimer];
// Create an immediate background task to run until the end of the current queue of work.
__block GDTCORBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
}];
dispatch_async(_coordinationQueue, ^{
if (bgID != GDTCORBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTCORBackgroundIdentifierInvalid;
}
});
}
- (void)appWillTerminate:(GDTCORApplication *)application {
dispatch_sync(_coordinationQueue, ^{
[self stopTimer];
});
}
#pragma mark - GDTCORUploadPackageProtocol
- (void)packageDelivered:(GDTCORUploadPackage *)package successful:(BOOL)successful {
if (!_coordinationQueue) {
return;
}
dispatch_async(_coordinationQueue, ^{
NSNumber *targetNumber = @(package.target);
NSMutableDictionary<NSNumber *, GDTCORUploadPackage *> *targetToInFlightPackages =
self->_targetToInFlightPackages;
GDTCORRegistrar *registrar = self->_registrar;
if (targetToInFlightPackages) {
[targetToInFlightPackages removeObjectForKey:targetNumber];
}
if (registrar) {
id<GDTCORPrioritizer> prioritizer = registrar.targetToPrioritizer[targetNumber];
if (!prioritizer) {
GDTCORLogError(GDTCORMCEPrioritizerError,
@"A prioritizer should be registered for this target: %@", targetNumber);
}
if ([prioritizer respondsToSelector:@selector(packageDelivered:successful:)]) {
[prioritizer packageDelivered:package successful:successful];
}
}
[self.storage removeEvents:package.events];
});
}
- (void)packageExpired:(GDTCORUploadPackage *)package {
if (!_coordinationQueue) {
return;
}
dispatch_async(_coordinationQueue, ^{
NSNumber *targetNumber = @(package.target);
NSMutableDictionary<NSNumber *, GDTCORUploadPackage *> *targetToInFlightPackages =
self->_targetToInFlightPackages;
GDTCORRegistrar *registrar = self->_registrar;
if (targetToInFlightPackages) {
[targetToInFlightPackages removeObjectForKey:targetNumber];
}
if (registrar) {
id<GDTCORPrioritizer> prioritizer = registrar.targetToPrioritizer[targetNumber];
id<GDTCORUploader> uploader = registrar.targetToUploader[targetNumber];
if ([prioritizer respondsToSelector:@selector(packageExpired:)]) {
[prioritizer packageExpired:package];
}
if ([uploader respondsToSelector:@selector(packageExpired:)]) {
[uploader packageExpired:package];
}
}
});
}
@end
@@ -0,0 +1,154 @@
/*
* Copyright 2019 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.
*/
#import <GoogleDataTransport/GDTCORUploadPackage.h>
#import <GoogleDataTransport/GDTCORClock.h>
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
#import <GoogleDataTransport/GDTCORStoredEvent.h>
#import "GDTCORLibrary/Private/GDTCORStorage_Private.h"
#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
#import "GDTCORLibrary/Private/GDTCORUploadPackage_Private.h"
@implementation GDTCORUploadPackage {
/** If YES, the package's -completeDelivery method has been called. */
BOOL _isDelivered;
/** If YES, is being handled by the handler. */
BOOL _isHandled;
/** A timer that will regularly check to see whether this package has expired or not. */
NSTimer *_expirationTimer;
}
- (instancetype)initWithTarget:(GDTCORTarget)target {
self = [super init];
if (self) {
_target = target;
_storage = [GDTCORStorage sharedInstance];
_deliverByTime = [GDTCORClock clockSnapshotInTheFuture:180000];
_handler = [GDTCORUploadCoordinator sharedInstance];
_expirationTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(checkIfPackageIsExpired:)
userInfo:nil
repeats:YES];
}
return self;
}
- (instancetype)copy {
GDTCORUploadPackage *newPackage = [[GDTCORUploadPackage alloc] initWithTarget:_target];
newPackage->_events = [_events copy];
return newPackage;
}
- (NSUInteger)hash {
return [_events hash];
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
- (void)dealloc {
[_expirationTimer invalidate];
}
- (void)setStorage:(GDTCORStorage *)storage {
if (storage != _storage) {
_storage = storage;
}
}
- (void)completeDelivery {
if (_isDelivered) {
GDTCORLogError(GDTCORMCEDeliverTwice, @"%@",
@"It's an API violation to call -completeDelivery twice.");
}
_isDelivered = YES;
if (!_isHandled && _handler &&
[_handler respondsToSelector:@selector(packageDelivered:successful:)]) {
[_expirationTimer invalidate];
_isHandled = YES;
[_handler packageDelivered:self successful:YES];
}
}
- (void)retryDeliveryInTheFuture {
if (!_isHandled && _handler &&
[_handler respondsToSelector:@selector(packageDelivered:successful:)]) {
[_expirationTimer invalidate];
_isHandled = YES;
[_handler packageDelivered:self successful:NO];
}
}
- (void)checkIfPackageIsExpired:(NSTimer *)timer {
if ([[GDTCORClock snapshot] isAfter:_deliverByTime]) {
if (_handler && [_handler respondsToSelector:@selector(packageExpired:)]) {
_isHandled = YES;
[_expirationTimer invalidate];
[_handler packageExpired:self];
}
}
}
#pragma mark - NSSecureCoding
/** The keyed archiver key for the events property. */
static NSString *const kEventsKey = @"GDTCORUploadPackageEventsKey";
/** The keyed archiver key for the _isHandled property. */
static NSString *const kDeliverByTimeKey = @"GDTCORUploadPackageDeliveryByTimeKey";
/** The keyed archiver key for the _isHandled ivar. */
static NSString *const kIsHandledKey = @"GDTCORUploadPackageIsHandledKey";
/** The keyed archiver key for the handler property. */
static NSString *const kHandlerKey = @"GDTCORUploadPackageHandlerKey";
/** The keyed archiver key for the target property. */
static NSString *const kTargetKey = @"GDTCORUploadPackageTargetKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_events forKey:kEventsKey];
[aCoder encodeObject:_deliverByTime forKey:kDeliverByTimeKey];
[aCoder encodeBool:_isHandled forKey:kIsHandledKey];
[aCoder encodeObject:_handler forKey:kHandlerKey];
[aCoder encodeInteger:_target forKey:kTargetKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
GDTCORTarget target = [aDecoder decodeIntegerForKey:kTargetKey];
self = [self initWithTarget:target];
if (self) {
NSSet *classes = [NSSet setWithObjects:[NSSet class], [GDTCORStoredEvent class], nil];
_events = [aDecoder decodeObjectOfClasses:classes forKey:kEventsKey];
_deliverByTime = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:kDeliverByTimeKey];
_isHandled = [aDecoder decodeBoolForKey:kIsHandledKey];
// _handler isn't technically NSSecureCoding, because we don't know the class of this object.
// but it gets decoded anyway.
}
return self;
}
@end
@@ -0,0 +1,30 @@
/*
* 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.
*/
#import <GoogleDataTransport/GDTCOREvent.h>
#import <GoogleDataTransport/GDTCORClock.h>
NS_ASSUME_NONNULL_BEGIN
@interface GDTCOREvent ()
/** The serialized bytes of the event data object. */
@property(nonatomic) NSData *dataObjectTransportBytes;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,31 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SCNetworkReachability.h>
NS_ASSUME_NONNULL_BEGIN
/** This class helps determine upload conditions by determining connectivity. */
@interface GDTCORReachability : NSObject
/** The current set flags indicating network conditions */
+ (SCNetworkReachabilityFlags)currentFlags;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,30 @@
/*
* Copyright 2019 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.
*/
#import "GDTCORLibrary/Private/GDTCORReachability.h"
@interface GDTCORReachability ()
/** Allows manually setting the flags for testing purposes. */
@property(nonatomic, readwrite) SCNetworkReachabilityFlags flags;
/** Creates/returns the singleton instance of this class.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance;
@end
@@ -0,0 +1,35 @@
/*
* 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.
*/
#import <GoogleDataTransport/GDTCORRegistrar.h>
@interface GDTCORRegistrar ()
NS_ASSUME_NONNULL_BEGIN
/** The concurrent queue on which all registration occurs. */
@property(nonatomic, readonly) dispatch_queue_t registrarQueue;
/** A map of targets to backend implementations. */
@property(atomic, readonly) NSMutableDictionary<NSNumber *, id<GDTCORUploader>> *targetToUploader;
/** A map of targets to prioritizer implementations. */
@property(atomic, readonly)
NSMutableDictionary<NSNumber *, id<GDTCORPrioritizer>> *targetToPrioritizer;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,50 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
@class GDTCOREvent;
@class GDTCORStoredEvent;
NS_ASSUME_NONNULL_BEGIN
/** Manages the storage of events. This class is thread-safe. */
@interface GDTCORStorage : NSObject <NSSecureCoding, GDTCORLifecycleProtocol>
/** Creates and/or returns the storage singleton.
*
* @return The storage singleton.
*/
+ (instancetype)sharedInstance;
/** Stores event.dataObjectTransportBytes into a shared on-device folder and tracks the event via
* a GDTCORStoredEvent instance.
*
* @param event The event to store.
*/
- (void)storeEvent:(GDTCOREvent *)event;
/** Removes a set of events from storage specified by their hash.
*
* @param events The set of stored events to remove.
*/
- (void)removeEvents:(NSSet<GDTCORStoredEvent *> *)events;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,51 @@
/*
* 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.
*/
#import "GDTCORLibrary/Private/GDTCORStorage.h"
@class GDTCORUploadCoordinator;
NS_ASSUME_NONNULL_BEGIN
@interface GDTCORStorage ()
/** The queue on which all storage work will occur. */
@property(nonatomic) dispatch_queue_t storageQueue;
/** A map of targets to a set of stored events. */
@property(nonatomic)
NSMutableDictionary<NSNumber *, NSMutableSet<GDTCORStoredEvent *> *> *targetToEventSet;
/** All the events that have been stored. */
@property(readonly, nonatomic) NSMutableOrderedSet<GDTCORStoredEvent *> *storedEvents;
/** The upload coordinator instance used by this storage instance. */
@property(nonatomic) GDTCORUploadCoordinator *uploadCoordinator;
/** If YES, every call to -storeLog results in background task and serializes the singleton to disk.
*/
@property(nonatomic) BOOL runningInBackground;
/** Returns the path to the keyed archive of the singleton. This is where the singleton is saved
* to disk during certain app lifecycle events.
*
* @return File path to serialized singleton.
*/
+ (NSString *)archivePath;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,54 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
@class GDTCOREvent;
@protocol GDTCOREventTransformer;
NS_ASSUME_NONNULL_BEGIN
/** Manages the transforming of events. It's desirable for this to be its own class
* because running all events through a single instance ensures that transformers are thread-safe.
* Having a per-transport queue to run on isn't sufficient because transformer objects could
* maintain state (or at least, there's nothing to stop them from doing that) and the same instances
* may be used across multiple instances.
*/
@interface GDTCORTransformer : NSObject <GDTCORLifecycleProtocol>
/** Instantiates or returns the event transformer singleton.
*
* @return The singleton instance of the event transformer.
*/
+ (instancetype)sharedInstance;
/** Writes the result of applying the given transformers' -transform method on the given event.
*
* @note If the app is suspended, a background task will be created to complete work in-progress,
* but this method will not send any further events until the app is resumed.
*
* @param event The event to apply transformers on.
* @param transformers The list of transformers to apply.
*/
- (void)transformEvent:(GDTCOREvent *)event
withTransformers:(nullable NSArray<id<GDTCOREventTransformer>> *)transformers;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,36 @@
/*
* 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.
*/
#import "GDTCORLibrary/Private/GDTCORTransformer.h"
@class GDTCORStorage;
NS_ASSUME_NONNULL_BEGIN
@interface GDTCORTransformer ()
/** The queue on which all work will occur. */
@property(nonatomic) dispatch_queue_t eventWritingQueue;
/** The storage instance used to store events. Should only be used to inject a testing fake. */
@property(nonatomic) GDTCORStorage *storageInstance;
/** If YES, every call to -transformEvent will result in a background task. */
@property(nonatomic, readonly) BOOL runningInBackground;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,39 @@
/*
* Copyright 2019 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.
*/
#import <GoogleDataTransport/GDTCORTransport.h>
@class GDTCORTransformer;
NS_ASSUME_NONNULL_BEGIN
@interface GDTCORTransport ()
/** The mapping identifier that the target backend will use to map the transport bytes to proto. */
@property(nonatomic) NSString *mappingID;
/** The transformers that will operate on events sent by this transport. */
@property(nonatomic) NSArray<id<GDTCOREventTransformer>> *transformers;
/** The target backend of this transport. */
@property(nonatomic) NSInteger target;
/** The transformer instance to used to transform events. Allows injecting a fake during testing. */
@property(nonatomic) GDTCORTransformer *transformerInstance;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,80 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
#import <GoogleDataTransport/GDTCORRegistrar.h>
#import "GDTCORLibrary/Private/GDTCORUploadPackage_Private.h"
@class GDTCORClock;
@class GDTCORStorage;
NS_ASSUME_NONNULL_BEGIN
/** This class connects storage and uploader implementations, providing events to an uploader
* and informing the storage what events were successfully uploaded or not.
*/
@interface GDTCORUploadCoordinator
: NSObject <NSSecureCoding, GDTCORLifecycleProtocol, GDTCORUploadPackageProtocol>
/** The queue on which all upload coordination will occur. Also used by a dispatch timer. */
/** Creates and/or returrns the singleton.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance;
@property(nonatomic, readonly) dispatch_queue_t coordinationQueue;
/** A timer that will causes regular checks for events to upload. */
@property(nonatomic, readonly) dispatch_source_t timer;
/** The interval the timer will fire. */
@property(nonatomic, readonly) uint64_t timerInterval;
/** Some leeway given to libdispatch for the timer interval event. */
@property(nonatomic, readonly) uint64_t timerLeeway;
/** The map of targets to in-flight packages. */
@property(nonatomic, readonly)
NSMutableDictionary<NSNumber *, GDTCORUploadPackage *> *targetToInFlightPackages;
/** The storage object the coordinator will use. Generally used for testing. */
@property(nonatomic) GDTCORStorage *storage;
/** The registrar object the coordinator will use. Generally used for testing. */
@property(nonatomic) GDTCORRegistrar *registrar;
/** If YES, completion and other operations will result in serializing the singleton to disk. */
@property(nonatomic, readonly) BOOL runningInBackground;
/** Forces the backend specified by the target to upload the provided set of events. This should
* only ever happen when the QoS tier of an event requires it.
*
* @param target The target that should force an upload.
*/
- (void)forceUploadForTarget:(GDTCORTarget)target;
/** Starts the upload timer. */
- (void)startTimer;
/** Stops the upload timer from running. */
- (void)stopTimer;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,29 @@
/*
* Copyright 2019 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.
*/
#import <GoogleDataTransport/GDTCORUploadPackage.h>
@class GDTCORStorage;
@interface GDTCORUploadPackage ()
/** The storage object this upload package will use to resolve event hashes to files. */
@property(nonatomic) GDTCORStorage *storage;
/** A handler that will receive callbacks for certain events. */
@property(nonatomic) id<NSSecureCoding, GDTCORUploadPackageProtocol> handler;
@end
@@ -0,0 +1,91 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
/** A block type that could be run instead of normal assertion logging. No return type, no params.
*/
typedef void (^GDTCORAssertionBlock)(void);
/** Returns the result of executing a soft-linked method present in unit tests that allows a block
* to be run instead of normal assertion logging. This helps ameliorate issues with catching
* exceptions that occur on a dispatch_queue.
*
* @return A block that can be run instead of normal assert printing.
*/
FOUNDATION_EXPORT GDTCORAssertionBlock _Nullable GDTCORAssertionBlockToRunInstead(void);
#if defined(NS_BLOCK_ASSERTIONS)
#define GDTCORAssert(condition, ...) \
do { \
} while (0);
#define GDTCORFatalAssert(condition, ...) \
do { \
} while (0);
#else // defined(NS_BLOCK_ASSERTIONS)
/** Asserts using a console log, unless a block was specified to be run instead.
*
* @param condition The condition you'd expect to be YES.
*/
#define GDTCORAssert(condition, ...) \
do { \
if (__builtin_expect(!(condition), 0)) { \
GDTCORAssertionBlock assertionBlock = GDTCORAssertionBlockToRunInstead(); \
if (assertionBlock) { \
assertionBlock(); \
} else { \
__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \
__assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \
GDTCORLogError(GDTCORMCEGeneralError, @"Assertion failed (%@:%d): %s,", __assert_file__, \
__LINE__, ##__VA_ARGS__); \
__PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
} \
} \
} while (0);
/** Asserts by logging to the console and throwing an exception if NS_BLOCK_ASSERTIONS is not
* defined.
*
* @param condition The condition you'd expect to be YES.
*/
#define GDTCORFatalAssert(condition, ...) \
do { \
__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
if (__builtin_expect(!(condition), 0)) { \
NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \
__assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \
GDTCORLogError(GDTCORMCEFatalAssertion, \
@"Fatal assertion encountered, please open an issue at " \
"https://github.com/firebase/firebase-ios-sdk/issues " \
"(%@:%d): %s,", \
__assert_file__, __LINE__, ##__VA_ARGS__); \
[[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
object:self \
file:__assert_file__ \
lineNumber:__LINE__ \
description:@"%@", ##__VA_ARGS__]; \
} \
__PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
} while (0);
#endif // defined(NS_BLOCK_ASSERTIONS)
@@ -0,0 +1,57 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** This class manages the device clock and produces snapshots of the current time. */
@interface GDTCORClock : NSObject <NSSecureCoding>
/** The wallclock time, UTC, in milliseconds. */
@property(nonatomic, readonly) int64_t timeMillis;
/** The offset from UTC in seconds. */
@property(nonatomic, readonly) int64_t timezoneOffsetSeconds;
/** The kernel boot time when this clock was created. */
@property(nonatomic, readonly) int64_t kernelBootTime;
/** The device uptime when this clock was created. */
@property(nonatomic, readonly) int64_t uptime;
/** Creates a GDTCORClock object using the current time and offsets.
*
* @return A new GDTCORClock object representing the current time state.
*/
+ (instancetype)snapshot;
/** Creates a GDTCORClock object representing a time in the future, relative to now.
*
* @param millisInTheFuture The millis in the future from now this clock should represent.
* @return An instance representing a future time.
*/
+ (instancetype)clockSnapshotInTheFuture:(uint64_t)millisInTheFuture;
/** Compares one clock with another, returns YES if the caller is after the parameter.
*
* @return YES if the calling clock's time is after the given clock's time.
*/
- (BOOL)isAfter:(GDTCORClock *)otherClock;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,84 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
/** A list of message codes to print in the logger that help to correspond printed messages with
* code locations.
*
* Prefixes:
* - MCW => MessageCodeWarning
* - MCE => MessageCodeError
*/
typedef NS_ENUM(NSInteger, GDTCORMessageCode) {
/** For warning messages concerning transportBytes: not being implemented by a data object. */
GDTCORMCWDataObjectMissingBytesImpl = 1,
/** For warning messages concerning a failed event upload. */
GDTCORMCWUploadFailed = 2,
/** For warning messages concerning a forced event upload. */
GDTCORMCWForcedUpload = 3,
/** For warning messages concerning a failed reachability call. */
GDTCORMCWReachabilityFailed = 4,
/** For error messages concerning transform: not being implemented by an event transformer. */
GDTCORMCETransformerDoesntImplementTransform = 1000,
/** For error messages concerning the creation of a directory failing. */
GDTCORMCEDirectoryCreationError = 1001,
/** For error messages concerning the writing of a event file. */
GDTCORMCEFileWriteError = 1002,
/** For error messages concerning the lack of a prioritizer for a given backend. */
GDTCORMCEPrioritizerError = 1003,
/** For error messages concerning a package delivery API violation. */
GDTCORMCEDeliverTwice = 1004,
/** For error messages concerning an error in an implementation of -transportBytes. */
GDTCORMCETransportBytesError = 1005,
/** For general purpose error messages in a dependency. */
GDTCORMCEGeneralError = 1006,
/** For fatal errors. Please go to https://github.com/firebase/firebase-ios-sdk/issues and open
* an issue if you encounter an error with this code.
*/
GDTCORMCEFatalAssertion = 1007
};
/** */
FOUNDATION_EXPORT
void GDTCORLog(GDTCORMessageCode code, NSString *_Nonnull format, ...);
/** Returns the string that represents some message code.
*
* @param code The code to convert to a string.
* @return The string representing the message code.
*/
FOUNDATION_EXPORT NSString *_Nonnull GDTCORMessageCodeEnumToString(GDTCORMessageCode code);
// A define to wrap GULLogWarning with slightly more convenient usage.
#define GDTCORLogWarning(MESSAGE_CODE, MESSAGE_FORMAT, ...) \
GDTCORLog(MESSAGE_CODE, MESSAGE_FORMAT, __VA_ARGS__);
// A define to wrap GULLogError with slightly more convenient usage and a failing assert.
#define GDTCORLogError(MESSAGE_CODE, MESSAGE_FORMAT, ...) \
GDTCORLog(MESSAGE_CODE, MESSAGE_FORMAT, __VA_ARGS__);
@@ -0,0 +1,42 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** This class represents a future data object, determined at instantiation time. */
@interface GDTCORDataFuture : NSObject <NSSecureCoding>
/** The data, computed on-demand, depending on the initializer. */
@property(nullable, readonly, nonatomic) NSData *data;
/** If not nil, this data future was instantiated with this file URL. */
@property(nullable, readonly, nonatomic) NSURL *fileURL;
/** If not nil, this data future was instantiated with this NSData instance. */
@property(nullable, readonly, nonatomic) NSData *originalData;
/** Initializes an instance with the given the fileURL.
*
* @param fileURL The fileURL containing the data to return in -data.
* @return An instance of this class.
*/
- (instancetype)initWithFileURL:(NSURL *)fileURL;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,94 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCOREventDataObject.h>
@class GDTCORClock;
@class GDTCORDataFuture;
@class GDTCORStoredEvent;
NS_ASSUME_NONNULL_BEGIN
/** The different possible quality of service specifiers. High values indicate high priority. */
typedef NS_ENUM(NSInteger, GDTCOREventQoS) {
/** The QoS tier wasn't set, and won't ever be sent. */
GDTCOREventQoSUnknown = 0,
/** This event is internal telemetry data that should not be sent on its own if possible. */
GDTCOREventQoSTelemetry = 1,
/** This event should be sent, but in a batch only roughly once per day. */
GDTCOREventQoSDaily = 2,
/** This event should be sent when requested by the uploader. */
GDTCOREventQosDefault = 3,
/** This event should be sent immediately along with any other data that can be batched. */
GDTCOREventQoSFast = 4,
/** This event should only be uploaded on wifi. */
GDTCOREventQoSWifiOnly = 5,
};
@interface GDTCOREvent : NSObject <NSSecureCoding>
/** The mapping identifier, to allow backends to map the transport bytes to a proto. */
@property(readonly, nonatomic) NSString *mappingID;
/** The identifier for the backend this event will eventually be sent to. */
@property(readonly, nonatomic) NSInteger target;
/** The data object encapsulated in the transport of your choice, as long as it implements
* the GDTCOREventDataObject protocol. */
@property(nullable, nonatomic) id<GDTCOREventDataObject> dataObject;
/** The quality of service tier this event belongs to. */
@property(nonatomic) GDTCOREventQoS qosTier;
/** The clock snapshot at the time of the event. */
@property(nonatomic) GDTCORClock *clockSnapshot;
/** A dictionary provided to aid prioritizers by allowing the passing of arbitrary data. It will be
* retained by a copy in -copy, but not used for -hash.
*
* @note Ensure that classes contained therein implement NSSecureCoding to prevent loss of data.
*/
@property(nullable, nonatomic) NSDictionary *customPrioritizationParams;
// Please use the designated initializer.
- (instancetype)init NS_UNAVAILABLE;
/** Initializes an instance using the given mappingID.
*
* @param mappingID The mapping identifier.
* @param target The event's target identifier.
* @return An instance of this class.
*/
- (instancetype)initWithMappingID:(NSString *)mappingID
target:(NSInteger)target NS_DESIGNATED_INITIALIZER;
/** Returns the GDTCORStoredEvent equivalent of self.
*
* @param dataFuture The data future representing the transport bytes of the original event.
* @return An equivalent GDTCORStoredEvent.
*/
- (GDTCORStoredEvent *)storedEventWithDataFuture:(GDTCORDataFuture *)dataFuture;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,36 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** This protocol defines the common interface that event protos should implement regardless of the
* underlying transport technology (protobuf, nanopb, etc).
*/
@protocol GDTCOREventDataObject <NSObject>
@required
/** Returns the serialized proto bytes of the implementing event proto.
*
* @return the serialized proto bytes of the implementing event proto.
*/
- (NSData *)transportBytes;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,38 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@class GDTCOREvent;
NS_ASSUME_NONNULL_BEGIN
/** Defines the API that event transformers must adopt. */
@protocol GDTCOREventTransformer <NSObject>
@required
/** Transforms an event by applying some logic to it. Events returned can be nil, for example, in
* instances where the event should be sampled.
*
* @param event The event to transform.
* @return A transformed event, or nil if the transformation dropped the event.
*/
- (GDTCOREvent *)transform:(GDTCOREvent *)event;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,63 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORPlatform.h>
@class GDTCOREvent;
NS_ASSUME_NONNULL_BEGIN
/** A protocol defining the lifecycle events objects in the library must respond to immediately. */
@protocol GDTCORLifecycleProtocol <NSObject>
@optional
/** Indicates an imminent app termination in the rare occurrence when -applicationWillTerminate: has
* been called.
*
* @param app The GDTCORApplication instance.
*/
- (void)appWillTerminate:(GDTCORApplication *)app;
/** Indicates that the app is moving to background and eventual suspension or the current UIScene is
* deactivating.
*
* @param app The GDTCORApplication instance.
*/
- (void)appWillBackground:(GDTCORApplication *)app;
/** Indicates that the app is resuming operation or a UIScene is activating.
*
* @param app The GDTCORApplication instance.
*/
- (void)appWillForeground:(GDTCORApplication *)app;
@end
/** This class manages the library's response to app lifecycle events.
*
* When backgrounding, the library doesn't stop processing events, it's just that several background
* tasks will end up being created for every event that's sent, and the stateful objects of the
* library (GDTCORStorage and GDTCORUploadCoordinator singletons) will deserialize themselves from
* and to disk before and after every operation, respectively.
*/
@interface GDTCORLifecycle : NSObject <GDTCORApplicationDelegate>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,87 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
#elif TARGET_OS_OSX
#import <AppKit/AppKit.h>
#endif // TARGET_OS_IOS || TARGET_OS_TV
NS_ASSUME_NONNULL_BEGIN
/** A notification sent out if the app is backgrounding. */
FOUNDATION_EXPORT NSString *const kGDTCORApplicationDidEnterBackgroundNotification;
/** A notification sent out if the app is foregrounding. */
FOUNDATION_EXPORT NSString *const kGDTCORApplicationWillEnterForegroundNotification;
/** A notification sent out if the app is terminating. */
FOUNDATION_EXPORT NSString *const kGDTCORApplicationWillTerminateNotification;
/** Compares flags with the WWAN reachability flag, if available, and returns YES if present.
*
* @param flags The set of reachability flags.
* @return YES if the WWAN flag is set, NO otherwise.
*/
BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags);
/** A typedef identify background identifiers. */
typedef volatile NSUInteger GDTCORBackgroundIdentifier;
/** A background task's invalid sentinel value. */
FOUNDATION_EXPORT const GDTCORBackgroundIdentifier GDTCORBackgroundIdentifierInvalid;
#if TARGET_OS_IOS || TARGET_OS_TV
/** A protocol that wraps UIApplicationDelegate or NSObject protocol, depending on the platform. */
@protocol GDTCORApplicationDelegate <UIApplicationDelegate>
#elif TARGET_OS_OSX
@protocol GDTCORApplicationDelegate <NSApplicationDelegate>
#else
@protocol GDTCORApplicationDelegate <NSObject>
#endif // TARGET_OS_IOS || TARGET_OS_TV
@end
/** A cross-platform application class. */
@interface GDTCORApplication : NSObject <GDTCORApplicationDelegate>
/** Creates and/or returns the shared application instance.
*
* @return The shared application instance.
*/
+ (nullable GDTCORApplication *)sharedApplication;
/** Creates a background task with the returned identifier if on a suitable platform.
*
* @param handler The handler block that is called if the background task expires.
* @return An identifier for the background task, or GDTCORBackgroundIdentifierInvalid if one
* couldn't be created.
*/
- (GDTCORBackgroundIdentifier)beginBackgroundTaskWithExpirationHandler:
(void (^__nullable)(void))handler;
/** Ends the background task if the identifier is valid.
*
* @param bgID The background task to end.
*/
- (void)endBackgroundTask:(GDTCORBackgroundIdentifier)bgID;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,73 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
#import <GoogleDataTransport/GDTCORUploadPackage.h>
@class GDTCORStoredEvent;
NS_ASSUME_NONNULL_BEGIN
/** Options that define a set of upload conditions. This is used to help minimize end user data
* consumption impact.
*/
typedef NS_OPTIONS(NSInteger, GDTCORUploadConditions) {
/** An upload shouldn't be attempted, because there's no network. */
GDTCORUploadConditionNoNetwork = 1 << 0,
/** An upload would likely use mobile data. */
GDTCORUploadConditionMobileData = 1 << 1,
/** An upload would likely use wifi data. */
GDTCORUploadConditionWifiData = 1 << 2,
/** An upload uses some sort of network connection, but it's unclear which. */
GDTCORUploadConditionUnclearConnection = 1 << 3,
/** A high priority event has occurred. */
GDTCORUploadConditionHighPriority = 1 << 4,
};
/** This protocol defines the common interface of event prioritization. Prioritizers are
* stateful objects that prioritize events upon insertion into storage and remain prepared to return
* a set of filenames to the storage system.
*/
@protocol GDTCORPrioritizer <NSObject, GDTCORLifecycleProtocol, GDTCORUploadPackageProtocol>
@required
/** Accepts an event and uses the event metadata to make choices on how to prioritize the event.
* This method exists as a way to help prioritize which events should be sent, which is dependent on
* the request proto structure of your backend.
*
* @param event The event to prioritize.
*/
- (void)prioritizeEvent:(GDTCORStoredEvent *)event;
/** Returns a set of events to upload given a set of conditions.
*
* @param conditions A bit mask specifying the current upload conditions.
* @return An object to be used by the uploader to determine file URLs to upload with respect to the
* current conditions.
*/
- (GDTCORUploadPackage *)uploadPackageWithConditions:(GDTCORUploadConditions)conditions;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,50 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORPrioritizer.h>
#import <GoogleDataTransport/GDTCORTargets.h>
#import <GoogleDataTransport/GDTCORUploader.h>
NS_ASSUME_NONNULL_BEGIN
/** Manages the registration of targets with the transport SDK. */
@interface GDTCORRegistrar : NSObject <GDTCORLifecycleProtocol>
/** Creates and/or returns the singleton instance.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance;
/** Registers a backend implementation with the GoogleDataTransport infrastructure.
*
* @param backend The backend object to register.
* @param target The target this backend object will be responsible for.
*/
- (void)registerUploader:(id<GDTCORUploader>)backend target:(GDTCORTarget)target;
/** Registers a event prioritizer implementation with the GoogleDataTransport infrastructure.
*
* @param prioritizer The prioritizer object to register.
* @param target The target this prioritizer object will be responsible for.
*/
- (void)registerPrioritizer:(id<GDTCORPrioritizer>)prioritizer target:(GDTCORTarget)target;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,58 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORDataFuture.h>
#import <GoogleDataTransport/GDTCOREvent.h>
@class GDTCOREvent;
NS_ASSUME_NONNULL_BEGIN
@interface GDTCORStoredEvent : NSObject <NSSecureCoding>
/** The data future representing the original event's transport bytes. */
@property(readonly, nonatomic) GDTCORDataFuture *dataFuture;
/** The mapping identifier, to allow backends to map the transport bytes to a proto. */
@property(readonly, nonatomic) NSString *mappingID;
/** The identifier for the backend this event will eventually be sent to. */
@property(readonly, nonatomic) NSNumber *target;
/** The quality of service tier this event belongs to. */
@property(readonly, nonatomic) GDTCOREventQoS qosTier;
/** The clock snapshot at the time of the event. */
@property(readonly, nonatomic) GDTCORClock *clockSnapshot;
/** A dictionary provided to aid prioritizers by allowing the passing of arbitrary data.
*
* @note Ensure that custom classes in this dict implement NSSecureCoding to prevent loss of data.
*/
@property(readonly, nullable, nonatomic) NSDictionary *customPrioritizationParams;
/** Initializes a stored event with the given URL and event.
*
* @param event The event this stored event represents.
* @param dataFuture The dataFuture this event represents.
* @return An instance of this class.
*/
- (instancetype)initWithEvent:(GDTCOREvent *)event dataFuture:(GDTCORDataFuture *)dataFuture;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,29 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
/** The list of targets supported by the shared transport infrastructure. If adding a new target,
* please use the previous value +1.
*/
typedef NS_ENUM(NSInteger, GDTCORTarget) {
/** A target only used in testing. */
kGDTCORTargetTest = 999,
/** The CCT target. */
kGDTCORTargetCCT = 1000,
};
@@ -0,0 +1,68 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCOREventTransformer.h>
@class GDTCOREvent;
NS_ASSUME_NONNULL_BEGIN
@interface GDTCORTransport : NSObject
// Please use the designated initializer.
- (instancetype)init NS_UNAVAILABLE;
/** Initializes a new transport that will send events to the given target backend.
*
* @param mappingID The mapping identifier used by the backend to map the data object transport
* bytes to a proto.
* @param transformers A list of transformers to be applied to events that are sent.
* @param target The target backend of this transport.
* @return A transport that will send events.
*/
- (instancetype)initWithMappingID:(NSString *)mappingID
transformers:(nullable NSArray<id<GDTCOREventTransformer>> *)transformers
target:(NSInteger)target NS_DESIGNATED_INITIALIZER;
/** Copies and sends an internal telemetry event. Events sent using this API are lower in priority,
* and sometimes won't be sent on their own.
*
* @note This will convert the event's data object to data and release the original event.
*
* @param event The event to send.
*/
- (void)sendTelemetryEvent:(GDTCOREvent *)event;
/** Copies and sends an SDK service data event. Events send using this API are higher in priority,
* and will cause a network request at some point in the relative near future.
*
* @note This will convert the event's data object to data and release the original event.
*
* @param event The event to send.
*/
- (void)sendDataEvent:(GDTCOREvent *)event;
/** Creates an event for use by this transport.
*
* @return An event that is suited for use by this transport.
*/
- (GDTCOREvent *)eventForTransport;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,80 @@
/*
* Copyright 2019 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORTargets.h>
@class GDTCORClock;
@class GDTCORStoredEvent;
@class GDTCORUploadPackage;
/** A protocol that allows a handler to respond to package lifecycle events. */
@protocol GDTCORUploadPackageProtocol <NSObject>
@optional
/** Indicates that the package has expired.
*
* @note Package expiration will only be checked every 5 seconds.
*
* @param package The package that has expired.
*/
- (void)packageExpired:(GDTCORUploadPackage *)package;
/** Indicates that the package was successfully delivered.
*
* @param package The package that was delivered.
*/
- (void)packageDelivered:(GDTCORUploadPackage *)package successful:(BOOL)successful;
@end
/** This class is a container that's handed off to uploaders. */
@interface GDTCORUploadPackage : NSObject <NSSecureCoding>
/** The set of stored events in this upload package. */
@property(nonatomic) NSSet<GDTCORStoredEvent *> *events;
/** The expiration time. If [[GDTCORClock snapshot] isAfter:deliverByTime] this package has expired.
*
* @note By default, the expiration time will be 3 minutes from creation.
*/
@property(nonatomic) GDTCORClock *deliverByTime;
/** The target of this package. */
@property(nonatomic, readonly) GDTCORTarget target;
/** Initializes a package instance.
*
* @param target The target/destination of this package.
* @return An instance of this class.
*/
- (instancetype)initWithTarget:(GDTCORTarget)target NS_DESIGNATED_INITIALIZER;
// Please use the designated initializer.
- (instancetype)init NS_UNAVAILABLE;
/** Completes delivery of the package.
*
* @note This *needs* to be called by an uploader for the package to not expire.
*/
- (void)completeDelivery;
/** Sends the package back, indicating that delivery should be attempted again in the future. */
- (void)retryDeliveryInTheFuture;
@end
@@ -0,0 +1,47 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORClock.h>
#import <GoogleDataTransport/GDTCORLifecycle.h>
#import <GoogleDataTransport/GDTCORPrioritizer.h>
#import <GoogleDataTransport/GDTCORTargets.h>
#import <GoogleDataTransport/GDTCORUploadPackage.h>
NS_ASSUME_NONNULL_BEGIN
/** This protocol defines the common interface for uploader implementations. */
@protocol GDTCORUploader <NSObject, GDTCORLifecycleProtocol, GDTCORUploadPackageProtocol>
@required
/** Returns YES if the uploader can make an upload attempt, NO otherwise.
*
* @param conditions The conditions that the upload attempt is likely to occur under.
* @return YES if the uploader can make an upload attempt, NO otherwise.
*/
- (BOOL)readyToUploadWithConditions:(GDTCORUploadConditions)conditions;
/** Uploads events to the backend using this specific backend's chosen format.
*
* @param package The event package to upload. Make sure to call -completeDelivery.
*/
- (void)uploadPackage:(GDTCORUploadPackage *)package;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,30 @@
/*
* 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.
*/
#import "GDTCORClock.h"
#import "GDTCORConsoleLogger.h"
#import "GDTCORDataFuture.h"
#import "GDTCOREvent.h"
#import "GDTCOREventDataObject.h"
#import "GDTCOREventTransformer.h"
#import "GDTCORLifecycle.h"
#import "GDTCORPrioritizer.h"
#import "GDTCORRegistrar.h"
#import "GDTCORStoredEvent.h"
#import "GDTCORTargets.h"
#import "GDTCORTransport.h"
#import "GDTCORUploadPackage.h"
#import "GDTCORUploader.h"