Push from command line

This commit is contained in:
Deniz Duezgoeren
2019-08-12 11:20:21 +02:00
parent 3a919dcb23
commit f1345eac14
512 changed files with 103288 additions and 1930 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 "GDTLibrary/Private/GDTAssert.h"
GDTAssertionBlock GDTAssertionBlockToRunInsteadOfNSAssert(void) {
// This class is only compiled in by unit tests, and this should fail quickly in optimized builds.
Class GDTAssertClass = NSClassFromString(@"GDTAssertHelper");
if (__builtin_expect(!!GDTAssertClass, 0)) {
SEL assertionBlockSEL = NSSelectorFromString(@"assertionBlock");
if (assertionBlockSEL) {
IMP assertionBlockIMP = [GDTAssertClass methodForSelector:assertionBlockSEL];
if (assertionBlockIMP) {
GDTAssertionBlock assertionBlock =
((GDTAssertionBlock(*)(id, SEL))assertionBlockIMP)(GDTAssertClass, 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 "GDTLibrary/Public/GDTClock.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 GDTClock
- (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;
}
+ (GDTClock *)snapshot {
return [[GDTClock alloc] init];
}
+ (instancetype)clockSnapshotInTheFuture:(uint64_t)millisInTheFuture {
GDTClock *snapshot = [self snapshot];
snapshot->_timeMillis += millisInTheFuture;
return snapshot;
}
- (BOOL)isAfter:(GDTClock *)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 kGDTClockTimeMillisKey = @"GDTClockTimeMillis";
/** NSKeyedCoder key for timezoneOffsetMillis property. */
static NSString *const kGDTClockTimezoneOffsetSeconds = @"GDTClockTimezoneOffsetSeconds";
/** NSKeyedCoder key for _kernelBootTime ivar. */
static NSString *const kGDTClockKernelBootTime = @"GDTClockKernelBootTime";
/** NSKeyedCoder key for _uptime ivar. */
static NSString *const kGDTClockUptime = @"GDTClockUptime";
+ (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:kGDTClockTimeMillisKey];
_timezoneOffsetSeconds = [aDecoder decodeInt64ForKey:kGDTClockTimezoneOffsetSeconds];
_kernelBootTime = [aDecoder decodeInt64ForKey:kGDTClockKernelBootTime];
_uptime = [aDecoder decodeInt64ForKey:kGDTClockUptime];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInt64:_timeMillis forKey:kGDTClockTimeMillisKey];
[aCoder encodeInt64:_timezoneOffsetSeconds forKey:kGDTClockTimezoneOffsetSeconds];
[aCoder encodeInt64:_kernelBootTime forKey:kGDTClockKernelBootTime];
[aCoder encodeInt64:_uptime forKey:kGDTClockUptime];
}
@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 "GDTLibrary/Public/GDTConsoleLogger.h"
/** The console logger prefix. */
static NSString *kGDTConsoleLogger = @"[GoogleDataTransport]";
NSString *GDTMessageCodeEnumToString(GDTMessageCode code) {
return [[NSString alloc] initWithFormat:@"I-GDT%06ld", (long)code];
}
void GDTLog(GDTMessageCode code, NSString *format, ...) {
// Don't log anything in not debug builds.
#ifndef NDEBUG
NSString *logFormat = [NSString
stringWithFormat:@"%@[%@] %@", kGDTConsoleLogger, GDTMessageCodeEnumToString(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/GDTDataFuture.h>
@implementation GDTDataFuture
- (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 *kGDTDataFutureFileURLKey = @"GDTDataFutureFileURLKey";
/** Coding key for _data ivar. */
static NSString *kGDTDataFutureDataKey = @"GDTDataFutureDataKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_fileURL forKey:kGDTDataFutureFileURLKey];
[aCoder encodeObject:_originalData forKey:kGDTDataFutureDataKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
self = [self init];
if (self) {
_fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kGDTDataFutureFileURLKey];
_originalData = [aDecoder decodeObjectOfClass:[NSData class] forKey:kGDTDataFutureDataKey];
}
return self;
}
@end
@@ -0,0 +1,116 @@
/*
* 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/GDTEvent.h>
#import <GoogleDataTransport/GDTStoredEvent.h>
#import "GDTLibrary/Private/GDTAssert.h"
#import "GDTLibrary/Private/GDTEvent_Private.h"
@implementation GDTEvent
- (instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target {
GDTAssert(mappingID.length > 0, @"Please give a valid mapping ID");
GDTAssert(target > 0, @"A target cannot be negative or 0");
self = [super init];
if (self) {
_mappingID = mappingID;
_target = target;
_qosTier = GDTEventQosDefault;
}
return self;
}
- (instancetype)copy {
GDTEvent *copy = [[GDTEvent 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<GDTEventDataObject>)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];
}
}
- (GDTStoredEvent *)storedEventWithDataFuture:(GDTDataFuture *)dataFuture {
return [[GDTStoredEvent 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:[GDTClock 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 "GDTLibrary/Public/GDTLifecycle.h"
#import <GoogleDataTransport/GDTEvent.h>
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
#import "GDTLibrary/Private/GDTStorage_Private.h"
#import "GDTLibrary/Private/GDTTransformer_Private.h"
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
@implementation GDTLifecycle
+ (void)load {
[self sharedInstance];
}
/** Creates/returns the singleton instance of this class.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance {
static GDTLifecycle *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTLifecycle alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:kGDTApplicationDidEnterBackgroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:kGDTApplicationWillEnterForegroundNotification
object:nil];
NSString *name = kGDTApplicationWillTerminateNotification;
[notificationCenter addObserver:self
selector:@selector(applicationWillTerminate:)
name:name
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification {
GDTApplication *application = [GDTApplication sharedApplication];
if ([[GDTTransformer sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTTransformer sharedInstance] appWillBackground:application];
}
if ([[GDTStorage sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTStorage sharedInstance] appWillBackground:application];
}
if ([[GDTUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTUploadCoordinator sharedInstance] appWillBackground:application];
}
if ([[GDTRegistrar sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTRegistrar sharedInstance] appWillBackground:application];
}
}
- (void)applicationWillEnterForeground:(NSNotification *)notification {
GDTApplication *application = [GDTApplication sharedApplication];
if ([[GDTTransformer sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTTransformer sharedInstance] appWillForeground:application];
}
if ([[GDTStorage sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTStorage sharedInstance] appWillForeground:application];
}
if ([[GDTUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTUploadCoordinator sharedInstance] appWillForeground:application];
}
if ([[GDTRegistrar sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTRegistrar sharedInstance] appWillForeground:application];
}
}
- (void)applicationWillTerminate:(NSNotification *)notification {
GDTApplication *application = [GDTApplication sharedApplication];
if ([[GDTTransformer sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTTransformer sharedInstance] appWillTerminate:application];
}
if ([[GDTStorage sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTStorage sharedInstance] appWillTerminate:application];
}
if ([[GDTUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTUploadCoordinator sharedInstance] appWillTerminate:application];
}
if ([[GDTRegistrar sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTRegistrar sharedInstance] appWillTerminate:application];
}
}
@end
@@ -0,0 +1,157 @@
/*
* 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/GDTPlatform.h>
const GDTBackgroundIdentifier GDTBackgroundIdentifierInvalid = 0;
NSString *const kGDTApplicationDidEnterBackgroundNotification =
@"GDTApplicationDidEnterBackgroundNotification";
NSString *const kGDTApplicationWillEnterForegroundNotification =
@"GDTApplicationWillEnterForegroundNotification";
NSString *const kGDTApplicationWillTerminateNotification =
@"GDTApplicationWillTerminateNotification";
BOOL GDTReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) {
#if TARGET_OS_IOS
return (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN;
#else
return NO;
#endif // TARGET_OS_IOS
}
@implementation GDTApplication
+ (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.
NSAssert(GDTBackgroundIdentifierInvalid == UIBackgroundTaskInvalid,
@"GDTBackgroundIdentifierInvalid and UIBackgroundTaskInvalid should be the same.");
#endif
[self sharedApplication];
}
+ (nullable GDTApplication *)sharedApplication {
static GDTApplication *application;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
application = [[GDTApplication 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];
#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;
}
- (GDTBackgroundIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
return
[[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithExpirationHandler:handler];
}
- (void)endBackgroundTask:(GDTBackgroundIdentifier)bgID {
[[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:kGDTApplicationDidEnterBackgroundNotification object:nil];
}
- (void)iOSApplicationWillEnterForeground:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTApplicationWillEnterForegroundNotification object:nil];
}
- (void)iOSApplicationWillTerminate:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTApplicationWillTerminateNotification 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:kGDTApplicationWillTerminateNotification object:nil];
}
#endif // TARGET_OS_OSX
@end
@@ -0,0 +1,110 @@
/*
* 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 "GDTLibrary/Private/GDTReachability.h"
#import "GDTLibrary/Private/GDTReachability_Private.h"
#import <GoogleDataTransport/GDTConsoleLogger.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 GDTReachabilityCallback(SCNetworkReachabilityRef reachability,
SCNetworkReachabilityFlags flags,
void *info);
@implementation GDTReachability {
/** 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 GDTReachability *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTReachability alloc] init];
});
return sharedInstance;
}
+ (SCNetworkReachabilityFlags)currentFlags {
__block SCNetworkReachabilityFlags currentFlags;
dispatch_sync([GDTReachability sharedInstance] -> _reachabilityQueue, ^{
GDTReachability *reachability = [GDTReachability 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.GDTReachability", DISPATCH_QUEUE_SERIAL);
_reachabilityRef = SCNetworkReachabilityCreateWithAddress(
kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
Boolean success = SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _reachabilityQueue);
if (!success) {
GDTLogWarning(GDTMCWReachabilityFailed, @"%@", @"The reachability queue wasn't set.");
}
success = SCNetworkReachabilitySetCallback(_reachabilityRef, GDTReachabilityCallback, NULL);
if (!success) {
GDTLogWarning(GDTMCWReachabilityFailed, @"%@", @"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 GDTReachabilityCallback(SCNetworkReachabilityRef reachability,
SCNetworkReachabilityFlags flags,
void *info) {
[[GDTReachability sharedInstance] setCallbackFlags:flags];
}
@@ -0,0 +1,139 @@
/*
* 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 "GDTLibrary/Public/GDTRegistrar.h"
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
@implementation GDTRegistrar {
/** Backing ivar for targetToUploader property. */
NSMutableDictionary<NSNumber *, id<GDTUploader>> *_targetToUploader;
/** Backing ivar for targetToPrioritizer property. */
NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *_targetToPrioritizer;
}
+ (instancetype)sharedInstance {
static GDTRegistrar *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTRegistrar alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_registrarQueue = dispatch_queue_create("com.google.GDTRegistrar", DISPATCH_QUEUE_CONCURRENT);
_targetToPrioritizer = [[NSMutableDictionary alloc] init];
_targetToUploader = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)registerUploader:(id<GDTUploader>)backend target:(GDTTarget)target {
__weak GDTRegistrar *weakSelf = self;
dispatch_barrier_async(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_targetToUploader[@(target)] = backend;
}
});
}
- (void)registerPrioritizer:(id<GDTPrioritizer>)prioritizer target:(GDTTarget)target {
__weak GDTRegistrar *weakSelf = self;
dispatch_barrier_async(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_targetToPrioritizer[@(target)] = prioritizer;
}
});
}
- (NSMutableDictionary<NSNumber *, id<GDTUploader>> *)targetToUploader {
__block NSMutableDictionary<NSNumber *, id<GDTUploader>> *targetToUploader;
__weak GDTRegistrar *weakSelf = self;
dispatch_sync(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
targetToUploader = strongSelf->_targetToUploader;
}
});
return targetToUploader;
}
- (NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *)targetToPrioritizer {
__block NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *targetToPrioritizer;
__weak GDTRegistrar *weakSelf = self;
dispatch_sync(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
targetToPrioritizer = strongSelf->_targetToPrioritizer;
}
});
return targetToPrioritizer;
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillBackground:(nonnull GDTApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillBackground:)]) {
[uploader appWillBackground:app];
}
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillBackground:)]) {
[prioritizer appWillBackground:app];
}
}
});
}
- (void)appWillForeground:(nonnull GDTApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillForeground:)]) {
[uploader appWillForeground:app];
}
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillForeground:)]) {
[prioritizer appWillForeground:app];
}
}
});
}
- (void)appWillTerminate:(nonnull GDTApplication *)app {
dispatch_sync(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillTerminate:)]) {
[uploader appWillTerminate:app];
}
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillTerminate:)]) {
[prioritizer appWillTerminate:app];
}
}
});
}
@end
@@ -0,0 +1,256 @@
/*
* 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 "GDTLibrary/Private/GDTStorage.h"
#import "GDTLibrary/Private/GDTStorage_Private.h"
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTLifecycle.h>
#import <GoogleDataTransport/GDTPrioritizer.h>
#import <GoogleDataTransport/GDTStoredEvent.h>
#import "GDTLibrary/Private/GDTAssert.h"
#import "GDTLibrary/Private/GDTEvent_Private.h"
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
/** Creates and/or returns a singleton NSString that is the shared storage path.
*
* @return The SDK event storage path.
*/
static NSString *GDTStoragePath() {
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 GDTStorage
+ (NSString *)archivePath {
static NSString *archivePath;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
archivePath = [GDTStoragePath() stringByAppendingPathComponent:@"GDTStorageArchive"];
});
return archivePath;
}
+ (instancetype)sharedInstance {
static GDTStorage *sharedStorage;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedStorage = [[GDTStorage alloc] init];
});
return sharedStorage;
}
- (instancetype)init {
self = [super init];
if (self) {
_storageQueue = dispatch_queue_create("com.google.GDTStorage", DISPATCH_QUEUE_SERIAL);
_targetToEventSet = [[NSMutableDictionary alloc] init];
_storedEvents = [[NSMutableOrderedSet alloc] init];
_uploadCoordinator = [GDTUploadCoordinator sharedInstance];
}
return self;
}
- (void)storeEvent:(GDTEvent *)event {
[self createEventDirectoryIfNotExists];
__block GDTBackgroundIdentifier bgID = GDTBackgroundIdentifierInvalid;
if (_runningInBackground) {
bgID = [[GDTApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
}];
}
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<GDTPrioritizer> prioritizer = [GDTRegistrar sharedInstance].targetToPrioritizer[@(target)];
GDTAssert(prioritizer, @"There's no prioritizer registered for the given target.");
// Write the transport bytes to disk, get a filename.
GDTAssert(event.dataObjectTransportBytes, @"The event should have been serialized to bytes");
NSURL *eventFile = [self saveEventBytesToDisk:event.dataObjectTransportBytes
eventHash:event.hash];
GDTDataFuture *dataFuture = [[GDTDataFuture alloc] initWithFileURL:eventFile];
GDTStoredEvent *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 == GDTEventQoSFast) {
[self.uploadCoordinator forceUploadForTarget:target];
}
// If running in the background, save state to disk and end the associated background task.
if (bgID != GDTBackgroundIdentifierInvalid) {
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
}
});
}
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)events {
NSSet<GDTStoredEvent *> *eventsToRemove = [events copy];
dispatch_async(_storageQueue, ^{
for (GDTStoredEvent *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];
GDTAssert(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:GDTStoragePath()
withIntermediateDirectories:YES
attributes:0
error:&error];
if (!result || error) {
GDTLogError(GDTMCEDirectoryCreationError, @"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 = GDTStoragePath();
NSString *event = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash];
NSURL *eventFilePath = [NSURL fileURLWithPath:[storagePath stringByAppendingPathComponent:event]];
GDTAssert(![[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) {
GDTLogError(GDTMCEFileWriteError, @"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:(GDTStoredEvent *)event {
[_storedEvents addObject:event];
NSMutableSet<GDTStoredEvent *> *events = self.targetToEventSet[event.target];
events = events ? events : [[NSMutableSet alloc] init];
[events addObject:event];
_targetToEventSet[event.target] = events;
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillForeground:(GDTApplication *)app {
[NSKeyedUnarchiver unarchiveObjectWithFile:[GDTStorage archivePath]];
self->_runningInBackground = NO;
}
- (void)appWillBackground:(GDTApplication *)app {
self->_runningInBackground = YES;
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
// Create an immediate background task to run until the end of the current queue of work.
__block GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgID];
}];
dispatch_async(_storageQueue, ^{
[app endBackgroundTask:bgID];
});
}
- (void)appWillTerminate:(GDTApplication *)application {
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
}
#pragma mark - NSSecureCoding
/** The NSKeyedCoder key for the storedEvents property. */
static NSString *const kGDTStorageStoredEventsKey = @"GDTStorageStoredEventsKey";
/** The NSKeyedCoder key for the targetToEventSet property. */
static NSString *const kGDTStorageTargetToEventSetKey = @"GDTStorageTargetToEventSetKey";
/** The NSKeyedCoder key for the uploadCoordinator property. */
static NSString *const kGDTStorageUploadCoordinatorKey = @"GDTStorageUploadCoordinatorKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
// Create the singleton and populate its ivars.
GDTStorage *sharedInstance = [self.class sharedInstance];
dispatch_sync(sharedInstance.storageQueue, ^{
sharedInstance->_storedEvents = [aDecoder decodeObjectOfClass:[NSMutableOrderedSet class]
forKey:kGDTStorageStoredEventsKey];
sharedInstance->_targetToEventSet =
[aDecoder decodeObjectOfClass:[NSMutableDictionary class]
forKey:kGDTStorageTargetToEventSetKey];
sharedInstance->_uploadCoordinator =
[aDecoder decodeObjectOfClass:[GDTUploadCoordinator class]
forKey:kGDTStorageUploadCoordinatorKey];
});
return sharedInstance;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
GDTStorage *sharedInstance = [self.class sharedInstance];
dispatch_sync(sharedInstance.storageQueue, ^{
[aCoder encodeObject:sharedInstance->_storedEvents forKey:kGDTStorageStoredEventsKey];
[aCoder encodeObject:sharedInstance->_targetToEventSet forKey:kGDTStorageTargetToEventSetKey];
[aCoder encodeObject:sharedInstance->_uploadCoordinator forKey:kGDTStorageUploadCoordinatorKey];
});
}
@end
@@ -0,0 +1,94 @@
/*
* 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/GDTStoredEvent.h>
#import <GoogleDataTransport/GDTClock.h>
#import "GDTLibrary/Private/GDTStorage_Private.h"
@implementation GDTStoredEvent
- (instancetype)initWithEvent:(GDTEvent *)event dataFuture:(nonnull GDTDataFuture *)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 = @"GDTStoredEventDataFutureKey";
/** Coding key for mappingID ivar. */
static NSString *kMappingIDKey = @"GDTStoredEventMappingIDKey";
/** Coding key for target ivar. */
static NSString *kTargetKey = @"GDTStoredEventTargetKey";
/** Coding key for qosTier ivar. */
static NSString *kQosTierKey = @"GDTStoredEventQosTierKey";
/** Coding key for clockSnapshot ivar. */
static NSString *kClockSnapshotKey = @"GDTStoredEventClockSnapshotKey";
/** Coding key for customPrioritizationParams ivar. */
static NSString *kCustomPrioritizationParamsKey = @"GDTStoredEventcustomPrioritizationParamsKey";
+ (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:[GDTDataFuture 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:[GDTClock class] forKey:kClockSnapshotKey];
_customPrioritizationParams = [aDecoder decodeObjectOfClass:[NSDictionary class]
forKey:kCustomPrioritizationParamsKey];
}
return self;
}
- (BOOL)isEqual:(GDTStoredEvent *)other {
return [self hash] == [other hash];
}
- (NSUInteger)hash {
return [_dataFuture hash] ^ [_mappingID hash] ^ [_target hash] ^ [_clockSnapshot hash] ^ _qosTier;
}
@end
@@ -0,0 +1,102 @@
/*
* 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 "GDTLibrary/Private/GDTTransformer.h"
#import "GDTLibrary/Private/GDTTransformer_Private.h"
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTEventTransformer.h>
#import <GoogleDataTransport/GDTLifecycle.h>
#import "GDTLibrary/Private/GDTAssert.h"
#import "GDTLibrary/Private/GDTStorage.h"
@implementation GDTTransformer
+ (instancetype)sharedInstance {
static GDTTransformer *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.GDTTransformer", DISPATCH_QUEUE_SERIAL);
_storageInstance = [GDTStorage sharedInstance];
}
return self;
}
- (void)transformEvent:(GDTEvent *)event
withTransformers:(NSArray<id<GDTEventTransformer>> *)transformers {
GDTAssert(event, @"You can't write a nil event");
__block GDTBackgroundIdentifier bgID = GDTBackgroundIdentifierInvalid;
if (_runningInBackground) {
bgID = [[GDTApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
}];
}
dispatch_async(_eventWritingQueue, ^{
GDTEvent *transformedEvent = event;
for (id<GDTEventTransformer> transformer in transformers) {
if ([transformer respondsToSelector:@selector(transform:)]) {
transformedEvent = [transformer transform:transformedEvent];
if (!transformedEvent) {
return;
}
} else {
GDTLogError(GDTMCETransformerDoesntImplementTransform,
@"Transformer doesn't implement transform: %@", transformer);
return;
}
}
[self.storageInstance storeEvent:transformedEvent];
if (self->_runningInBackground) {
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
}
});
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillForeground:(GDTApplication *)app {
dispatch_async(_eventWritingQueue, ^{
self->_runningInBackground = NO;
});
}
- (void)appWillBackground:(GDTApplication *)app {
// Create an immediate background task to run until the end of the current queue of work.
__block GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgID];
}];
dispatch_async(_eventWritingQueue, ^{
[app endBackgroundTask:bgID];
});
}
- (void)appWillTerminate:(GDTApplication *)application {
// Flush the queue immediately.
dispatch_sync(_eventWritingQueue, ^{
});
}
@end
@@ -0,0 +1,65 @@
/*
* 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/GDTTransport.h>
#import "GDTLibrary/Private/GDTTransport_Private.h"
#import <GoogleDataTransport/GDTClock.h>
#import <GoogleDataTransport/GDTEvent.h>
#import "GDTLibrary/Private/GDTAssert.h"
#import "GDTLibrary/Private/GDTTransformer.h"
@implementation GDTTransport
- (instancetype)initWithMappingID:(NSString *)mappingID
transformers:(nullable NSArray<id<GDTEventTransformer>> *)transformers
target:(NSInteger)target {
self = [super init];
if (self) {
GDTAssert(mappingID.length > 0, @"A mapping ID cannot be nil or empty");
GDTAssert(target > 0, @"A target cannot be negative or 0");
_mappingID = mappingID;
_transformers = transformers;
_target = target;
_transformerInstance = [GDTTransformer sharedInstance];
}
return self;
}
- (void)sendTelemetryEvent:(GDTEvent *)event {
// TODO: Determine if sending an event before registration is allowed.
GDTAssert(event, @"You can't send a nil event");
GDTEvent *copiedEvent = [event copy];
copiedEvent.qosTier = GDTEventQoSTelemetry;
copiedEvent.clockSnapshot = [GDTClock snapshot];
[self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers];
}
- (void)sendDataEvent:(GDTEvent *)event {
// TODO: Determine if sending an event before registration is allowed.
GDTAssert(event, @"You can't send a nil event");
GDTAssert(event.qosTier != GDTEventQoSTelemetry, @"Use -sendTelemetryEvent, please.");
GDTEvent *copiedEvent = [event copy];
copiedEvent.clockSnapshot = [GDTClock snapshot];
[self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers];
}
- (GDTEvent *)eventForTransport {
return [[GDTEvent alloc] initWithMappingID:_mappingID target:_target];
}
@end
@@ -0,0 +1,241 @@
/*
* 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 "GDTLibrary/Private/GDTUploadCoordinator.h"
#import <GoogleDataTransport/GDTClock.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import "GDTLibrary/Private/GDTAssert.h"
#import "GDTLibrary/Private/GDTReachability.h"
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
#import "GDTLibrary/Private/GDTStorage.h"
@implementation GDTUploadCoordinator
+ (instancetype)sharedInstance {
static GDTUploadCoordinator *sharedUploader;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedUploader = [[GDTUploadCoordinator alloc] init];
[sharedUploader startTimer];
});
return sharedUploader;
}
- (instancetype)init {
self = [super init];
if (self) {
_coordinationQueue =
dispatch_queue_create("com.google.GDTUploadCoordinator", DISPATCH_QUEUE_SERIAL);
_registrar = [GDTRegistrar sharedInstance];
_timerInterval = 30 * NSEC_PER_SEC;
_timerLeeway = 5 * NSEC_PER_SEC;
_targetToInFlightPackages = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)forceUploadForTarget:(GDTTarget)target {
dispatch_async(_coordinationQueue, ^{
GDTUploadConditions conditions = [self uploadConditions];
conditions |= GDTUploadConditionHighPriority;
[self uploadTargets:@[ @(target) ] conditions:conditions];
});
}
#pragma mark - Property overrides
// GDTStorage and GDTUploadCoordinator +sharedInstance methods call each other, so this breaks
// the loop.
- (GDTStorage *)storage {
if (!_storage) {
_storage = [GDTStorage 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) {
GDTUploadConditions 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:(GDTUploadConditions)conditions {
dispatch_async(_coordinationQueue, ^{
if ((conditions & GDTUploadConditionNoNetwork) == GDTUploadConditionNoNetwork) {
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<GDTUploader> uploader = self.registrar.targetToUploader[target];
if ([uploader readyToUploadWithConditions:conditions]) {
id<GDTPrioritizer> prioritizer = self.registrar.targetToPrioritizer[target];
GDTUploadPackage *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.
*/
- (GDTUploadConditions)uploadConditions {
SCNetworkReachabilityFlags currentFlags = [GDTReachability currentFlags];
BOOL reachable =
(currentFlags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable;
BOOL connectionRequired = (currentFlags & kSCNetworkReachabilityFlagsConnectionRequired) ==
kSCNetworkReachabilityFlagsConnectionRequired;
BOOL networkConnected = reachable && !connectionRequired;
if (!networkConnected) {
return GDTUploadConditionNoNetwork;
}
BOOL isWWAN = GDTReachabilityFlagsContainWWAN(currentFlags);
if (isWWAN) {
return GDTUploadConditionMobileData;
} else {
return GDTUploadConditionWifiData;
}
}
#pragma mark - NSSecureCoding support
/** The NSKeyedCoder key for the targetToInFlightPackages property. */
static NSString *const ktargetToInFlightPackagesKey =
@"GDTUploadCoordinatortargetToInFlightPackages";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
GDTUploadCoordinator *sharedCoordinator = [GDTUploadCoordinator sharedInstance];
sharedCoordinator->_targetToInFlightPackages =
[aDecoder decodeObjectOfClass:[NSMutableDictionary class]
forKey:ktargetToInFlightPackagesKey];
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.
[aCoder encodeObject:_targetToInFlightPackages forKey:ktargetToInFlightPackagesKey];
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillForeground:(GDTApplication *)app {
// Not entirely thread-safe, but it should be fine.
self->_runningInBackground = NO;
[self startTimer];
}
- (void)appWillBackground:(GDTApplication *)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 GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgID];
}];
dispatch_async(_coordinationQueue, ^{
[app endBackgroundTask:bgID];
});
}
- (void)appWillTerminate:(GDTApplication *)application {
dispatch_sync(_coordinationQueue, ^{
[self stopTimer];
});
}
#pragma mark - GDTUploadPackageProtocol
- (void)packageDelivered:(GDTUploadPackage *)package successful:(BOOL)successful {
dispatch_async(_coordinationQueue, ^{
NSNumber *targetNumber = @(package.target);
[self->_targetToInFlightPackages removeObjectForKey:targetNumber];
id<GDTPrioritizer> prioritizer = self->_registrar.targetToPrioritizer[targetNumber];
if (!prioritizer) {
GDTLogError(GDTMCEPrioritizerError, @"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:(GDTUploadPackage *)package {
dispatch_async(_coordinationQueue, ^{
NSNumber *targetNumber = @(package.target);
[self->_targetToInFlightPackages removeObjectForKey:targetNumber];
id<GDTPrioritizer> prioritizer = self->_registrar.targetToPrioritizer[targetNumber];
id<GDTUploader> uploader = self->_registrar.targetToUploader[targetNumber];
if ([prioritizer respondsToSelector:@selector(packageExpired:)]) {
[prioritizer packageExpired:package];
}
if ([uploader respondsToSelector:@selector(packageExpired:)]) {
[uploader packageExpired:package];
}
});
}
@end
@@ -0,0 +1,152 @@
/*
* 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/GDTUploadPackage.h>
#import <GoogleDataTransport/GDTClock.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import "GDTLibrary/Private/GDTStorage_Private.h"
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
#import "GDTLibrary/Private/GDTUploadPackage_Private.h"
@implementation GDTUploadPackage {
/** 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:(GDTTarget)target {
self = [super init];
if (self) {
_target = target;
_storage = [GDTStorage sharedInstance];
_deliverByTime = [GDTClock clockSnapshotInTheFuture:180000];
_handler = [GDTUploadCoordinator sharedInstance];
_expirationTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(checkIfPackageIsExpired:)
userInfo:nil
repeats:YES];
}
return self;
}
- (instancetype)copy {
GDTUploadPackage *newPackage = [[GDTUploadPackage 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:(GDTStorage *)storage {
if (storage != _storage) {
_storage = storage;
}
}
- (void)completeDelivery {
if (_isDelivered) {
GDTLogError(GDTMCEDeliverTwice, @"%@",
@"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 ([[GDTClock 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 = @"GDTUploadPackageEventsKey";
/** The keyed archiver key for the _isHandled property. */
static NSString *const kDeliverByTimeKey = @"GDTUploadPackageDeliveryByTimeKey";
/** The keyed archiver key for the _isHandled ivar. */
static NSString *const kIsHandledKey = @"GDTUploadPackageIsHandledKey";
/** The keyed archiver key for the handler property. */
static NSString *const kHandlerKey = @"GDTUploadPackageHandlerKey";
/** The keyed archiver key for the target property. */
static NSString *const kTargetKey = @"GDTUploadPackageTargetKey";
+ (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 {
GDTTarget target = [aDecoder decodeIntegerForKey:kTargetKey];
self = [self initWithTarget:target];
if (self) {
_events = [aDecoder decodeObjectOfClass:[NSSet class] forKey:kEventsKey];
_deliverByTime = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:kDeliverByTimeKey];
_isHandled = [aDecoder decodeBoolForKey:kIsHandledKey];
// Isn't technically NSSecureCoding, because we don't know the class of this object.
_handler = [aDecoder decodeObjectForKey:kHandlerKey];
}
return self;
}
@end
@@ -0,0 +1,54 @@
/*
* 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>
/** A block type that could be run instead of NSAssert. No return type, no params. */
typedef void (^GDTAssertionBlock)(void);
/** Returns the result of executing a soft-linked method present in unit tests that allows a block
* to be run in lieu of a call to NSAssert. This helps ameliorate issues with catching exceptions
* that occur on a dispatch_queue.
*
* @return A block that can be run instead of calling NSAssert, or nil.
*/
FOUNDATION_EXPORT GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void);
#if !defined(NS_BLOCK_ASSERTIONS)
/** Asserts using NSAssert, unless a block was specified to be run instead.
*
* @param condition The condition you'd expect to be YES.
*/
#define GDTAssert(condition, ...) \
do { \
if (__builtin_expect(!(condition), 0)) { \
GDTAssertionBlock assertionBlock = GDTAssertionBlockToRunInsteadOfNSAssert(); \
if (assertionBlock) { \
assertionBlock(); \
} else { \
NSAssert(condition, __VA_ARGS__); \
} \
} \
} while (0);
#else
#define GDTAssert(condition, ...) \
do { \
} while (0);
#endif // !defined(NS_BLOCK_ASSERTIONS)
@@ -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/GDTEvent.h>
#import <GoogleDataTransport/GDTClock.h>
NS_ASSUME_NONNULL_BEGIN
@interface GDTEvent ()
/** 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 GDTReachability : 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 "GDTLibrary/Private/GDTReachability.h"
@interface GDTReachability ()
/** 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/GDTRegistrar.h>
@interface GDTRegistrar ()
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<GDTUploader>> *targetToUploader;
/** A map of targets to prioritizer implementations. */
@property(atomic, readonly)
NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *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/GDTLifecycle.h>
@class GDTEvent;
@class GDTStoredEvent;
NS_ASSUME_NONNULL_BEGIN
/** Manages the storage of events. This class is thread-safe. */
@interface GDTStorage : NSObject <NSSecureCoding, GDTLifecycleProtocol>
/** 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 GDTStoredEvent instance.
*
* @param event The event to store.
*/
- (void)storeEvent:(GDTEvent *)event;
/** Removes a set of events from storage specified by their hash.
*
* @param events The set of stored events to remove.
*/
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)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 "GDTLibrary/Private/GDTStorage.h"
@class GDTUploadCoordinator;
NS_ASSUME_NONNULL_BEGIN
@interface GDTStorage ()
/** 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<GDTStoredEvent *> *> *targetToEventSet;
/** All the events that have been stored. */
@property(readonly, nonatomic) NSMutableOrderedSet<GDTStoredEvent *> *storedEvents;
/** The upload coordinator instance used by this storage instance. */
@property(nonatomic) GDTUploadCoordinator *uploadCoordinator;
/** If YES, every call to -storeLog results in background task and serializes the singleton to disk.
*/
@property(nonatomic, readonly) 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/GDTLifecycle.h>
@class GDTEvent;
@protocol GDTEventTransformer;
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 GDTTransformer : NSObject <GDTLifecycleProtocol>
/** 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:(GDTEvent *)event
withTransformers:(nullable NSArray<id<GDTEventTransformer>> *)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 "GDTLibrary/Private/GDTTransformer.h"
@class GDTStorage;
NS_ASSUME_NONNULL_BEGIN
@interface GDTTransformer ()
/** 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) GDTStorage *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/GDTTransport.h>
@class GDTTransformer;
NS_ASSUME_NONNULL_BEGIN
@interface GDTTransport ()
/** 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<GDTEventTransformer>> *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) GDTTransformer *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/GDTLifecycle.h>
#import <GoogleDataTransport/GDTRegistrar.h>
#import "GDTLibrary/Private/GDTUploadPackage_Private.h"
@class GDTClock;
@class GDTStorage;
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 GDTUploadCoordinator
: NSObject <NSSecureCoding, GDTLifecycleProtocol, GDTUploadPackageProtocol>
/** 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 *, GDTUploadPackage *> *targetToInFlightPackages;
/** The storage object the coordinator will use. Generally used for testing. */
@property(nonatomic) GDTStorage *storage;
/** The registrar object the coordinator will use. Generally used for testing. */
@property(nonatomic) GDTRegistrar *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:(GDTTarget)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/GDTUploadPackage.h>
@class GDTStorage;
@interface GDTUploadPackage ()
/** The storage object this upload package will use to resolve event hashes to files. */
@property(nonatomic) GDTStorage *storage;
/** A handler that will receive callbacks for certain events. */
@property(nonatomic) id<NSSecureCoding, GDTUploadPackageProtocol> handler;
@end
@@ -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 GDTClock : 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 GDTClock object using the current time and offsets.
*
* @return A new GDTClock object representing the current time state.
*/
+ (instancetype)snapshot;
/** Creates a GDTClock 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:(GDTClock *)otherClock;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,79 @@
/*
* 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, GDTMessageCode) {
/** For warning messages concerning transportBytes: not being implemented by a data object. */
GDTMCWDataObjectMissingBytesImpl = 1,
/** For warning messages concerning a failed event upload. */
GDTMCWUploadFailed = 2,
/** For warning messages concerning a forced event upload. */
GDTMCWForcedUpload = 3,
/** For warning messages concerning a failed reachability call. */
GDTMCWReachabilityFailed = 4,
/** For error messages concerning transform: not being implemented by an event transformer. */
GDTMCETransformerDoesntImplementTransform = 1000,
/** For error messages concerning the creation of a directory failing. */
GDTMCEDirectoryCreationError = 1001,
/** For error messages concerning the writing of a event file. */
GDTMCEFileWriteError = 1002,
/** For error messages concerning the lack of a prioritizer for a given backend. */
GDTMCEPrioritizerError = 1003,
/** For error messages concerning a package delivery API violation. */
GDTMCEDeliverTwice = 1004,
/** For error messages concerning an error in an implementation of -transportBytes. */
GDTMCETransportBytesError = 1005,
/** For general purpose error messages in a dependency. */
GDTMCEGeneralError = 1006
};
/** */
FOUNDATION_EXPORT
void GDTLog(GDTMessageCode 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 GDTMessageCodeEnumToString(GDTMessageCode code);
// A define to wrap GULLogWarning with slightly more convenient usage.
#define GDTLogWarning(MESSAGE_CODE, MESSAGE_FORMAT, ...) \
GDTLog(MESSAGE_CODE, MESSAGE_FORMAT, __VA_ARGS__);
// A define to wrap GULLogError with slightly more convenient usage and a failing assert.
#define GDTLogError(MESSAGE_CODE, MESSAGE_FORMAT, ...) \
GDTLog(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 GDTDataFuture : 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/GDTEventDataObject.h>
@class GDTClock;
@class GDTDataFuture;
@class GDTStoredEvent;
NS_ASSUME_NONNULL_BEGIN
/** The different possible quality of service specifiers. High values indicate high priority. */
typedef NS_ENUM(NSInteger, GDTEventQoS) {
/** The QoS tier wasn't set, and won't ever be sent. */
GDTEventQoSUnknown = 0,
/** This event is internal telemetry data that should not be sent on its own if possible. */
GDTEventQoSTelemetry = 1,
/** This event should be sent, but in a batch only roughly once per day. */
GDTEventQoSDaily = 2,
/** This event should be sent when requested by the uploader. */
GDTEventQosDefault = 3,
/** This event should be sent immediately along with any other data that can be batched. */
GDTEventQoSFast = 4,
/** This event should only be uploaded on wifi. */
GDTEventQoSWifiOnly = 5,
};
@interface GDTEvent : 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 GDTEventDataObject protocol. */
@property(nullable, nonatomic) id<GDTEventDataObject> dataObject;
/** The quality of service tier this event belongs to. */
@property(nonatomic) GDTEventQoS qosTier;
/** The clock snapshot at the time of the event. */
@property(nonatomic) GDTClock *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 GDTStoredEvent equivalent of self.
*
* @param dataFuture The data future representing the transport bytes of the original event.
* @return An equivalent GDTStoredEvent.
*/
- (GDTStoredEvent *)storedEventWithDataFuture:(GDTDataFuture *)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 GDTEventDataObject <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 GDTEvent;
NS_ASSUME_NONNULL_BEGIN
/** Defines the API that event transformers must adopt. */
@protocol GDTEventTransformer <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.
*/
- (GDTEvent *)transform:(GDTEvent *)event;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,62 @@
/*
* 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/GDTPlatform.h>
@class GDTEvent;
NS_ASSUME_NONNULL_BEGIN
/** A protocol defining the lifecycle events objects in the library must respond to immediately. */
@protocol GDTLifecycleProtocol <NSObject>
@optional
/** Indicates an imminent app termination in the rare occurrence when -applicationWillTerminate: has
* been called.
*
* @param app The GDTApplication instance.
*/
- (void)appWillTerminate:(GDTApplication *)app;
/** Indicates that the app is moving to background and eventual suspension.
*
* @param app The GDTApplication instance.
*/
- (void)appWillBackground:(GDTApplication *)app;
/** Indicates that the app is resuming operation.
*
* @param app The GDTApplication instance.
*/
- (void)appWillForeground:(GDTApplication *)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 (GDTStorage and GDTUploadCoordinator singletons) will deserialize themselves from and to
* disk before and after every operation, respectively.
*/
@interface GDTLifecycle : NSObject <GDTApplicationDelegate>
@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 kGDTApplicationDidEnterBackgroundNotification;
/** A notification sent out if the app is foregrounding. */
FOUNDATION_EXPORT NSString *const kGDTApplicationWillEnterForegroundNotification;
/** A notification sent out if the app is terminating. */
FOUNDATION_EXPORT NSString *const kGDTApplicationWillTerminateNotification;
/** 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 GDTReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags);
/** A typedef identify background identifiers. */
typedef NSUInteger GDTBackgroundIdentifier;
/** A background task's invalid sentinel value. */
FOUNDATION_EXPORT const GDTBackgroundIdentifier GDTBackgroundIdentifierInvalid;
#if TARGET_OS_IOS || TARGET_OS_TV
/** A protocol that wraps UIApplicationDelegate or NSObject protocol, depending on the platform. */
@protocol GDTApplicationDelegate <UIApplicationDelegate>
#elif TARGET_OS_OSX
@protocol GDTApplicationDelegate <NSApplicationDelegate>
#else
@protocol GDTApplicationDelegate <NSObject>
#endif // TARGET_OS_IOS || TARGET_OS_TV
@end
/** A cross-platform application class. */
@interface GDTApplication : NSObject <GDTApplicationDelegate>
/** Creates and/or returns the shared application instance.
*
* @return The shared application instance.
*/
+ (nullable GDTApplication *)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 GDTBackgroundIdentifierInvalid if one couldn't
* be created.
*/
- (GDTBackgroundIdentifier)beginBackgroundTaskWithExpirationHandler:
(void (^__nullable)(void))handler;
/** Ends the background task if the identifier is valid.
*
* @param bgID The background task to end.
*/
- (void)endBackgroundTask:(GDTBackgroundIdentifier)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/GDTLifecycle.h>
#import <GoogleDataTransport/GDTUploadPackage.h>
@class GDTStoredEvent;
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, GDTUploadConditions) {
/** An upload shouldn't be attempted, because there's no network. */
GDTUploadConditionNoNetwork = 1 << 0,
/** An upload would likely use mobile data. */
GDTUploadConditionMobileData = 1 << 1,
/** An upload would likely use wifi data. */
GDTUploadConditionWifiData = 1 << 2,
/** An upload uses some sort of network connection, but it's unclear which. */
GDTUploadConditionUnclearConnection = 1 << 3,
/** A high priority event has occurred. */
GDTUploadConditionHighPriority = 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 GDTPrioritizer <NSObject, GDTLifecycleProtocol, GDTUploadPackageProtocol>
@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:(GDTStoredEvent *)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.
*/
- (GDTUploadPackage *)uploadPackageWithConditions:(GDTUploadConditions)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/GDTPrioritizer.h>
#import <GoogleDataTransport/GDTTargets.h>
#import <GoogleDataTransport/GDTUploader.h>
NS_ASSUME_NONNULL_BEGIN
/** Manages the registration of targets with the transport SDK. */
@interface GDTRegistrar : NSObject <GDTLifecycleProtocol>
/** 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<GDTUploader>)backend target:(GDTTarget)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<GDTPrioritizer>)prioritizer target:(GDTTarget)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/GDTDataFuture.h>
#import <GoogleDataTransport/GDTEvent.h>
@class GDTEvent;
NS_ASSUME_NONNULL_BEGIN
@interface GDTStoredEvent : NSObject <NSSecureCoding>
/** The data future representing the original event's transport bytes. */
@property(readonly, nonatomic) GDTDataFuture *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) GDTEventQoS qosTier;
/** The clock snapshot at the time of the event. */
@property(readonly, nonatomic) GDTClock *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:(GDTEvent *)event dataFuture:(GDTDataFuture *)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, GDTTarget) {
/** A target only used in testing. */
kGDTTargetTest = 999,
/** The CCT target. */
kGDTTargetCCT = 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/GDTEventTransformer.h>
@class GDTEvent;
NS_ASSUME_NONNULL_BEGIN
@interface GDTTransport : 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<GDTEventTransformer>> *)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:(GDTEvent *)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:(GDTEvent *)event;
/** Creates an event for use by this transport.
*
* @return An event that is suited for use by this transport.
*/
- (GDTEvent *)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/GDTTargets.h>
@class GDTClock;
@class GDTStoredEvent;
@class GDTUploadPackage;
/** A protocol that allows a handler to respond to package lifecycle events. */
@protocol GDTUploadPackageProtocol <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:(GDTUploadPackage *)package;
/** Indicates that the package was successfully delivered.
*
* @param package The package that was delivered.
*/
- (void)packageDelivered:(GDTUploadPackage *)package successful:(BOOL)successful;
@end
/** This class is a container that's handed off to uploaders. */
@interface GDTUploadPackage : NSObject <NSSecureCoding>
/** The set of stored events in this upload package. */
@property(nonatomic) NSSet<GDTStoredEvent *> *events;
/** The expiration time. If [[GDTClock snapshot] isAfter:deliverByTime] this package has expired.
*
* @note By default, the expiration time will be 3 minutes from creation.
*/
@property(nonatomic) GDTClock *deliverByTime;
/** The target of this package. */
@property(nonatomic, readonly) GDTTarget target;
/** Initializes a package instance.
*
* @param target The target/destination of this package.
* @return An instance of this class.
*/
- (instancetype)initWithTarget:(GDTTarget)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/GDTClock.h>
#import <GoogleDataTransport/GDTLifecycle.h>
#import <GoogleDataTransport/GDTPrioritizer.h>
#import <GoogleDataTransport/GDTTargets.h>
#import <GoogleDataTransport/GDTUploadPackage.h>
NS_ASSUME_NONNULL_BEGIN
/** This protocol defines the common interface for uploader implementations. */
@protocol GDTUploader <NSObject, GDTLifecycleProtocol, GDTUploadPackageProtocol>
@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:(GDTUploadConditions)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:(GDTUploadPackage *)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 "GDTClock.h"
#import "GDTConsoleLogger.h"
#import "GDTDataFuture.h"
#import "GDTEvent.h"
#import "GDTEventDataObject.h"
#import "GDTEventTransformer.h"
#import "GDTLifecycle.h"
#import "GDTPrioritizer.h"
#import "GDTRegistrar.h"
#import "GDTStoredEvent.h"
#import "GDTTargets.h"
#import "GDTTransport.h"
#import "GDTUploadPackage.h"
#import "GDTUploader.h"
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
+223
View File
@@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).