diff --git a/Analytics.xcodeproj/project.pbxproj b/Analytics.xcodeproj/project.pbxproj index cc835cf95..1fabe6b19 100644 --- a/Analytics.xcodeproj/project.pbxproj +++ b/Analytics.xcodeproj/project.pbxproj @@ -23,12 +23,13 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 019892FF1B67121A00ED875F /* SEGKahunaDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 019892FE1B67121A00ED875F /* SEGKahunaDefines.h */; }; + 01FB19751B66881F000483CC /* SEGKahunaIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01FB19741B66881F000483CC /* SEGKahunaIntegrationTests.m */; }; 3208C0861A64F6B200D6014F /* AMPARCMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 3208C0811A64F6B200D6014F /* AMPARCMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3208C0871A64F6B200D6014F /* AMPConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 3208C0821A64F6B200D6014F /* AMPConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3208C0881A64F6B200D6014F /* AMPDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3208C0831A64F6B200D6014F /* AMPDeviceInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3208C0891A64F6B200D6014F /* AMPLocationManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 3208C0841A64F6B200D6014F /* AMPLocationManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3208C08A1A64F6B200D6014F /* Amplitude.h in Headers */ = {isa = PBXBuildFile; fileRef = 3208C0851A64F6B200D6014F /* Amplitude.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 320AFEE91A840DEB00929778 /* KahunaAnalytics.h in Headers */ = {isa = PBXBuildFile; fileRef = 320AFEE81A840DEB00929778 /* KahunaAnalytics.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32225231199AD6D200478B1A /* SEGReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 3222522F199AD6D200478B1A /* SEGReachability.h */; }; 32225232199AD6D200478B1A /* SEGReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 32225230199AD6D200478B1A /* SEGReachability.m */; }; 32225233199AD6D200478B1A /* SEGReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 32225230199AD6D200478B1A /* SEGReachability.m */; }; @@ -237,6 +238,7 @@ 6E85F3901B2F922F00763913 /* CountlyDB.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E85F38F1B2F922F00763913 /* CountlyDB.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E85F3921B2F974700763913 /* Localytics.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E85F3911B2F974700763913 /* Localytics.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E85F3941B2F980B00763913 /* MPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E85F3931B2F980B00763913 /* MPLogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E865BE41B697BBB00F80367 /* Kahuna.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E865BE31B697BBB00F80367 /* Kahuna.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E8F058C1B42321800305E99 /* SEGOptimizelyIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E8F058B1B42321800305E99 /* SEGOptimizelyIntegrationTests.m */; }; 6E92C1E71B3DD5260049B5A8 /* SEGLocalyticsIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E92C1E61B3DD5260049B5A8 /* SEGLocalyticsIntegrationTests.m */; }; 6E9F1EE71B605E7400E55D88 /* SEGApptimizeIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E9F1EE61B605E7400E55D88 /* SEGApptimizeIntegrationTests.m */; }; @@ -325,6 +327,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 019892FE1B67121A00ED875F /* SEGKahunaDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SEGKahunaDefines.h; sourceTree = ""; }; + 01FB19741B66881F000483CC /* SEGKahunaIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SEGKahunaIntegrationTests.m; path = Integrations/Kahuna/SEGKahunaIntegrationTests.m; sourceTree = ""; }; 095FDFE35C86958DB4798F57 /* Pods-Analytics.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Analytics.release.xcconfig"; path = "Pods/Target Support Files/Pods-Analytics/Pods-Analytics.release.xcconfig"; sourceTree = ""; }; 23CD79ED9C3E478C83CB7A34 /* libPods-AnalyticsTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AnalyticsTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 24AE4A015CBF4E95B18D10A7 /* libPods-iOS Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iOS Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -333,7 +337,6 @@ 3208C0831A64F6B200D6014F /* AMPDeviceInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AMPDeviceInfo.h; path = "Pods/Amplitude-iOS/Amplitude/AMPDeviceInfo.h"; sourceTree = ""; }; 3208C0841A64F6B200D6014F /* AMPLocationManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AMPLocationManagerDelegate.h; path = "Pods/Amplitude-iOS/Amplitude/AMPLocationManagerDelegate.h"; sourceTree = ""; }; 3208C0851A64F6B200D6014F /* Amplitude.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Amplitude.h; path = "Pods/Amplitude-iOS/Amplitude/Amplitude.h"; sourceTree = ""; }; - 320AFEE81A840DEB00929778 /* KahunaAnalytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = KahunaAnalytics.h; path = Pods/KahunaSDK/Kahuna/KahunaAnalytics.h; sourceTree = ""; }; 3222522F199AD6D200478B1A /* SEGReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SEGReachability.h; path = Helpers/SEGReachability.h; sourceTree = ""; }; 32225230199AD6D200478B1A /* SEGReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SEGReachability.m; path = Helpers/SEGReachability.m; sourceTree = ""; }; 32385FD81A2DF0CA008E09B2 /* MPCategoryHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MPCategoryHelpers.h; path = Pods/Mixpanel/Mixpanel/MPCategoryHelpers.h; sourceTree = ""; }; @@ -543,6 +546,7 @@ 6E85F38F1B2F922F00763913 /* CountlyDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CountlyDB.h; path = Pods/Countly/CountlyDB.h; sourceTree = ""; }; 6E85F3911B2F974700763913 /* Localytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Localytics.h; path = "Pods/Localytics/Localytics-iOS-3.3.0/Localytics.h"; sourceTree = ""; }; 6E85F3931B2F980B00763913 /* MPLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MPLogger.h; path = Pods/Mixpanel/Mixpanel/MPLogger.h; sourceTree = ""; }; + 6E865BE31B697BBB00F80367 /* Kahuna.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Kahuna.h; path = Pods/Kahuna/Kahuna.framework/Headers/Kahuna.h; sourceTree = ""; }; 6E8F058B1B42321800305E99 /* SEGOptimizelyIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SEGOptimizelyIntegrationTests.m; path = Integrations/Optimizely/SEGOptimizelyIntegrationTests.m; sourceTree = ""; }; 6E92C1E61B3DD5260049B5A8 /* SEGLocalyticsIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SEGLocalyticsIntegrationTests.m; path = Integrations/Localytics/SEGLocalyticsIntegrationTests.m; sourceTree = ""; }; 6E9F1EE61B605E7400E55D88 /* SEGApptimizeIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SEGApptimizeIntegrationTests.m; path = Integrations/Apptimize/SEGApptimizeIntegrationTests.m; sourceTree = ""; }; @@ -682,6 +686,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 01FB19711B667B47000483CC /* Kahuna */ = { + isa = PBXGroup; + children = ( + 01FB19741B66881F000483CC /* SEGKahunaIntegrationTests.m */, + ); + name = Kahuna; + sourceTree = ""; + }; 3242C99C1946A3E100FBE441 /* iOS Tests */ = { isa = PBXGroup; children = ( @@ -759,6 +771,7 @@ children = ( 32F638191A8134A600C65643 /* SEGKahunaIntegration.h */, 32F6381A1A8134A600C65643 /* SEGKahunaIntegration.m */, + 019892FE1B67121A00ED875F /* SEGKahunaDefines.h */, ); name = Kahuna; sourceTree = ""; @@ -793,6 +806,7 @@ 6E16FFF31B33AD0600B4F7FE /* Integrations */ = { isa = PBXGroup; children = ( + 01FB19711B667B47000483CC /* Kahuna */, 6EB4681F1B39CDBD00A0E4B9 /* Amplitude */, 6EF6652D1B585DC900DC7EA1 /* AppsFlyer */, 6E9F1EE51B605E4100E55D88 /* Apptimize */, @@ -938,6 +952,7 @@ D3D35174186242B4005586E7 /* Headers */ = { isa = PBXGroup; children = ( + 6E865BE31B697BBB00F80367 /* Kahuna.h */, 6E2AFA401B55852400C8A14E /* MOInbox.h */, 6E2AFA411B55852400C8A14E /* MOInboxViewController.h */, 6E2AFA3C1B55850C00C8A14E /* MOEHelperConstants.h */, @@ -1013,7 +1028,6 @@ 325DCE2E1A8D875100D4142E /* BugsnagNotifier.h */, 325DCE2F1A8D875100D4142E /* BugsnagOSXNotifier.h */, 325DCE301A8D875100D4142E /* BugsnagSink.h */, - 320AFEE81A840DEB00929778 /* KahunaAnalytics.h */, 3208C0811A64F6B200D6014F /* AMPARCMacros.h */, 3208C0821A64F6B200D6014F /* AMPConstants.h */, 3208C0831A64F6B200D6014F /* AMPDeviceInfo.h */, @@ -1422,7 +1436,6 @@ 6E165A1A1B54771A002C1C40 /* GAITrackedViewController.h in Headers */, 6E165A1B1B54771A002C1C40 /* GAITracker.h in Headers */, 6E85F3901B2F922F00763913 /* CountlyDB.h in Headers */, - 320AFEE91A840DEB00929778 /* KahunaAnalytics.h in Headers */, 6E85F3711B2F792100763913 /* TSAppEventSourceImpl.h in Headers */, 32385FE31A2DF0FB008E09B2 /* MPUITableViewBinding.h in Headers */, 3252EA881999D6CC0056C32A /* Taplytics.h in Headers */, @@ -1433,6 +1446,7 @@ 32CEE41E19F5B6DB007758F2 /* TaplyticsOptions.h in Headers */, 32385FDF1A2DF0E7008E09B2 /* MPEventBinding.h in Headers */, DAEB6FF11AC9EBCA003E7FB3 /* SEGApptimizeIntegration.h in Headers */, + 6E865BE41B697BBB00F80367 /* Kahuna.h in Headers */, 32CCD99C1919A4F4007B63BA /* Analytics.h in Headers */, EA25892317C6979D000CEF61 /* SEGAnalytics.h in Headers */, D371F32E185942BB0053337D /* Crittercism.h in Headers */, @@ -1596,6 +1610,7 @@ 32430A761914186C004EFF59 /* SEGLocation.h in Headers */, D331BD351860F5F0007CB22F /* TSLogLevel.h in Headers */, D3EDB66218C315F100A88A7E /* SEGLocalyticsIntegration.h in Headers */, + 019892FF1B67121A00ED875F /* SEGKahunaDefines.h in Headers */, D331BD361860F5F0007CB22F /* TSPlatform.h in Headers */, D3EDB65C18C315F100A88A7E /* SEGCrittercismIntegration.h in Headers */, D3EDB66618C315F100A88A7E /* SEGSegmentioIntegration.h in Headers */, @@ -1866,6 +1881,7 @@ 3242C9A31946A3E100FBE441 /* iOS_Tests.m in Sources */, 3242C9B21946ADA900FBE441 /* SEGAnalyticsIntegration.m in Sources */, 32A69C241979CF4A00D69943 /* SEGOptimizelyIntegration.m in Sources */, + 01FB19751B66881F000483CC /* SEGKahunaIntegrationTests.m in Sources */, 3242C9B71946AFCC00FBE441 /* SEGCountlyIntegration.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Analytics/Integrations/Kahuna/SEGKahunaIntegration.h b/Analytics/Integrations/Kahuna/SEGKahunaIntegration.h index 4b123c33d..7bed126e2 100644 --- a/Analytics/Integrations/Kahuna/SEGKahunaIntegration.h +++ b/Analytics/Integrations/Kahuna/SEGKahunaIntegration.h @@ -14,6 +14,7 @@ @property (nonatomic, assign) BOOL valid; @property (nonatomic, assign) BOOL initialized; @property (nonatomic, copy) NSDictionary *settings; +@property Class kahunaClass; @end @@ -22,6 +23,7 @@ @property (nonatomic) NSDictionary *pushInfo; @property (nonatomic) UIApplicationState applicationState; @property (nonatomic) BOOL kahunaInitialized; +@property Class kahunaClass; + (instancetype)sharedInstance; - (void)didFinishLaunching:(NSNotification *)userInfo; diff --git a/Analytics/Integrations/Kahuna/SEGKahunaIntegration.m b/Analytics/Integrations/Kahuna/SEGKahunaIntegration.m index 1a39fce28..81cbce7e5 100644 --- a/Analytics/Integrations/Kahuna/SEGKahunaIntegration.m +++ b/Analytics/Integrations/Kahuna/SEGKahunaIntegration.m @@ -1,8 +1,9 @@ // KahunaIntegration.m // Copyright (c) 2014 Segment.io. All rights reserved. +#import "SEGKahunaDefines.h" #import "SEGKahunaIntegration.h" -#import "KahunaAnalytics.h" +#import "Kahuna.h" #import "SEGAnalyticsUtils.h" #import "SEGAnalytics.h" #import @@ -17,26 +18,6 @@ void (*selOriginalApplicationDidReceiveRemoteNotificationWithFetchCompletionHandler)(id, SEL, id, id, void (^)(UIBackgroundFetchResult result)); void (*selOriginalApplicationHandleActionWithIdentifierWithFetchCompletionHandler)(id, SEL, id, id, id, void (^)()); -static NSString *const KAHUNA_VIEWED_PRODUCT_CATEGORY = @"Viewed Product Category"; -static NSString *const KAHUNA_VIEWED_PRODUCT = @"Viewed Product"; -static NSString *const KAHUNA_ADDED_PRODUCT = @"Added Product"; -static NSString *const KAHUNA_COMPLETED_ORDER = @"Completed Order"; - -static NSString *const KAHUNA_LAST_VIEWED_CATEGORY = @"Last Viewed Category"; -static NSString *const KAHUNA_CATEGORIES_VIEWED = @"Categories Viewed"; -static NSString *const KAHUNA_LAST_PRODUCT_VIEWED_NAME = @"Last Product Viewed Name"; -static NSString *const KAHUNA_LAST_PRODUCT_VIEWED_ID = @"Last Produced Viewed Id"; -static NSString *const KAHUNA_LAST_PRODUCT_ADDED_TO_CART_NAME = @"Last Product Added To Cart Name"; -static NSString *const KAHUNA_LAST_PRODUCT_ADDED_TO_CART_CATEGORY = @"Last Product Added To Cart Category"; -static NSString *const KAHUNA_LAST_PURCHASE_DISCOUNT = @"Last Purchase Discount"; - -static NSString *const KAHUNA_CATEGORY = @"category"; -static NSString *const KAHUNA_NAME = @"name"; -static NSString *const KAHUNA_ID = @"id"; -static NSString *const KAHUNA_DISCOUNT = @"discount"; -static NSString *const KAHUNA_NONE = @"None"; - - @implementation SEGKahunaIntegration @synthesize initialized, valid, name, settings; @@ -53,6 +34,7 @@ - (id)init self.name = @"Kahuna"; self.valid = NO; self.initialized = NO; + self.kahunaClass = [Kahuna class]; _kahunaCredentialsKeys = [NSSet setWithObjects:KAHUNA_CREDENTIAL_USERNAME, KAHUNA_CREDENTIAL_EMAIL, @@ -73,10 +55,14 @@ - (void)start // We just need one call to launchWithKey and not multiple. The "start" method is called // everytime the app comes to foreground. if ([SEGKahunaPushMonitor sharedInstance].kahunaInitialized == NO) { - [KahunaAnalytics launchWithKey:apiKey]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wundeclared-selector" + [self.kahunaClass performSelector:@selector(setSDKWrapper:withVersion:) withObject:SEGMENT withObject:[SEGAnalytics version]]; +#pragma GCC diagnostic pop + [self.kahunaClass launchWithKey:apiKey]; // If we have recorded any push user info, then if ([SEGKahunaPushMonitor sharedInstance].pushInfo != nil) { - [KahunaAnalytics handleNotification:[SEGKahunaPushMonitor sharedInstance].pushInfo withApplicationState:[SEGKahunaPushMonitor sharedInstance].applicationState]; + [self.kahunaClass handleNotification:[SEGKahunaPushMonitor sharedInstance].pushInfo withApplicationState:[SEGKahunaPushMonitor sharedInstance].applicationState]; [SEGKahunaPushMonitor sharedInstance].pushInfo = nil; } @@ -106,27 +92,27 @@ - (void)validate - (void)identify:(NSString *)userId traits:(NSDictionary *)traits options:(NSDictionary *)options { NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init]; + KahunaUserCredentials *credentials = [self.kahunaClass createUserCredentials]; if (KAHUNA_NOT_STRING_NULL_EMPTY(userId)) { - [KahunaAnalytics setUserCredentialsWithKey:KAHUNA_CREDENTIAL_USER_ID andValue:userId]; + [credentials addCredential:KAHUNA_CREDENTIAL_USER_ID withValue:userId]; } // We will go through each of the above keys, and try to see if the traits has that key. If it does, then we will add the key:value as a credential. // All other traits is being tracked as an attribute. for (NSString *eachKey in traits) { if (!KAHUNA_NOT_STRING_NULL_EMPTY(eachKey)) continue; - NSString *eachValue = [traits objectForKey:eachKey]; if (KAHUNA_NOT_STRING_NULL_EMPTY(eachValue)) { // Check if this is a Kahuna credential key. if ([_kahunaCredentialsKeys containsObject:eachKey]) { - [KahunaAnalytics setUserCredentialsWithKey:eachKey andValue:eachValue]; + [credentials addCredential:eachKey withValue:eachValue]; } else { [attributes setValue:eachValue forKey:eachKey]; } } else if ([eachValue isKindOfClass:[NSNumber class]]) { // Check if this is a Kahuna credential key. if ([_kahunaCredentialsKeys containsObject:eachKey]) { - [KahunaAnalytics setUserCredentialsWithKey:eachKey andValue:[NSString stringWithFormat:@"%@", eachValue]]; + [credentials addCredential:eachKey withValue:[NSString stringWithFormat:@"%@", eachValue]]; } else { [attributes setValue:[NSString stringWithFormat:@"%@", eachValue] forKey:eachKey]; } @@ -139,10 +125,16 @@ - (void)identify:(NSString *)userId traits:(NSDictionary *)traits options:(NSDic } } } - + + NSError *error = nil; + [self.kahunaClass loginWithCredentials:credentials error:&error]; + if (error) { + NSLog(@"Kahuna-Segment Login Error : %@", error.description); + } + // Track the attributes if we have any items in it. if (attributes.count > 0) { - [KahunaAnalytics setUserAttributes:attributes]; + [self.kahunaClass setUserAttributes:attributes]; } } @@ -164,14 +156,15 @@ - (void)track:(NSString *)event properties:(NSDictionary *)properties options:(N } } - // Get the count and value from quantity and revenue. - long value = (long)([revenue doubleValue] * 100); - long count = [quantity longValue]; - - if (count + value > 0) { - [KahunaAnalytics trackEvent:event withCount:count andValue:value]; + // If we get revenue and quantity in the properties, then no matter what we will try to extract the numbers they hold and trackEvent with Count and Value. + if (revenue && quantity) { + // Get the count and value from quantity and revenue. + long value = (long)([revenue doubleValue] * 100); + long count = [quantity longValue]; + + [self.kahunaClass trackEvent:event withCount:count andValue:value]; } else { - [KahunaAnalytics trackEvent:event]; + [self.kahunaClass trackEvent:event]; } NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init]; @@ -195,7 +188,7 @@ - (void)track:(NSString *)event properties:(NSDictionary *)properties options:(N // If we have collected any attributes, then we will call the setUserAttributes API if (attributes.count > 0) { - [KahunaAnalytics setUserAttributes:attributes]; + [self.kahunaClass setUserAttributes:attributes]; } } @@ -204,7 +197,7 @@ - (void)addViewedProductCategoryElements:(NSMutableDictionary *__autoreleasing * id value = properties[KAHUNA_CATEGORY]; if (value && ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]])) { [(*attributes)setValue:value forKey:KAHUNA_LAST_VIEWED_CATEGORY]; - NSDictionary *existingAttributes = [KahunaAnalytics getUserAttributes]; + NSDictionary *existingAttributes = [self.kahunaClass getUserAttributes]; id categoriesViewed = [existingAttributes valueForKey:KAHUNA_CATEGORIES_VIEWED]; if (categoriesViewed && [categoriesViewed isKindOfClass:[NSString class]]) { NSMutableArray *aryOfCategoriesViewed = [[categoriesViewed componentsSeparatedByString:@","] mutableCopy]; @@ -272,18 +265,18 @@ - (void)screen:(NSString *)screenTitle properties:(NSDictionary *)properties opt - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken options:(NSDictionary *)options { - [KahunaAnalytics setDeviceToken:deviceToken]; + [self.kahunaClass setDeviceToken:deviceToken]; } - (void)reset { - [KahunaAnalytics logout]; + [self.kahunaClass logout]; } @end // This class is responsible for getting the 'UIApplicationDidFinishLaunchingNotification' notification. It is received by the -// method didFinishLaunching and it calls [KahunaAnalytics handleNotification API. +// method didFinishLaunching and it calls [self.kahunaClass handleNotification API. @implementation SEGKahunaPushMonitor + (instancetype)sharedInstance @@ -291,11 +284,21 @@ + (instancetype)sharedInstance static SEGKahunaPushMonitor *instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - instance = [[SEGKahunaPushMonitor alloc] init]; + instance = [[SEGKahunaPushMonitor alloc] init]; }); return instance; } +- (id) init { + self = [super init]; + if (self) { + self.kahunaClass = [Kahuna class]; + return self; + } + + return nil; +} + - (void)didFinishLaunching:(NSNotification *)notificationPayload { NSDictionary *userInfo = notificationPayload.userInfo; @@ -397,7 +400,7 @@ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotif { @try { if ([SEGKahunaPushMonitor sharedInstance].kahunaInitialized) { - [KahunaAnalytics handleNotificationRegistrationFailure:error]; + [self.kahunaClass handleNotificationRegistrationFailure:error]; } if (selOriginalApplicationDidFailToRegisterForRemoteNotificationsWithError) { @@ -471,7 +474,7 @@ - (void)pushReceived:(NSDictionary *)userInfo { // When we get this notification, check if kahuna is initialized. If not store it for future use. if ([SEGKahunaPushMonitor sharedInstance].kahunaInitialized) { - [KahunaAnalytics handleNotification:userInfo withApplicationState:[UIApplication sharedApplication].applicationState]; + [self.kahunaClass handleNotification:userInfo withApplicationState:[UIApplication sharedApplication].applicationState]; } else { [SEGKahunaPushMonitor sharedInstance].pushInfo = userInfo; [SEGKahunaPushMonitor sharedInstance].applicationState = [UIApplication sharedApplication].applicationState; diff --git a/Analytics/Integrations/SEGKahunaDefines.h b/Analytics/Integrations/SEGKahunaDefines.h new file mode 100644 index 000000000..b3654bb08 --- /dev/null +++ b/Analytics/Integrations/SEGKahunaDefines.h @@ -0,0 +1,28 @@ +// SEGKahunaDefines.h +// Copyright (c) 2014 Segment.io. All rights reserved. + +#ifndef Analytics_SEGKahunaDefines_h +#define Analytics_SEGKahunaDefines_h + +static NSString *const KAHUNA_VIEWED_PRODUCT_CATEGORY = @"Viewed Product Category"; +static NSString *const KAHUNA_VIEWED_PRODUCT = @"Viewed Product"; +static NSString *const KAHUNA_ADDED_PRODUCT = @"Added Product"; +static NSString *const KAHUNA_COMPLETED_ORDER = @"Completed Order"; + +static NSString *const KAHUNA_LAST_VIEWED_CATEGORY = @"Last Viewed Category"; +static NSString *const KAHUNA_CATEGORIES_VIEWED = @"Categories Viewed"; +static NSString *const KAHUNA_LAST_PRODUCT_VIEWED_NAME = @"Last Product Viewed Name"; +static NSString *const KAHUNA_LAST_PRODUCT_VIEWED_ID = @"Last Produced Viewed Id"; +static NSString *const KAHUNA_LAST_PRODUCT_ADDED_TO_CART_NAME = @"Last Product Added To Cart Name"; +static NSString *const KAHUNA_LAST_PRODUCT_ADDED_TO_CART_CATEGORY = @"Last Product Added To Cart Category"; +static NSString *const KAHUNA_LAST_PURCHASE_DISCOUNT = @"Last Purchase Discount"; + +static NSString *const KAHUNA_CATEGORY = @"category"; +static NSString *const KAHUNA_NAME = @"name"; +static NSString *const KAHUNA_ID = @"id"; +static NSString *const KAHUNA_DISCOUNT = @"discount"; +static NSString *const KAHUNA_NONE = @"None"; +static NSString *const SEGMENT = @"segment"; + + +#endif diff --git a/Podfile.lock b/Podfile.lock index 0fe7e73ea..eb170cad5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -13,7 +13,7 @@ PODS: - FlurrySDK/FlurrySDK (6.5.0) - GoogleAnalytics (3.12.0) - GoogleIDFASupport (3.12.0) - - KahunaSDK (1.0.570) + - Kahuna (2.0.3) - Localytics (3.3.0) - Mixpanel (2.8.2): - Mixpanel/Mixpanel (= 2.8.2) @@ -43,7 +43,7 @@ DEPENDENCIES: - FlurrySDK (= 6.5.0) - GoogleAnalytics (= 3.12.0) - GoogleIDFASupport (= 3.12.0) - - KahunaSDK (= 1.0.570) + - Kahuna (= 2.0.3) - Localytics (= 3.3.0) - Mixpanel (= 2.8.2) - MoEngage-iOS-SDK (= 1.4.3) @@ -67,7 +67,7 @@ SPEC CHECKSUMS: FlurrySDK: 7e749c1bd22f4fb0a863663e38c58bc841980b25 GoogleAnalytics: ef2e0b800ef3c1e62688cdfc959e0d3132bc246c GoogleIDFASupport: 0dd25ffdd152fd8e3deefd72978b42ef818ef0fa - KahunaSDK: ea15fdf9757c4d9bf639bf190888e20b688a30ed + Kahuna: 06c3dea6aee22f5ae9c3eb6a6b9ae703cd5dd5cb Localytics: 64ada284cde3f2b30fb04272d5d735d49657d0ab Mixpanel: f4d71ac2a306e7cda6ad03d4a2be249a0fd7a967 MoEngage-iOS-SDK: 81be7a433349a7d535c905a08629359eeefe0952 diff --git a/iOS Tests/Integrations/Kahuna/SEGKahunaIntegrationTests.m b/iOS Tests/Integrations/Kahuna/SEGKahunaIntegrationTests.m new file mode 100644 index 000000000..a18581ee2 --- /dev/null +++ b/iOS Tests/Integrations/Kahuna/SEGKahunaIntegrationTests.m @@ -0,0 +1,205 @@ +// +// SEGKahunaIntegrationTests.m +// Analytics +// +// Copyright (c) 2015 Segment.io. All rights reserved. +// + +#import +#import "SEGKahunaDefines.h" +#import "SEGKahunaIntegration.h" +#import + +#define HC_SHORTHAND +#import + +#define MOCKITO_SHORTHAND +#import + + +@interface SEGKahunaIntegrationTests : XCTestCase + +@property SEGKahunaIntegration *integration; +@property Class kahunaClassMock; +@property Class nserrorClassMock; +@property NSError *nserrorMock; +@property Kahuna *kahunaMock; +@property KahunaUserCredentials *kahunaCredentialsMock; + +@end + + +@implementation SEGKahunaIntegrationTests + +- (void)setUp +{ + [super setUp]; + + _kahunaMock = mock([Kahuna class]); + _kahunaClassMock = mockClass([Kahuna class]); + _nserrorMock = mock([NSError class]); + _nserrorClassMock = mockClass([NSError class]); + _kahunaCredentialsMock = mock([KahunaUserCredentials class]); + + [given([_kahunaClassMock sharedInstance]) willReturn:_kahunaMock]; + [given([_kahunaClassMock createUserCredentials]) willReturn:_kahunaCredentialsMock]; + [given([_nserrorClassMock errorWithDomain:anything() code:anything() userInfo:anything()]) willReturn:_nserrorMock]; + + _integration = [[SEGKahunaIntegration alloc] init]; + [_integration setKahunaClass:_kahunaClassMock]; +} + +- (void)testStart +{ + [_integration updateSettings:@{ @"apiKey" : @"foo" }]; + + XCTAssertTrue(_integration.valid); + [verifyCount(_kahunaClassMock, times(1)) launchWithKey:@"foo"]; +} + +- (void)testReset +{ + [_integration reset]; + + [verifyCount(_kahunaClassMock, times(1)) logout]; +} + +- (void)testIdentify +{ + [_integration identify:@"foo" traits:@{ @"bar" : @"baz" } options:@{}]; + + // Verify that Add Credential was called once on the KahunaCredentialsMock object. + [verifyCount(_kahunaCredentialsMock, times(1)) addCredential:KAHUNA_CREDENTIAL_USER_ID withValue:@"foo"]; + + [[verifyCount(_kahunaClassMock, times(1)) withMatcher:anything() forArgument:1] loginWithCredentials:_kahunaCredentialsMock error:nil]; + [verifyCount(_kahunaClassMock, times(1)) setUserAttributes:@{ @"bar" : @"baz" }]; +} + +- (void)testIdentifyWithNoTraits +{ + [_integration identify:@"foo" traits:@{} options:@{}]; + + // Verify that Add Credential was called once on the KahunaCredentialsMock object. + [verifyCount(_kahunaCredentialsMock, times(1)) addCredential:KAHUNA_CREDENTIAL_USER_ID withValue:@"foo"]; + + [[verifyCount(_kahunaClassMock, times(1)) withMatcher:anything() forArgument:1] loginWithCredentials:_kahunaCredentialsMock error:nil]; + [verifyCount(_kahunaClassMock, never()) setUserAttributes:anything()]; +} + +- (void)testIdentifyWithNoCredentialsAndNoTraits +{ + [_integration identify:nil traits:@{} options:@{}]; + + // Verify that Add Credential was called once on the KahunaCredentialsMock object. + [verifyCount(_kahunaCredentialsMock, never()) addCredential:anything() withValue:anything()]; + + [[verifyCount(_kahunaClassMock, times(1)) withMatcher:anything() forArgument:1] loginWithCredentials:_kahunaCredentialsMock error:nil]; + [verifyCount(_kahunaClassMock, never()) setUserAttributes:anything()]; +} + +- (void)testIdentifyWithMultipleCredentialsAndTraits +{ + [_integration identify:@"foo" traits:@{ @"bar" : @"baz", KAHUNA_CREDENTIAL_EMAIL : @"segkah@gmail.com", @"moon" : @"drake" } options:@{}]; + + // Verify that Add Credential was called twice on the KahunaCredentialsMock object. + [verifyCount(_kahunaCredentialsMock, times(1)) addCredential:KAHUNA_CREDENTIAL_USER_ID withValue:@"foo"]; + [verifyCount(_kahunaCredentialsMock, times(1)) addCredential:KAHUNA_CREDENTIAL_EMAIL withValue:@"segkah@gmail.com"]; + + [[verifyCount(_kahunaClassMock, times(1)) withMatcher:anything() forArgument:1] loginWithCredentials:_kahunaCredentialsMock error:nil]; + [verifyCount(_kahunaClassMock, times(1)) setUserAttributes:@{ @"bar" : @"baz", @"moon" : @"drake" }]; +} + +- (void)testTrack +{ + [_integration track:@"foo" properties:@{} options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) trackEvent:@"foo"]; +} + +- (void)testTrackWithRevenueButNoQuantity +{ + [_integration track:@"foo" properties:@{ @"revenue" : @10 } options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) trackEvent:@"foo"]; + [verifyCount(_kahunaClassMock, never()) trackEvent:@"foo" withCount:anything() andValue:anything()]; +} + +- (void)testTrackWithQuantityButNoRevenue +{ + [_integration track:@"foo" properties:@{ @"quantity" : @10 } options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) trackEvent:@"foo"]; + [verifyCount(_kahunaClassMock, never()) trackEvent:@"foo" withCount:anything() andValue:anything()]; +} + +- (void)testTrackWithQuantityAndRevenue +{ + [_integration track:@"foo" properties:@{ @"revenue" : @10, @"quantity" : @4 } options:nil]; + + [verifyCount(_kahunaClassMock, never()) trackEvent:anything()]; + [verifyCount(_kahunaClassMock, times(1)) trackEvent:@"foo" withCount:4 andValue:1000]; +} + +- (void)testTrackWithQuantityRevenueAndProperties +{ + [_integration track:@"foo" + properties:@{@"productId" : @"bar", + @"quantity" : @10, + @"receipt" : @"baz", + @"revenue" : @5 + } options:@{}]; + + [verifyCount(_kahunaClassMock, times(1)) trackEvent:@"foo" withCount:10 andValue:500]; +} + +- (void)testTrackWithPropertyViewedCategory +{ + [_integration track:KAHUNA_VIEWED_PRODUCT_CATEGORY properties:@{ KAHUNA_CATEGORY : @"shirts" } options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) setUserAttributes:@{KAHUNA_LAST_VIEWED_CATEGORY : @"shirts", KAHUNA_CATEGORIES_VIEWED : @"shirts" }]; + [verifyCount(_kahunaClassMock, times(1)) trackEvent:KAHUNA_VIEWED_PRODUCT_CATEGORY]; +} + +- (void)testTrackWithPropertyViewedProduct +{ + [_integration track:KAHUNA_VIEWED_PRODUCT properties:@{ KAHUNA_NAME : @"gopher shirts" } options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) setUserAttributes:@{KAHUNA_LAST_PRODUCT_VIEWED_NAME : @"gopher shirts", + KAHUNA_CATEGORIES_VIEWED : KAHUNA_NONE, + KAHUNA_LAST_VIEWED_CATEGORY : KAHUNA_NONE }]; + [verifyCount(_kahunaClassMock, times(1)) trackEvent:KAHUNA_VIEWED_PRODUCT]; + +} + +- (void)testTrackWithPropertyAddedProduct +{ + [_integration track:KAHUNA_ADDED_PRODUCT properties:@{ KAHUNA_NAME : @"gopher shirts" } options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) setUserAttributes:@{KAHUNA_LAST_PRODUCT_ADDED_TO_CART_NAME : @"gopher shirts", + KAHUNA_LAST_PRODUCT_ADDED_TO_CART_CATEGORY : KAHUNA_NONE }]; + [verifyCount(_kahunaClassMock, times(1)) trackEvent:KAHUNA_ADDED_PRODUCT]; +} + +- (void)testTrackWithPropertyCompletedOrder +{ + [_integration track:KAHUNA_COMPLETED_ORDER properties:@{ KAHUNA_DISCOUNT : @15.0 } options:nil]; + + [verifyCount(_kahunaClassMock, times(1)) setUserAttributes:@{KAHUNA_LAST_PURCHASE_DISCOUNT : @15.0 }]; + [verifyCount(_kahunaClassMock, times(1)) trackEvent:KAHUNA_COMPLETED_ORDER]; +} + +- (void)testScreen +{ + [_integration setSettings:@{ @"trackAllPages" : @1 }]; + + [_integration screen:@"foo" properties:@{} options:@{}]; + [verifyCount(_kahunaClassMock, times(1)) trackEvent:@"Viewed foo Screen"]; +} + +- (void)testScreenWithNoTrackAllPagesSettings +{ + [_integration screen:@"foo" properties:@{} options:@{}]; + [verifyCount(_kahunaClassMock, never()) trackEvent:anything()]; +} + +@end diff --git a/scripts/integrations.json b/scripts/integrations.json index 0b74691db..4e8d58cfb 100644 --- a/scripts/integrations.json +++ b/scripts/integrations.json @@ -62,8 +62,8 @@ { "name": "Kahuna", "dependencies": [{ - "name": "KahunaSDK", - "version": "1.0.570" + "name": "Kahuna", + "version": "2.0.3" }] }, {