diff --git a/Example/Tests/Tests.m b/Example/Tests/Tests.m index fc64f9e..e22d5b4 100644 --- a/Example/Tests/Tests.m +++ b/Example/Tests/Tests.m @@ -10,21 +10,491 @@ SpecBegin(InitialSpecs) -describe(@"these will pass", ^{ +describe(@"Firebase Integration", ^{ + __block id mockFirebase; + __block SEGFirebaseIntegration *integration; - it(@"can do maths", ^{ - expect(1).beLessThan(23); + beforeEach(^{ + mockFirebase = mockClass([FIRAnalytics class]); + integration = [[SEGFirebaseIntegration alloc] initWithSettings:@{} andFirebase:mockFirebase]; }); - it(@"can read", ^{ - expect(@"team").toNot.contain(@"I"); + it(@"identify with no traits", ^{ + SEGIdentifyPayload *payload = [[SEGIdentifyPayload alloc] initWithUserId:@"1111" anonymousId:nil traits:@{} context:@{} integrations:@{}]; + + [integration identify:payload]; + [verify(mockFirebase) setUserID:@"1111"]; + }); - it(@"will wait and succeed", ^{ - waitUntil(^(DoneCallback done) { - done(); - }); + it(@"identify with traits", ^{ + SEGIdentifyPayload *payload = [[SEGIdentifyPayload alloc] initWithUserId:@"7891" anonymousId:nil traits:@{ + @"name":@"Jerry Seinfield", + @"gender":@"male", + @"emotion":@"confused", + @"age" :@47 + } context:@{} integrations:@{}]; + [integration identify:payload]; + [verify(mockFirebase) setUserID:@"7891"]; + [verify(mockFirebase) setUserPropertyString:@"Jerry_Seinfield" forName: @"name"]; + [verify(mockFirebase) setUserPropertyString:@"male" forName: @"gender"]; + [verify(mockFirebase) setUserPropertyString:@"confused" forName: @"emotion"]; + [verify(mockFirebase) setUserPropertyString:@"47" forName: @"age"]; + + }); + + it(@"track with no props", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Email Sent" properties:@{} context:@{} integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"Email_Sent" parameters:@{}]; + }); + + it(@"track with props", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Starship Ordered" + properties:@{ + @"Starship Type": @"Death Star" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"Starship_Ordered" parameters:@{ + @"Starship_Type": @"Death Star"}]; + }); + + it(@"track Order Completed", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Order Completed" properties:@{ + @"checkout_id":@"9bcf000000000000", + @"order_id":@"50314b8e", + @"affiliation":@"App Store", + @"total":@30.45, + @"shipping":@5.05, + @"tax":@1.20, + @"currency":@"USD", + @"category":@"Games", + @"revenue": @8, + @"products":@{ + @"product_id":@"2013294", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbros", + @"price":@"21.99", + @"quantity":@"1" + } + } + context:@{} integrations:@{}]; + + [integration track:payload]; + // TODO: look into how to handle mapping Firebase reserved params to each products + [verify(mockFirebase) logEventWithName:@"ecommerce_purchase" parameters:@{ + @"checkout_id":@"9bcf000000000000", + @"transaction_id":@"50314b8e", + @"affiliation":@"App Store", + @"value":@30.45, + @"shipping":@5.05, + @"tax":@1.20, + @"currency":@"USD", + @"item_category":@"Games", + @"products":@{ + @"product_id":@"2013294", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbros", + @"price":@"21.99", + @"quantity":@"1" + } + }]; + }); + + it(@"track Product Clicked", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product Clicked" + properties:@{ + @"product_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"select_content" parameters:@{ + @"item_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"item_category":@"Games", + @"item_name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg"}]; + }); + + it(@"track Product Viewed", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product Viewed" + properties:@{ + @"product_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"category":@"Games", + @"name":@"Monopoly 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"currency":@"usd", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"view_item" parameters:@{ + @"item_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"item_category":@"Games", + @"item_name":@"Monopoly 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"currency":@"usd", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg"}]; + }); + + it(@"track Product Added", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product Added" + properties:@{ + @"cart_id":@"skdjsidjsdkdj29j", + @"product_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"add_to_cart" parameters:@{ + @"cart_id":@"skdjsidjsdkdj29j", + @"item_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"item_category":@"Games", + @"item_name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg"}]; + }); + + it(@"track Product Removed", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product Removed" + properties:@{ + @"cart_id":@"ksjdj92dj29dj92d2j", + @"product_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"remove_from_cart" parameters:@{ + @"cart_id":@"ksjdj92dj29dj92d2j", + @"item_id": @"507f1f77bcf86cd799439011", + @"sku":@"G-32", + @"item_category":@"Games", + @"item_name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbro", + @"variant":@"200 pieces", + @"price":@18.99, + @"quantity":@1, + @"coupon":@"MAYDEALS", + @"position":@3, + @"url":@"https://www.company.com/product/path", + @"image_url":@"https://www.company.com/product/path.jpg"}]; + }); + + it(@"track Checkout Started", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Checkout Started" + properties:@{ + @"checkout_id":@"9bcf000000000000", + @"order_id":@"50314b8e", + @"affiliation":@"App Store", + @"total":@30.45, + @"shipping":@5.05, + @"tax":@1.20, + @"currency":@"USD", + @"category":@"Games", + @"revenue": @8, + @"products":@{ + @"product_id":@"2013294", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbros", + @"price":@"21.99", + @"quantity":@"1" + } + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"begin_checkout" parameters:@{ + @"checkout_id":@"9bcf000000000000", + @"transaction_id":@"50314b8e", + @"affiliation":@"App Store", + @"value":@30.45, + @"shipping":@5.05, + @"tax":@1.20, + @"currency":@"USD", + @"item_category":@"Games", + @"products":@{ + @"product_id":@"2013294", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbros", + @"price":@"21.99", + @"quantity":@"1" + } + }]; + }); + + it(@"track Payment Info Entered", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Payment Info Entered" + properties:@{ + @"checkout_id": @"39f39fj39f3jf93fj9fj39fj3f", + @"order_id":@"dkfsjidfjsdifsdfksdjfkdsfjsdfkdsf", + @"step":@"Payment", + @"shipping_method":@"ground", + @"payment_method":@"credit card" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"add_payment_info" parameters:@{ + @"checkout_id": @"39f39fj39f3jf93fj9fj39fj3f", + @"transaction_id":@"dkfsjidfjsdifsdfksdjfkdsfjsdfkdsf", + @"step":@"Payment", + @"shipping_method":@"ground", + @"payment_method":@"credit card"}]; + }); + + it(@"track Order Refunded", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Order Refunded" + properties:@{ + @"order_id": @"50314b8e9bcf000000000000" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"purchase_refund" parameters:@{ + @"transaction_id": @"50314b8e9bcf000000000000"}]; + }); + + + it(@"track Product List Viewed", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product List Viewed" + properties:@{ + @"list_id": @"hot_deals_1", + @"category":@"Deals", + @"products":@{ + @"product_id":@"2013294", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbros", + @"price":@"21.99", + @"quantity":@"1" + } + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"view_item_list" parameters:@{ + @"list_id": @"hot_deals_1", + @"item_category":@"Deals", + @"products":@{ + @"product_id":@"2013294", + @"category":@"Games", + @"name":@"Monopoly: 3rd Edition", + @"brand":@"Hasbros", + @"price":@"21.99", + @"quantity":@"1" + } + }]; + }); + + + + it(@"track Product Added to Wishlist", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product Added to Wishlist" + properties:@{ + @"wishlist_id": @"skdjsidjsdkdj29j", + @"wishlist_name": @"Loved Games", + @"product_id": @"507f1f77bcf86cd799439011", + @"sku": @"G-32", + @"category": @"Games", + @"name": @"Monopoly: 3rd Edition", + @"brand": @"Hasbro", + @"variant": @"200 pieces", + @"price": @18.99, + @"quantity": @1, + @"coupon": @"MAYDEALS", + @"position": @3, + @"url": @"https://www.company.com/product/path", + @"image_url": @"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"add_to_wishlist" parameters:@{ + @"wishlist_id": @"skdjsidjsdkdj29j", + @"wishlist_name": @"Loved Games", + @"item_id": @"507f1f77bcf86cd799439011", + @"sku": @"G-32", + @"item_category": @"Games", + @"item_name": @"Monopoly: 3rd Edition", + @"brand": @"Hasbro", + @"variant": @"200 pieces", + @"price": @18.99, + @"quantity": @1, + @"coupon": @"MAYDEALS", + @"position": @3, + @"url": @"https://www.company.com/product/path", + @"image_url": @"https://www.company.com/product/path.jpg" + }]; + }); + + + it(@"track Product Shared", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Product Shared" + properties:@{ + @"share_via": @"email", + @"share_message": @"Hey, check out this item", + @"recipient": @"friend@gmail.com", + @"product_id": @"507f1f77bcf86cd799439011", + @"sku": @"G-32", + @"category": @"Games", + @"name": @"Monopoly: 3rd Edition", + @"brand": @"Hasbro", + @"variant": @"200 pieces", + @"price": @18.99, + @"url": @"https://www.company.com/product/path", + @"image_url": @"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"share" parameters:@{ + @"share_via": @"email", + @"share_message": @"Hey, check out this item", + @"recipient": @"friend@gmail.com", + @"item_id": @"507f1f77bcf86cd799439011", + @"sku": @"G-32", + @"item_category": @"Games", + @"item_name": @"Monopoly: 3rd Edition", + @"brand": @"Hasbro", + @"variant": @"200 pieces", + @"price": @18.99, + @"url": @"https://www.company.com/product/path", + @"image_url": @"https://www.company.com/product/path.jpg" + }]; + }); + + it(@"track Cart Shared", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Cart Shared" + properties:@{ + @"share_via": @"email", + @"share_message": @"Hey, check out this item", + @"recipient": @"friend@gmail.com", + @"product_id": @"507f1f77bcf86cd799439011", + @"sku": @"G-32", + @"category": @"Games", + @"name": @"Monopoly: 3rd Edition", + @"brand": @"Hasbro", + @"variant": @"200 pieces", + @"price": @18.99, + @"url": @"https://www.company.com/product/path", + @"image_url": @"https://www.company.com/product/path.jpg" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"share" parameters:@{ + @"share_via": @"email", + @"share_message": @"Hey, check out this item", + @"recipient": @"friend@gmail.com", + @"item_id": @"507f1f77bcf86cd799439011", + @"sku": @"G-32", + @"item_category": @"Games", + @"item_name": @"Monopoly: 3rd Edition", + @"brand": @"Hasbro", + @"variant": @"200 pieces", + @"price": @18.99, + @"url": @"https://www.company.com/product/path", + @"image_url": @"https://www.company.com/product/path.jpg" + }]; + }); + + it(@"track Products Searched", ^{ + SEGTrackPayload *payload = [[SEGTrackPayload alloc] initWithEvent:@"Products Searched" + properties:@{ + @"query": @"blue hotpants" + } + context:@{} + integrations:@{}]; + + [integration track:payload]; + [verify(mockFirebase) logEventWithName:@"search" parameters:@{ + @"search_term": @"blue hotpants" + }]; + }); + }); SpecEnd diff --git a/Segment-Firebase/Classes/SEGFirebaseIntegration.h b/Segment-Firebase/Classes/SEGFirebaseIntegration.h index a7b4724..a0a370b 100644 --- a/Segment-Firebase/Classes/SEGFirebaseIntegration.h +++ b/Segment-Firebase/Classes/SEGFirebaseIntegration.h @@ -6,11 +6,13 @@ #import #import - @interface SEGFirebaseIntegration : NSObject @property (nonatomic, strong) NSDictionary *settings; +@property (nonatomic, strong) Class firebaseClass; - (id)initWithSettings:(NSDictionary *)settings; +- (id)initWithSettings:(NSDictionary *)settings andFirebase:(id)firebaseClass; + @end diff --git a/Segment-Firebase/Classes/SEGFirebaseIntegration.m b/Segment-Firebase/Classes/SEGFirebaseIntegration.m index a87ac1c..ccbf3f7 100644 --- a/Segment-Firebase/Classes/SEGFirebaseIntegration.m +++ b/Segment-Firebase/Classes/SEGFirebaseIntegration.m @@ -2,10 +2,159 @@ #import #import - @implementation SEGFirebaseIntegration -#pragma mark - Helper Functions +#pragma mark - Initialization + +- (id)initWithSettings:(NSDictionary *)settings +{ + if (self = [super init]) { + self.settings = settings; + self.firebaseClass = [FIRAnalytics class]; + NSString *deepLinkURLScheme = [self.settings objectForKey:@"deepLinkURLScheme"]; + if (deepLinkURLScheme) { + [FIROptions defaultOptions].deepLinkURLScheme = deepLinkURLScheme; + SEGLog(@"[FIROptions defaultOptions].deepLinkURLScheme = %@;", deepLinkURLScheme); + } + + [FIRApp configure]; + SEGLog(@"[FIRApp Configure]"); + } + return self; +} + +- (id)initWithSettings:(NSDictionary *)settings andFirebase:(id)firebaseClass; +{ + if (self = [super init]) { + self.settings = settings; + self.firebaseClass = firebaseClass; + } + return self; +} + +- (void)identify:(SEGIdentifyPayload *)payload +{ + if (payload.userId) { + [self.firebaseClass setUserID:payload.userId]; + SEGLog(@"[FIRAnalytics setUserId:%@]", payload.userId); + } + // Firebase requires user properties to be an NSString + NSDictionary *mappedTraits = [SEGFirebaseIntegration mapToStrings:payload.traits]; + [mappedTraits enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop){ + NSString *trait = [key stringByReplacingOccurrencesOfString:@" " withString:@"_"]; + NSString *value = [obj stringByReplacingOccurrencesOfString:@" " withString:@"_"]; + [self.firebaseClass setUserPropertyString:value forName:trait]; + SEGLog(@"[FIRAnalytics setUserPropertyString:%@ forName:%@]", value, trait); + }]; +} + +- (void)track:(SEGTrackPayload *)payload + { + NSString *name = [self formatFirebaseEventNames:payload.event]; + NSDictionary *parameters = [self returnMappedFirebaseParameters:payload.properties]; + + [self.firebaseClass logEventWithName:name parameters:parameters]; + SEGLog(@"[FIRAnalytics logEventWithName:%@ parameters:%@]", name, parameters); + + } + + +# pragma mark - Utilities + +// Event names can be up to 32 characters long, may only contain alphanumeric +// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_" +// prefix is reserved and should not be used. + +// Maps Segment Spec to Firebase Constants +// https://firebase.google.com/docs/reference/ios/firebaseanalytics/api/reference/Constants#/c:FIRParameterNames.h@kFIRParameterCampaign + +- (NSString *)formatFirebaseEventNames:(NSString *)event +{ + NSDictionary *mapper = [NSDictionary dictionaryWithObjectsAndKeys: + kFIREventSelectContent, @"Product Clicked", + kFIREventViewItem, @"Product Viewed", + kFIREventAddToCart, @"Product Added", + kFIREventRemoveFromCart, @"Product Removed", + kFIREventBeginCheckout, @"Checkout Started", + kFIREventAddPaymentInfo, @"Payment Info Entered", + kFIREventEcommercePurchase, @"Order Completed", + kFIREventPurchaseRefund, @"Order Refunded", + kFIREventViewItemList, @"Product List Viewed", + kFIREventAddToWishlist, @"Product Added to Wishlist", + kFIREventShare, @"Product Shared", + kFIREventShare, @"Cart Shared", + kFIREventSearch, @"Products Searched", nil + ]; + + NSString *mappedEvent = [mapper objectForKey:event]; + + if (mappedEvent) { + return mappedEvent; + } else { + return [event stringByReplacingOccurrencesOfString:@" " withString:@"_"]; + } +} + +/// Params supply information that contextualize Events. You can associate up to 25 unique Params +/// with each Event type. Some Params are suggested below for certain common Events, but you are +/// not limited to these. You may supply extra Params for suggested Events or custom Params for +/// Custom events. Param names can be up to 24 characters long, may only contain alphanumeric +/// characters and underscores ("_"), and must start with an alphabetic character. Param values can +/// be up to 36 characters long. The "firebase_" prefix is reserved and should not be used. + +- (NSDictionary *)returnMappedFirebaseParameters:(NSDictionary *)properties +{ + NSDictionary *map = [NSDictionary dictionaryWithObjectsAndKeys: + kFIRParameterItemCategory, @"category", + kFIRParameterItemID, @"product_id", + kFIRParameterItemName, @"name", + kFIRParameterPrice, @"price", + kFIRParameterQuantity, @"quantity", + kFIRParameterSearchTerm, @"query", + kFIRParameterShipping, @"shipping", + kFIRParameterTax, @"tax", + kFIRParameterValue, @"total", + kFIRParameterValue, @"revenue", + kFIRParameterTransactionID, @"order_id", + kFIRParameterCurrency, @"currency", nil]; + + + return [SEGFirebaseIntegration mapToFirebaseParameters:properties withMap:map]; +} + ++ (NSDictionary *)mapToFirebaseParameters:(NSDictionary *)properties withMap:(NSDictionary *)mapper +{ + NSMutableDictionary *mappedParams = [NSMutableDictionary dictionaryWithDictionary:properties]; + [mapper enumerateKeysAndObjectsUsingBlock:^(NSString *original, NSString *new, BOOL *stop) { + id data = [properties objectForKey:original]; + if (data) { + [mappedParams removeObjectForKey:original]; + [mappedParams setObject:data forKey:new]; + } + }]; + + return [formatEventProperties(mappedParams) copy]; +} + +NSDictionary *formatEventProperties(NSDictionary *dictionary) +{ + NSMutableDictionary *output = [NSMutableDictionary dictionaryWithCapacity:dictionary.count]; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id data, BOOL *stop) { + [output removeObjectForKey:key]; + key = [key stringByReplacingOccurrencesOfString:@" " withString:@"_"]; + if ([data isKindOfClass:[NSNumber class]]){ + data = [NSNumber numberWithDouble:[data doubleValue]]; + [output setObject:data forKey:key]; + } else { + [output setObject:data forKey:key]; + } + }]; + + return [output copy]; + +} + +// Firebase requires all User traits to be Strings + (NSDictionary *)mapToStrings:(NSDictionary *)dictionary { NSMutableDictionary *output = [NSMutableDictionary dictionaryWithCapacity:dictionary.count]; @@ -22,147 +171,4 @@ } -#pragma mark - Initialization - -- (id)initWithSettings:(NSDictionary *)settings -{ - if (self = [super init]) { - self.settings = settings; - NSString *deepLinkURLScheme = [self.settings objectForKey:@"deepLinkURLScheme"]; - if (deepLinkURLScheme) { - [FIROptions defaultOptions].deepLinkURLScheme = deepLinkURLScheme; - SEGLog(@"[FIROptions defaultOptions].deepLinkURLScheme = %@;", deepLinkURLScheme); - } - - [FIRApp configure]; - SEGLog(@"[FIRApp Configure]"); - } - return self; -} - -- (void)identify:(SEGIdentifyPayload *)payload -{ - if (payload.userId) { - [FIRAnalytics setUserID:payload.userId]; - SEGLog(@"[FIRAnalytics setUserId:%@]", payload.userId); - } - // Firebase requires user properties to be a NSString - NSDictionary *mappedTraits = [SEGFirebaseIntegration mapToStrings:payload.traits]; - [mappedTraits enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop){ - NSString *trait = [key stringByReplacingOccurrencesOfString:@" " withString:@"_"]; - NSString *value = [obj stringByReplacingOccurrencesOfString:@" " withString:@"_"]; - [FIRAnalytics setUserPropertyString:value forName:trait]; - SEGLog(@"[FIRAnalytics setUserPropertyString:%@ forName:%@]", value, trait); - }]; -} - -- (void)track:(SEGTrackPayload *)payload - { - - NSString *name = [self firebaseEventNames:payload.event]; - NSDictionary *parameters = [self firebaseParameters:payload.properties]; - - [FIRAnalytics logEventWithName:name parameters:parameters]; - SEGLog(@"[FIRAnalytics logEventWithName:%@ parameters:%@]", name, parameters); - } - - -# pragma mark - Utilities -//Event names can be up to 32 characters long, may only contain alphanumeric -// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_" -// prefix is reserved and should not be used. - -- (NSString *)firebaseEventNames:(NSString *)event -{ - // Map the event names to special firebase events - NSDictionary *mapper = [NSDictionary dictionaryWithObjectsAndKeys: - @"add_payment_info", @"payment info entered", - @"add_to_cart", @"product added", - @"add_to_wishlist", @"product added to wishlist", - @"app_open", @"application opened", - @"begin_checkout", @"checkout started", - @"present_offer", @"promotion viewed", - @"search", @"products searched", - @"select_content", @"product clicked", - @"view_item", @"product viewed", - @"view_item_list", @"product list viewed", - @"share", @"product shared", - @"ecommerce_purchase", @"order completed", - @"purchase_refund", @"order refunded", nil - ]; - - NSString *mappedEvent = [mapper objectForKey:[event lowercaseString]]; - - if (mappedEvent) { - return mappedEvent; - } else { - return [event stringByReplacingOccurrencesOfString:@" " withString:@"_"]; - } -} - -/// Params supply information that contextualize Events. You can associate up to 25 unique Params -/// with each Event type. Some Params are suggested below for certain common Events, but you are -/// not limited to these. You may supply extra Params for suggested Events or custom Params for -/// Custom events. Param names can be up to 24 characters long, may only contain alphanumeric -/// characters and underscores ("_"), and must start with an alphabetic character. Param values can -/// be up to 36 characters long. The "firebase_" prefix is reserved and should not be used. -- (NSDictionary *)firebaseParameters:(NSDictionary *)properties -{ - // Map to special firebase properties. - NSDictionary *mapper = [NSDictionary dictionaryWithObjectsAndKeys: - @"content_type", @"category", - // flatten location - @"destination", @"location", - @"start_date", @"checkin_date", - @"end_date", @"checkout_date", - @"item_category", @"category", - @"item_id", @"product_id", - @"item_name", @"name", - @"number_of_nights", @"booking_window", - @"number_of_rooms", @"quantity", - @"origin", @"origin", - @"price", @"price", - @"quantity", @"quantity", - @"search_term", @"query", - @"shipping", @"shipping", - @"tax", @"tax", - @"travel_class", @"class", - @"value", @"total", - @"value", @"revenue", - @"transaction_id", @"order_id", nil]; - - return [SEGFirebaseIntegration map:properties withMap:mapper]; -} - -+ (NSDictionary *)map:(NSDictionary *)dictionary withMap:(NSDictionary *)map -{ - NSMutableDictionary *mapped = [NSMutableDictionary dictionaryWithDictionary:dictionary]; - - [map enumerateKeysAndObjectsUsingBlock:^(NSString *original, NSString *new, BOOL *stop) { - id data = [mapped objectForKey:original]; - if (data) { - // Check if the property could be a number, convert if so - if ([data isKindOfClass:[NSString class]]) { - NSNumber *number; - BOOL validNumber; - NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; - [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; - number = [formatter numberFromString:data]; - validNumber = number != nil; - - if (validNumber) { - data = number; - } else { - data = [data stringByReplacingOccurrencesOfString:@" " withString:@"_"]; - } - } - - [mapped setObject:data forKey:new]; - [mapped removeObjectForKey:original]; - } - }]; - - return [mapped copy]; -} - @end