diff --git a/Analytics.podspec b/Analytics.podspec index ddb107a8e..1ad140ef2 100644 --- a/Analytics.podspec +++ b/Analytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Analytics" - s.version = "3.8.0-beta.1" + s.version = "3.7.0-beta.4" s.summary = "The hassle-free way to add analytics to your iOS app." s.description = <<-DESC @@ -17,8 +17,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '7.0' s.tvos.deployment_target = '9.0' - s.ios.frameworks = 'CoreTelephony' - s.frameworks = 'Security', 'StoreKit', 'SystemConfiguration', 'UIKit' + s.frameworks = 'CoreTelephony', 'Security', 'StoreKit', 'SystemConfiguration', 'UIKit' s.source_files = [ 'Analytics/Classes/**/*', diff --git a/Analytics.xcodeproj/project.pbxproj b/Analytics.xcodeproj/project.pbxproj index ea97bbbe9..c40b6a9a3 100644 --- a/Analytics.xcodeproj/project.pbxproj +++ b/Analytics.xcodeproj/project.pbxproj @@ -10,8 +10,6 @@ 3481C14AEC76E5A8DA64DA59 /* Pods_AnalyticsTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89033CBF22319674E6CE7E61 /* Pods_AnalyticsTests.framework */; }; 6E265C791FB1178C0030E08E /* IntegrationsManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E265C781FB1178C0030E08E /* IntegrationsManagerTest.swift */; }; 6EEC1C712017EA370089C478 /* EndToEndTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EEC1C702017EA370089C478 /* EndToEndTests.swift */; }; - A31958EF2385AC3A00A47EFA /* SerializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A31958EE2385AC3A00A47EFA /* SerializationTests.m */; }; - A352176023AD5825005B07F6 /* SEGMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = A352175F23AD5825005B07F6 /* SEGMacros.h */; settings = {ATTRIBUTES = (Private, ); }; }; EA88A5981DED7608009FB66A /* SEGSerializableValue.h in Headers */ = {isa = PBXBuildFile; fileRef = EA88A5971DED7608009FB66A /* SEGSerializableValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; EA8F09741E24C5C600B8B93F /* MiddlewareTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8F09731E24C5C600B8B93F /* MiddlewareTests.swift */; }; EAA542771EB4035400945DA7 /* TrackingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA542761EB4035400945DA7 /* TrackingTests.swift */; }; @@ -94,8 +92,6 @@ 6E265C781FB1178C0030E08E /* IntegrationsManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationsManagerTest.swift; sourceTree = ""; }; 6EEC1C702017EA370089C478 /* EndToEndTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndToEndTests.swift; sourceTree = ""; }; 89033CBF22319674E6CE7E61 /* Pods_AnalyticsTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AnalyticsTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A31958EE2385AC3A00A47EFA /* SerializationTests.m */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = SerializationTests.m; sourceTree = ""; tabWidth = 4; }; - A352175F23AD5825005B07F6 /* SEGMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SEGMacros.h; sourceTree = ""; }; D3BF8AE673FE0FD91DF5B503 /* Pods-AnalyticsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AnalyticsTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AnalyticsTests/Pods-AnalyticsTests.release.xcconfig"; sourceTree = ""; }; EA88A5971DED7608009FB66A /* SEGSerializableValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SEGSerializableValue.h; sourceTree = ""; }; EA8F09731E24C5C600B8B93F /* MiddlewareTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiddlewareTests.swift; sourceTree = ""; }; @@ -270,7 +266,6 @@ EADEB8EC1DECD335005322DA /* AnalyticsTests-Bridging-Header.h */, 6E265C781FB1178C0030E08E /* IntegrationsManagerTest.swift */, 6EEC1C702017EA370089C478 /* EndToEndTests.swift */, - A31958EE2385AC3A00A47EFA /* SerializationTests.m */, ); indentWidth = 2; path = AnalyticsTests; @@ -349,7 +344,6 @@ EADEB89F1DECD12B005322DA /* SEGUserDefaultsStorage.m */, EADEB8A01DECD12B005322DA /* SEGUtils.h */, EADEB8A11DECD12B005322DA /* SEGUtils.m */, - A352175F23AD5825005B07F6 /* SEGMacros.h */, EADEB8A21DECD12B005322DA /* UIViewController+SEGScreen.h */, EADEB8A31DECD12B005322DA /* UIViewController+SEGScreen.m */, ); @@ -413,7 +407,6 @@ EADEB8DE1DECD12B005322DA /* SEGAnalyticsConfiguration.h in Headers */, EADEB8D61DECD12B005322DA /* UIViewController+SEGScreen.h in Headers */, EADEB8CF1DECD12B005322DA /* SEGStorage.h in Headers */, - A352176023AD5825005B07F6 /* SEGMacros.h in Headers */, EADEB8D21DECD12B005322DA /* SEGUserDefaultsStorage.h in Headers */, EADEB8D01DECD12B005322DA /* SEGStoreKitTracker.h in Headers */, EADEB8D41DECD12B005322DA /* SEGUtils.h in Headers */, @@ -493,7 +486,6 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( - English, en, ); mainGroup = EADEB8511DECD080005322DA; @@ -549,7 +541,7 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-AnalyticsTests/Pods-AnalyticsTests-frameworks.sh", + "${SRCROOT}/Pods/Target Support Files/Pods-AnalyticsTests/Pods-AnalyticsTests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", "${BUILT_PRODUCTS_DIR}/Alamofire-Synchronous/Alamofire_Synchronous.framework", "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", @@ -568,7 +560,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AnalyticsTests/Pods-AnalyticsTests-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AnalyticsTests/Pods-AnalyticsTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -612,7 +604,6 @@ EADEB8F21DECD335005322DA /* UserDefaultsStorageTest.swift in Sources */, EAA542771EB4035400945DA7 /* TrackingTests.swift in Sources */, EADEB8F41DECD335005322DA /* ContextTest.swift in Sources */, - A31958EF2385AC3A00A47EFA /* SerializationTests.m in Sources */, EADEB8EE1DECD335005322DA /* HTTPClientTest.swift in Sources */, EADEB8F31DECD335005322DA /* AnalyticsTests.swift in Sources */, EADEB8F11DECD335005322DA /* FileStorageTest.swift in Sources */, diff --git a/Analytics.xcodeproj/xcshareddata/xcschemes/Analytics.xcscheme b/Analytics.xcodeproj/xcshareddata/xcschemes/Analytics.xcscheme index cce21eeec..377a38856 100644 --- a/Analytics.xcodeproj/xcshareddata/xcschemes/Analytics.xcscheme +++ b/Analytics.xcodeproj/xcshareddata/xcschemes/Analytics.xcscheme @@ -26,17 +26,9 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" shouldUseLaunchSchemeArgsEnv = "YES" codeCoverageEnabled = "YES"> - - - - @@ -49,11 +41,23 @@ + + + + + + + + + + + + + + - - - - - - + + + + --(id _Nullable) serializableDeepCopy; -@end - -@interface NSDictionary(SerializableDeepCopy) -@end - -@interface NSArray(SerializableDeepCopy) -@end - - NS_ASSUME_NONNULL_END diff --git a/Analytics/Classes/Internal/SEGAnalyticsUtils.m b/Analytics/Classes/Internal/SEGAnalyticsUtils.m index 5f6edaa8c..4ccefe55b 100644 --- a/Analytics/Classes/Internal/SEGAnalyticsUtils.m +++ b/Analytics/Classes/Internal/SEGAnalyticsUtils.m @@ -107,23 +107,25 @@ void SEGLog(NSString *format, ...) static id SEGCoerceJSONObject(id obj) { + // Hotfix: Storage format should support NSNull instead + if ([obj isKindOfClass:[NSNull class]]) { + return @""; + } // if the object is a NSString, NSNumber // then we're good if ([obj isKindOfClass:[NSString class]] || - [obj isKindOfClass:[NSNumber class]] || - [obj isKindOfClass:[NSNull class]]) { + [obj isKindOfClass:[NSNumber class]]) { return obj; } if ([obj isKindOfClass:[NSArray class]]) { NSMutableArray *array = [NSMutableArray array]; for (id i in obj) { - NSObject *value = i; // Hotfix: Storage format should support NSNull instead - if ([value isKindOfClass:[NSNull class]]) { - value = [NSData data]; + if ([i isKindOfClass:[NSNull class]]) { + continue; } - [array addObject:SEGCoerceJSONObject(value)]; + [array addObject:SEGCoerceJSONObject(i)]; } return array; } @@ -131,12 +133,16 @@ static id SEGCoerceJSONObject(id obj) if ([obj isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; for (NSString *key in obj) { - NSObject *value = obj[key]; + // Hotfix for issue where SEGFileStorage uses plist which does NOT support NSNull + // So when `[NSNull null]` gets passed in as track property values the queue serialization fails + if ([obj[key] isKindOfClass:[NSNull class]]) { + continue; + } if (![key isKindOfClass:[NSString class]]) SEGLog(@"warning: dictionary keys should be strings. got: %@. coercing " @"to: %@", [key class], [key description]); - dict[key.description] = SEGCoerceJSONObject(value); + dict[key.description] = SEGCoerceJSONObject(obj[key]); } return dict; } @@ -154,12 +160,31 @@ static id SEGCoerceJSONObject(id obj) return [obj description]; } +static void AssertDictionaryTypes(id dict) +{ +#ifdef DEBUG + assert([dict isKindOfClass:[NSDictionary class]]); + for (id key in dict) { + assert([key isKindOfClass:[NSString class]]); + id value = dict[key]; + + assert([value isKindOfClass:[NSString class]] || + [value isKindOfClass:[NSNumber class]] || + [value isKindOfClass:[NSNull class]] || + [value isKindOfClass:[NSArray class]] || + [value isKindOfClass:[NSDictionary class]] || + [value isKindOfClass:[NSDate class]] || + [value isKindOfClass:[NSURL class]]); + } +#endif +} + NSDictionary *SEGCoerceDictionary(NSDictionary *dict) { // make sure that a new dictionary exists even if the input is null dict = dict ?: @{}; // assert that the proper types are in the dictionary - dict = [dict serializableDeepCopy]; + AssertDictionaryTypes(dict); // coerce urls, and dates to the proper format return SEGCoerceJSONObject(dict); } @@ -189,75 +214,3 @@ static id SEGCoerceJSONObject(id obj) { return [[NSString alloc] initWithFormat:@"Viewed %@ Screen", title]; } - - -@implementation NSDictionary(SerializableDeepCopy) - -- (NSDictionary *)serializableDeepCopy -{ - NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:self.count]; - NSArray *keys = [self allKeys]; - for (id key in keys) { - id aValue = [self objectForKey:key]; - id theCopy = nil; - - if (![aValue conformsToProtocol:@protocol(NSCoding)]) { -#ifdef DEBUG - NSAssert(FALSE, @"key `%@` doesn't conform to NSCoding and can't be serialized for delivery.", key); -#else - SEGLog(@"key `%@` doesn't conform to NSCoding and can't be serialized for delivery.", key); - // simply leave it out since we can't encode it anyway. - continue; -#endif - } - - if ([aValue conformsToProtocol:@protocol(SEGSerializableDeepCopy)]) { - theCopy = [aValue serializableDeepCopy]; - } else if ([aValue conformsToProtocol:@protocol(NSCopying)]) { - theCopy = [aValue copy]; - } else { - theCopy = aValue; - } - - [returnDict setValue:theCopy forKey:key]; - } - - return [returnDict copy]; -} - -@end - - -@implementation NSArray(SerializableDeepCopy) - --(NSArray *)serializableDeepCopy -{ - NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:self.count]; - - for (id aValue in self) { - id theCopy = nil; - - if (![aValue conformsToProtocol:@protocol(NSCoding)]) { -#ifdef DEBUG - NSAssert(FALSE, @"type `%@` doesn't conform to NSCoding and can't be serialized for delivery.", NSStringFromClass([aValue class])); -#else - SEGLog(@"type `%@` doesn't conform to NSCoding and can't be serialized for delivery.", NSStringFromClass([aValue class])); - // simply leave it out since we can't encode it anyway. - continue; -#endif - } - - if ([aValue conformsToProtocol:@protocol(SEGSerializableDeepCopy)]) { - theCopy = [aValue serializableDeepCopy]; - } else if ([aValue conformsToProtocol:@protocol(NSCopying)]) { - theCopy = [aValue copy]; - } else { - theCopy = aValue; - } - [returnArray addObject:theCopy]; - } - - return [returnArray copy]; -} - -@end diff --git a/Analytics/Classes/Internal/SEGFileStorage.m b/Analytics/Classes/Internal/SEGFileStorage.m index 8e25ebac2..dd0fd21dd 100644 --- a/Analytics/Classes/Internal/SEGFileStorage.m +++ b/Analytics/Classes/Internal/SEGFileStorage.m @@ -87,38 +87,32 @@ - (NSData *)dataForKey:(NSString *)key - (NSDictionary *)dictionaryForKey:(NSString *)key { - return [self jsonForKey:key]; + return [self plistForKey:key]; } - (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key { - [self setJSON:dictionary forKey:key]; + [self setPlist:dictionary forKey:key]; } - (NSArray *)arrayForKey:(NSString *)key { - return [self jsonForKey:key]; + return [self plistForKey:key]; } - (void)setArray:(NSArray *)array forKey:(NSString *)key { - [self setJSON:array forKey:key]; + [self setPlist:array forKey:key]; } - (NSString *)stringForKey:(NSString *)key { - // unlike plists, we can't have stray values, they need to be - // in a dictionary or array container. - NSDictionary *data = [self jsonForKey:key]; - return data[key]; + return [self plistForKey:key]; } - (void)setString:(NSString *)string forKey:(NSString *)key { - // unlike plists, we can't have stray values, they need to be - // in a dictionary or array container. - NSDictionary *data = @{key: string}; - [self setJSON:data forKey:key]; + [self setPlist:string forKey:key]; } + (NSURL *)applicationSupportDirectoryURL @@ -135,66 +129,44 @@ - (NSURL *)urlForKey:(NSString *)key #pragma mark - Helpers -- (id _Nullable)jsonForKey:(NSString *)key +- (id _Nullable)plistForKey:(NSString *)key { - id result = nil; - NSData *data = [self dataForKey:key]; - if (data) { - BOOL needsConversion = NO; - id json = [self jsonFromData:data needsConversion:&needsConversion]; - if (needsConversion) { - [self setJSON:result forKey:key]; - result = @{key: result}; - } else { - result = json; - } - } - return result; + return data ? [self plistFromData:data] : nil; } -- (void)setJSON:(id _Nonnull)plist forKey:(NSString *)key +- (void)setPlist:(id _Nonnull)plist forKey:(NSString *)key { - NSDictionary *dict = nil; - - // json doesn't allow stand alone values like plist (previous storage format) does so - // we need to massage it a little. - if ([plist isKindOfClass:[NSDictionary class]] || [plist isKindOfClass:[NSArray class]]) { - dict = plist; - } else { - dict = @{key: plist}; - } - - NSData *data = [self dataFromJSON:dict]; + NSData *data = [self dataFromPlist:plist]; if (data) { [self setData:data forKey:key]; } } -- (NSData *_Nullable)dataFromJSON:(id)json +- (NSData *_Nullable)dataFromPlist:(nonnull id)plist { NSError *error = nil; - NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; + NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist + format:NSPropertyListXMLFormat_v1_0 + options:0 + error:&error]; if (error) { - SEGLog(@"Unable to serialize data from json object; %@, %@", error, json); + SEGLog(@"Unable to serialize data from plist object", error, plist); } return data; } -- (id _Nullable)jsonFromData:(NSData *_Nonnull)data needsConversion:(BOOL *)needsConversion +- (id _Nullable)plistFromData:(NSData *_Nonnull)data { NSError *error = nil; - id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + id plist = [NSPropertyListSerialization propertyListWithData:data + options:0 + format:nil + error:&error]; if (error) { - // maybe it's a plist and needs to be converted. - result = [self plistFromData:data]; - if (result != nil) { - *needsConversion = YES; - } else { - SEGLog(@"Unable to parse json from data %@", error); - } + SEGLog(@"Unable to parse plist from data %@", error); } - return result; + return plist; } - (void)createDirectoryAtURLIfNeeded:(NSURL *)url @@ -211,39 +183,4 @@ - (void)createDirectoryAtURLIfNeeded:(NSURL *)url } } -/// Deprecated -- (NSData *_Nullable)dataFromPlist:(nonnull id)plist -{ - NSError *error = nil; - NSData *data = nil; - // Temporary just-in-case fix for issue #846; Follow-on PR to move away from plist storage. - @try { - data = [NSPropertyListSerialization dataWithPropertyList:plist - format:NSPropertyListXMLFormat_v1_0 - options:0 - error:&error]; - } @catch (NSException *e) { - SEGLog(@"Unable to serialize data from plist object; Exception: %@, plist: %@", e, plist); - } @finally { - if (error) { - SEGLog(@"Unable to serialize data from plist object; Error: %@, plist: %@", error, plist); - } - } - return data; -} - -/// Deprecated -- (id _Nullable)plistFromData:(NSData *_Nonnull)data -{ - NSError *error = nil; - id plist = [NSPropertyListSerialization propertyListWithData:data - options:0 - format:nil - error:&error]; - if (error) { - SEGLog(@"Unable to parse plist from data %@", error); - } - return plist; -} - @end diff --git a/Analytics/Classes/Internal/SEGMacros.h b/Analytics/Classes/Internal/SEGMacros.h deleted file mode 100644 index bfc470046..000000000 --- a/Analytics/Classes/Internal/SEGMacros.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// SEGMacros.h -// Analytics -// -// Created by Brandon Sneed on 12/20/19. -// Copyright © 2019 Segment. All rights reserved. -// - -#ifndef SEGMacros_h -#define SEGMacros_h - -#define __deprecated__(s) __attribute__((deprecated(s))) - -#define weakify(var) __weak typeof(var) __weak_##var = var; - -#define strongify(var) \ -_Pragma("clang diagnostic push") \ -_Pragma("clang diagnostic ignored \"-Wshadow\"") \ -__strong typeof(var) var = __weak_##var; \ -_Pragma("clang diagnostic pop") - -#endif /* SEGMacros_h */ diff --git a/Analytics/Classes/Internal/SEGSegmentIntegration.m b/Analytics/Classes/Internal/SEGSegmentIntegration.m index 3d16084d3..1c7b68730 100644 --- a/Analytics/Classes/Internal/SEGSegmentIntegration.m +++ b/Analytics/Classes/Internal/SEGSegmentIntegration.m @@ -7,7 +7,6 @@ #import "SEGReachability.h" #import "SEGHTTPClient.h" #import "SEGStorage.h" -#import "SEGMacros.h" #if TARGET_OS_IOS #import @@ -54,7 +53,7 @@ static BOOL GetAdTrackingEnabled() @interface SEGSegmentIntegration () @property (nonatomic, strong) NSMutableArray *queue; -@property (nonatomic, strong) NSDictionary *_cachedStaticContext; +@property (nonatomic, strong) NSDictionary *cachedStaticContext; @property (nonatomic, strong) NSURLSessionUploadTask *batchRequest; @property (nonatomic, assign) UIBackgroundTaskIdentifier flushTaskID; @property (nonatomic, strong) SEGReachability *reachability; @@ -108,24 +107,26 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht [self trackAttributionData:self.configuration.trackAttributionData]; }]; - self.flushTimer = [NSTimer timerWithTimeInterval:self.configuration.flushInterval - target:self - selector:@selector(flush) - userInfo:nil - repeats:YES]; - - [NSRunLoop.mainRunLoop addTimer:self.flushTimer - forMode:NSDefaultRunLoopMode]; - - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(updateStaticContext) - name:UIApplicationWillEnterForegroundNotification - object:nil]; + if ([NSThread isMainThread]) { + [self setupFlushTimer]; + } else { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self setupFlushTimer]; + }); + } } return self; } +- (void)setupFlushTimer +{ + self.flushTimer = [NSTimer scheduledTimerWithTimeInterval:self.configuration.flushInterval + target:self + selector:@selector(flush) + userInfo:nil + repeats:YES]; +} + /* * There is an iOS bug that causes instances of the CTTelephonyNetworkInfo class to * sometimes get notifications after they have been deallocated. @@ -167,7 +168,6 @@ - (NSDictionary *)staticContext dict[@"type"] = @"ios"; dict[@"model"] = GetDeviceModel(); dict[@"id"] = [[device identifierForVendor] UUIDString]; - dict[@"name"] = [device model]; if (NSClassFromString(SEGAdvertisingClassIdentifier)) { dict[@"adTrackingEnabled"] = @(GetAdTrackingEnabled()); } @@ -212,29 +212,6 @@ - (NSDictionary *)staticContext return dict; } -- (void)updateStaticContext -{ - self.cachedStaticContext = [self staticContext]; -} - -- (NSDictionary *)cachedStaticContext { - __block NSDictionary *result = nil; - weakify(self); - dispatch_sync(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ - strongify(self); - result = self._cachedStaticContext; - }); - return result; -} - -- (void)setCachedStaticContext:(NSDictionary *)cachedStaticContext { - weakify(self); - dispatch_sync(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ - strongify(self); - self._cachedStaticContext = cachedStaticContext; - }); -} - - (NSDictionary *)liveContext { NSMutableDictionary *context = [[NSMutableDictionary alloc] init]; diff --git a/Analytics/Classes/Internal/SEGUtils.m b/Analytics/Classes/Internal/SEGUtils.m index b6019a109..afbc01418 100644 --- a/Analytics/Classes/Internal/SEGUtils.m +++ b/Analytics/Classes/Internal/SEGUtils.m @@ -37,6 +37,10 @@ + (id _Nullable)plistFromData:(NSData *_Nonnull)data +(id)traverseJSON:(id)object andReplaceWithFilters:(NSDictionary*)patterns { + if (object == nil || object == NSNull.null || [object isKindOfClass:NSNull.class]) { + return object; + } + if ([object isKindOfClass:NSDictionary.class]) { NSDictionary* dict = object; NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:dict.count]; diff --git a/Analytics/Classes/SEGAnalytics.m b/Analytics/Classes/SEGAnalytics.m index 67b638b5d..b62116330 100644 --- a/Analytics/Classes/SEGAnalytics.m +++ b/Analytics/Classes/SEGAnalytics.m @@ -111,8 +111,6 @@ - (void)handleAppStateNotification:(NSNotification *)note [self _applicationDidFinishLaunchingWithOptions:note.userInfo]; } else if ([note.name isEqualToString:UIApplicationWillEnterForegroundNotification]) { [self _applicationWillEnterForeground]; - } else if ([note.name isEqualToString: UIApplicationDidEnterBackgroundNotification]) { - [self _applicationDidEnterBackground]; } } @@ -169,19 +167,15 @@ - (void)_applicationWillEnterForeground if (!self.configuration.trackApplicationLifecycleEvents) { return; } + NSString *currentVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; + NSString *currentBuild = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; [self track:@"Application Opened" properties:@{ @"from_background" : @YES, + @"version" : currentVersion ?: @"", + @"build" : currentBuild ?: @"", }]; } -- (void)_applicationDidEnterBackground -{ - if (!self.configuration.trackApplicationLifecycleEvents) { - return; - } - [self track: @"Application Backgrounded"]; -} - #pragma mark - Public API @@ -422,9 +416,7 @@ + (void)debug:(BOOL)showDebugLogs + (NSString *)version { - // this has to match the actual version, NOT what's in info.plist - // because Apple only accepts X.X.X as versions in the review process. - return @"3.8.0-beta.1"; + return @"3.7.0-beta.4"; } #pragma mark - Helpers diff --git a/Analytics/Info.plist b/Analytics/Info.plist index 2e2340f5c..111a5b05b 100644 --- a/Analytics/Info.plist +++ b/Analytics/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.8.0 + 3.7.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/AnalyticsTests/AnalyticsTests.swift b/AnalyticsTests/AnalyticsTests.swift index 0166e642c..17ec11c18 100644 --- a/AnalyticsTests/AnalyticsTests.swift +++ b/AnalyticsTests/AnalyticsTests.swift @@ -103,13 +103,6 @@ class AnalyticsTests: QuickSpec { expect(event?.event) == "Application Opened" expect(event?.properties?["from_background"] as? Bool) == true } - - it("fires Application Backgrounded during UIApplicationDidEnterBackground") { - testMiddleware.swallowEvent = true - NotificationCenter.default.post(name: .UIApplicationDidEnterBackground, object: testApplication) - let event = testMiddleware.lastContext?.payload as? SEGTrackPayload - expect(event?.event) == "Application Backgrounded" - } it("flushes when UIApplicationDidEnterBackgroundNotification is fired") { analytics.track("test") diff --git a/AnalyticsTests/IntegrationsManagerTest.swift b/AnalyticsTests/IntegrationsManagerTest.swift index 8e1d41824..9f78b699a 100644 --- a/AnalyticsTests/IntegrationsManagerTest.swift +++ b/AnalyticsTests/IntegrationsManagerTest.swift @@ -1,7 +1,6 @@ import Analytics import Quick import Nimble -import SwiftTryCatch class IntegrationsManagerTest: QuickSpec { @@ -9,40 +8,6 @@ class IntegrationsManagerTest: QuickSpec { describe("IntegrationsManager") { context("is track event enabled for integration in plan") { - it("asserts when invalid value types are used integration enablement flags") { - var exception: NSException? = nil - SwiftTryCatch.tryRun({ - SEGIntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": "blah"]) - }, catchRun: { e in - exception = e - }, finallyRun: nil) - - expect(exception).toNot(beNil()) - } - - it("asserts when invalid value types are used integration enablement flags") { - var exception: NSException? = nil - SwiftTryCatch.tryRun({ - SEGIntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": ["key": 1]]) - }, catchRun: { e in - exception = e - }, finallyRun: nil) - - expect(exception).toNot(beNil()) - } - - it("pulls valid integration data when supplied") { - let enabled = SEGIntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": true]) - expect(enabled).to(beTrue()) - } - - it("falls back correctly when values aren't explicitly specified") { - let enabled = SEGIntegrationsManager.isIntegration("comScore", enabledInOptions: ["all": true]) - expect(enabled).to(beTrue()) - let allEnabled = SEGIntegrationsManager.isIntegration("comScore", enabledInOptions: ["All": true]) - expect(allEnabled).to(beTrue()) - } - it("returns true when there is no plan") { let enabled = SEGIntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Amplitude", inPlan:[:]) expect(enabled).to(beTrue()) diff --git a/AnalyticsTests/MiddlewareTests.swift b/AnalyticsTests/MiddlewareTests.swift index 2d8a3c9bd..3dd86eb4e 100644 --- a/AnalyticsTests/MiddlewareTests.swift +++ b/AnalyticsTests/MiddlewareTests.swift @@ -21,7 +21,6 @@ let customizeAllTrackCalls = SEGBlockMiddleware { (context, next) in let newEvent = "[New] \(track.event)" var newProps = track.properties ?? [:] newProps["customAttribute"] = "Hello" - newProps["nullTest"] = NSNull() ctx.payload = SEGTrackPayload( event: newEvent, properties: newProps, @@ -66,8 +65,6 @@ class MiddlewareTests: QuickSpec { let track = passthrough.lastContext?.payload as? SEGTrackPayload expect(track?.event) == "[New] Purchase Success" expect(track?.properties?["customAttribute"] as? String) == "Hello" - let isNull = (track?.properties?["nullTest"] is NSNull) - expect(isNull) == true } it("expects event to be swallowed if next is not called") { diff --git a/AnalyticsTests/SerializationTests.m b/AnalyticsTests/SerializationTests.m deleted file mode 100644 index 76c57eb66..000000000 --- a/AnalyticsTests/SerializationTests.m +++ /dev/null @@ -1,55 +0,0 @@ -// -// PropSerializationTests.m -// AnalyticsTests -// -// Created by Brandon Sneed on 11/20/19. -// Copyright © 2019 Segment. All rights reserved. -// - -#import -@import Analytics; - -@protocol SEGSerializableDeepCopy --(id _Nullable) serializableDeepCopy; -@end - -@interface NSDictionary(SerializableDeepCopy) -@end - -@interface NSArray(SerializableDeepCopy) -@end - -@interface SerializationTests : XCTestCase - -@end - -@implementation SerializationTests - -- (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. -} - -- (void)testDeepCopyAndConformance { - NSDictionary *nonserializable = @{@"test": @1, @"nonserializable": self, @"nested": @{@"nonserializable": self}, @"array": @[@1, @2, @3, self]}; - NSDictionary *serializable = @{@"test": @1, @"nonserializable": @0, @"nested": @{@"nonserializable": @0}, @"array": @[@1, @2, @3, @0]}; - - NSDictionary *aCopy = [serializable serializableDeepCopy]; - XCTAssert(aCopy != serializable); - - NSDictionary *sub = [serializable objectForKey:@"nested"]; - NSDictionary *subCopy = [aCopy objectForKey:@"nested"]; - XCTAssert(sub != subCopy); - - NSDictionary *array = [serializable objectForKey:@"array"]; - NSDictionary *arrayCopy = [aCopy objectForKey:@"array"]; - XCTAssert(array != arrayCopy); - - XCTAssertNoThrow([serializable serializableDeepCopy]); - XCTAssertThrows([nonserializable serializableDeepCopy]); -} - -@end diff --git a/AnalyticsTests/TrackingTests.swift b/AnalyticsTests/TrackingTests.swift index 53f8ced2b..77c2bf9e8 100644 --- a/AnalyticsTests/TrackingTests.swift +++ b/AnalyticsTests/TrackingTests.swift @@ -89,15 +89,6 @@ class TrackingTests: QuickSpec { expect(payload?.groupId) == "acme-company" expect(payload?.traits?["employees"] as? Int) == 2333 } - - it("handles null values") { - analytics.track("null test", properties: [ - "nullTest": NSNull() - ]) - let payload = passthrough.lastContext?.payload as? SEGTrackPayload - let isNull = (payload?.properties?["nullTest"] is NSNull) - expect(isNull) == true - } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 28056324e..723fbb370 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,28 +1,6 @@ Change Log ========== -Version 3.8.0-beta.1 *(7th January, 2020)* ------------------------------------------- - - * [Fix](https://github.com/segmentio/analytics-ios/pull/856) Reload static context data when the app returns from background. - * [Fix](https://github.com/segmentio/analytics-ios/pull/855) Fixes issue where customers can overwrite information regarding integration enablement. - * [Fix](https://github.com/segmentio/analytics-ios/pull/854) Swapped JSON in for the storage format instead of plists. - * [Fix](https://github.com/segmentio/analytics-ios/pull/853) Hardened handling of user-supplied data in event properties. - * [New](https://github.com/segmentio/analytics-ios/pull/839) Added support for SSL pinning. - * [Fix](https://github.com/segmentio/analytics-ios/pull/842) CoreTelephony library is now only included on iOS targets. - -Version 3.8.0-beta.0 *(25th July, 2019)* ----------------------------------------- - - * [New](https://github.com/segmentio/analytics-ios/pull/831): Add iOS Backgrounded Event. - * [Fix](https://github.com/segmentio/analytics-ios/pull/785): Fix GCD mutual dependency - * [Fix](https://github.com/segmentio/analytics-ios/pull/): adding “name” field to payload; updated nimble to version 7.3.4 - -Version 3.7.0 *(22nd July, 2019)* ---------------------------------- - -This release promotes 3.7.0-beta.4 to stable. - Version 3.7.0-beta.4 *(19th June, 2019)* ----------------------------------------- * [Fix](https://github.com/segmentio/analytics-ios/pull/812): Remove invalid `.clang-format` symlink which can cause issues with manual builds. diff --git a/Examples/CarthageExample/Cartfile b/Examples/CarthageExample/Cartfile index 0fa78b699..65d12a1b4 100644 --- a/Examples/CarthageExample/Cartfile +++ b/Examples/CarthageExample/Cartfile @@ -1,3 +1,3 @@ -github "segmentio/analytics-ios" "3.8.0-beta.0" +github "segmentio/analytics-ios" "3.7.0-beta.4" # Use a local project when debugging # git "~/Code/segmentio/analytics-ios/" "master" diff --git a/Podfile b/Podfile index 2446fe8af..4c1ed0721 100644 --- a/Podfile +++ b/Podfile @@ -1,10 +1,10 @@ target 'AnalyticsTests' do platform :ios, '11' - + use_frameworks! - + pod 'Quick', '~> 1.2.0' - pod 'Nimble', '~> 7.3.4' + pod 'Nimble', '~> 7.3.1' pod 'Nocilla', '~> 0.11.0' pod 'Alamofire', '~> 4.5' pod 'Alamofire-Synchronous', '~> 4.0' diff --git a/Podfile.lock b/Podfile.lock index 53dc69dda..162d71864 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -2,7 +2,7 @@ PODS: - Alamofire (4.6.0) - Alamofire-Synchronous (4.0.0): - Alamofire (~> 4.0) - - Nimble (7.3.4) + - Nimble (7.3.1) - Nocilla (0.11.0) - Quick (1.2.0) - SwiftTryCatch (1.0.0) @@ -10,13 +10,13 @@ PODS: DEPENDENCIES: - Alamofire (~> 4.5) - Alamofire-Synchronous (~> 4.0) - - Nimble (~> 7.3.4) + - Nimble (~> 7.3.1) - Nocilla (~> 0.11.0) - Quick (~> 1.2.0) - SwiftTryCatch (from `https://github.com/segmentio/SwiftTryCatch.git`) SPEC REPOS: - https://github.com/CocoaPods/Specs.git: + https://github.com/cocoapods/specs.git: - Alamofire - Alamofire-Synchronous - Nimble @@ -35,11 +35,11 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: Alamofire: f41a599bd63041760b26d393ec1069d9d7b917f4 Alamofire-Synchronous: eedf1e6e961c3795a63c74990b3f7d9fbfac7e50 - Nimble: 051e3d8912d40138fa5591c78594f95fb172af37 + Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae Nocilla: 7af7a386071150cc8aa5da4da97d060f049dd61c Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 SwiftTryCatch: 2f4ef36cf5396bdb450006b70633dbce5260d3b3 -PODFILE CHECKSUM: 6c11ce6879d40225a5ec2b9b5ac275626edbeeb6 +PODFILE CHECKSUM: cf4abb4263c7b514d71c70514284ac657d90865d -COCOAPODS: 1.8.4 +COCOAPODS: 1.5.3 diff --git a/README.md b/README.md index a9c905752..5df485b85 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Analytics is available through [CocoaPods](http://cocoapods.org) and [Carthage]( ### CocoaPods ```ruby -pod "Analytics", "3.7.0" +pod "Analytics", "3.6.10" ``` ### Carthage