| 1 | 
           efrain | 
           1 | 
           /*
  | 
        
        
            | 
            | 
           2 | 
            * Copyright 2018 Google
  | 
        
        
            | 
            | 
           3 | 
            *
  | 
        
        
            | 
            | 
           4 | 
            * Licensed under the Apache License, Version 2.0 (the "License");
  | 
        
        
            | 
            | 
           5 | 
            * you may not use this file except in compliance with the License.
  | 
        
        
            | 
            | 
           6 | 
            * You may obtain a copy of the License at
  | 
        
        
            | 
            | 
           7 | 
            *
  | 
        
        
            | 
            | 
           8 | 
            *      http://www.apache.org/licenses/LICENSE-2.0
  | 
        
        
            | 
            | 
           9 | 
            *
  | 
        
        
            | 
            | 
           10 | 
            * Unless required by applicable law or agreed to in writing, software
  | 
        
        
            | 
            | 
           11 | 
            * distributed under the License is distributed on an "AS IS" BASIS,
  | 
        
        
            | 
            | 
           12 | 
            * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  | 
        
        
            | 
            | 
           13 | 
            * See the License for the specific language governing permissions and
  | 
        
        
            | 
            | 
           14 | 
            * limitations under the License.
  | 
        
        
            | 
            | 
           15 | 
            */
  | 
        
        
            | 
            | 
           16 | 
              | 
        
        
            | 
            | 
           17 | 
           #import "FirebaseMessaging/Sources/FIRMessagingAnalytics.h"
  | 
        
        
            | 
            | 
           18 | 
              | 
        
        
            | 
            | 
           19 | 
           #import <GoogleUtilities/GULAppDelegateSwizzler.h>
  | 
        
        
            | 
            | 
           20 | 
           #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  | 
        
        
            | 
            | 
           21 | 
           #import "Interop/Analytics/Public/FIRInteropEventNames.h"
  | 
        
        
            | 
            | 
           22 | 
           #import "Interop/Analytics/Public/FIRInteropParameterNames.h"
  | 
        
        
            | 
            | 
           23 | 
              | 
        
        
            | 
            | 
           24 | 
           #import "FirebaseMessaging/Sources/FIRMessagingConstants.h"
  | 
        
        
            | 
            | 
           25 | 
           #import "FirebaseMessaging/Sources/FIRMessagingLogger.h"
  | 
        
        
            | 
            | 
           26 | 
              | 
        
        
            | 
            | 
           27 | 
           static NSString *const kLogTag = @"FIRMessagingAnalytics";
  | 
        
        
            | 
            | 
           28 | 
              | 
        
        
            | 
            | 
           29 | 
           // aps Key
  | 
        
        
            | 
            | 
           30 | 
           static NSString *const kApsKey = @"aps";
  | 
        
        
            | 
            | 
           31 | 
           static NSString *const kApsAlertKey = @"alert";
  | 
        
        
            | 
            | 
           32 | 
           static NSString *const kApsSoundKey = @"sound";
  | 
        
        
            | 
            | 
           33 | 
           static NSString *const kApsBadgeKey = @"badge";
  | 
        
        
            | 
            | 
           34 | 
           static NSString *const kApsContentAvailableKey = @"badge";
  | 
        
        
            | 
            | 
           35 | 
              | 
        
        
            | 
            | 
           36 | 
           // Data Key
  | 
        
        
            | 
            | 
           37 | 
           static NSString *const kDataKey = @"data";
  | 
        
        
            | 
            | 
           38 | 
              | 
        
        
            | 
            | 
           39 | 
           static NSString *const kFIRParameterLabel = @"label";
  | 
        
        
            | 
            | 
           40 | 
              | 
        
        
            | 
            | 
           41 | 
           static NSString *const kReengagementSource = @"Firebase";
  | 
        
        
            | 
            | 
           42 | 
           static NSString *const kReengagementMedium = @"notification";
  | 
        
        
            | 
            | 
           43 | 
              | 
        
        
            | 
            | 
           44 | 
           // Analytics
  | 
        
        
            | 
            | 
           45 | 
           static NSString *const kAnalyticsEnabled = @"google.c.a.e";
  | 
        
        
            | 
            | 
           46 | 
           static NSString *const kAnalyticsMessageTimestamp = @"google.c.a.ts";
  | 
        
        
            | 
            | 
           47 | 
           static NSString *const kAnalyticsMessageUseDeviceTime = @"google.c.a.udt";
  | 
        
        
            | 
            | 
           48 | 
           static NSString *const kAnalyticsTrackConversions = @"google.c.a.tc";
  | 
        
        
            | 
            | 
           49 | 
              | 
        
        
            | 
            | 
           50 | 
           @implementation FIRMessagingAnalytics
  | 
        
        
            | 
            | 
           51 | 
              | 
        
        
            | 
            | 
           52 | 
           + (BOOL)canLogNotification:(NSDictionary *)notification {
  | 
        
        
            | 
            | 
           53 | 
             if (!notification.count) {
  | 
        
        
            | 
            | 
           54 | 
               // Payload is empty
  | 
        
        
            | 
            | 
           55 | 
               return NO;
  | 
        
        
            | 
            | 
           56 | 
             }
  | 
        
        
            | 
            | 
           57 | 
             NSString *isAnalyticsLoggingEnabled = notification[kAnalyticsEnabled];
  | 
        
        
            | 
            | 
           58 | 
             if (![isAnalyticsLoggingEnabled isKindOfClass:[NSString class]] ||
  | 
        
        
            | 
            | 
           59 | 
                 ![isAnalyticsLoggingEnabled isEqualToString:@"1"]) {
  | 
        
        
            | 
            | 
           60 | 
               // Analytics logging is not enabled
  | 
        
        
            | 
            | 
           61 | 
               FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics001,
  | 
        
        
            | 
            | 
           62 | 
                                       @"Analytics logging is disabled. Do not log event.");
  | 
        
        
            | 
            | 
           63 | 
               return NO;
  | 
        
        
            | 
            | 
           64 | 
             }
  | 
        
        
            | 
            | 
           65 | 
             return YES;
  | 
        
        
            | 
            | 
           66 | 
           }
  | 
        
        
            | 
            | 
           67 | 
              | 
        
        
            | 
            | 
           68 | 
           + (void)logOpenNotification:(NSDictionary *)notification
  | 
        
        
            | 
            | 
           69 | 
                           toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  | 
        
        
            | 
            | 
           70 | 
             [self logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  | 
        
        
            | 
            | 
           71 | 
             [self logEvent:kFIRIEventNotificationOpen withNotification:notification toAnalytics:analytics];
  | 
        
        
            | 
            | 
           72 | 
           }
  | 
        
        
            | 
            | 
           73 | 
              | 
        
        
            | 
            | 
           74 | 
           + (void)logForegroundNotification:(NSDictionary *)notification
  | 
        
        
            | 
            | 
           75 | 
                                 toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  | 
        
        
            | 
            | 
           76 | 
             [self logEvent:kFIRIEventNotificationForeground
  | 
        
        
            | 
            | 
           77 | 
                 withNotification:notification
  | 
        
        
            | 
            | 
           78 | 
                      toAnalytics:analytics];
  | 
        
        
            | 
            | 
           79 | 
           }
  | 
        
        
            | 
            | 
           80 | 
              | 
        
        
            | 
            | 
           81 | 
           + (void)logEvent:(NSString *)event
  | 
        
        
            | 
            | 
           82 | 
               withNotification:(NSDictionary *)notification
  | 
        
        
            | 
            | 
           83 | 
                    toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  | 
        
        
            | 
            | 
           84 | 
             if (!event.length) {
  | 
        
        
            | 
            | 
           85 | 
               FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalyticsInvalidEvent,
  | 
        
        
            | 
            | 
           86 | 
                                       @"Can't log analytics with empty event.");
  | 
        
        
            | 
            | 
           87 | 
               return;
  | 
        
        
            | 
            | 
           88 | 
             }
  | 
        
        
            | 
            | 
           89 | 
             NSMutableDictionary *params = [self paramsForEvent:event withNotification:notification];
  | 
        
        
            | 
            | 
           90 | 
              | 
        
        
            | 
            | 
           91 | 
             [analytics logEventWithOrigin:@"fcm" name:event parameters:params];
  | 
        
        
            | 
            | 
           92 | 
             FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics005, @"%@: Sending event: %@ params: %@",
  | 
        
        
            | 
            | 
           93 | 
                                     kLogTag, event, params);
  | 
        
        
            | 
            | 
           94 | 
           }
  | 
        
        
            | 
            | 
           95 | 
              | 
        
        
            | 
            | 
           96 | 
           + (NSMutableDictionary *)paramsForEvent:(NSString *)event
  | 
        
        
            | 
            | 
           97 | 
                                  withNotification:(NSDictionary *)notification {
  | 
        
        
            | 
            | 
           98 | 
             NSDictionary *analyticsDataMap = notification;
  | 
        
        
            | 
            | 
           99 | 
             if (!analyticsDataMap.count) {
  | 
        
        
            | 
            | 
           100 | 
               FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics000,
  | 
        
        
            | 
            | 
           101 | 
                                       @"No data found in notification. Will not log any analytics events.");
  | 
        
        
            | 
            | 
           102 | 
               return nil;
  | 
        
        
            | 
            | 
           103 | 
             }
  | 
        
        
            | 
            | 
           104 | 
              | 
        
        
            | 
            | 
           105 | 
             if (![self canLogNotification:analyticsDataMap]) {
  | 
        
        
            | 
            | 
           106 | 
               return nil;
  | 
        
        
            | 
            | 
           107 | 
             }
  | 
        
        
            | 
            | 
           108 | 
              | 
        
        
            | 
            | 
           109 | 
             NSMutableDictionary *params = [NSMutableDictionary dictionary];
  | 
        
        
            | 
            | 
           110 | 
             NSString *composerIdentifier = analyticsDataMap[kFIRMessagingAnalyticsComposerIdentifier];
  | 
        
        
            | 
            | 
           111 | 
             if ([composerIdentifier isKindOfClass:[NSString class]] && composerIdentifier.length) {
  | 
        
        
            | 
            | 
           112 | 
               params[kFIRIParameterMessageIdentifier] = [composerIdentifier copy];
  | 
        
        
            | 
            | 
           113 | 
             }
  | 
        
        
            | 
            | 
           114 | 
              | 
        
        
            | 
            | 
           115 | 
             NSString *composerLabel = analyticsDataMap[kFIRMessagingAnalyticsComposerLabel];
  | 
        
        
            | 
            | 
           116 | 
             if ([composerLabel isKindOfClass:[NSString class]] && composerLabel.length) {
  | 
        
        
            | 
            | 
           117 | 
               params[kFIRIParameterMessageName] = [composerLabel copy];
  | 
        
        
            | 
            | 
           118 | 
             }
  | 
        
        
            | 
            | 
           119 | 
              | 
        
        
            | 
            | 
           120 | 
             NSString *messageLabel = analyticsDataMap[kFIRMessagingAnalyticsMessageLabel];
  | 
        
        
            | 
            | 
           121 | 
             if ([messageLabel isKindOfClass:[NSString class]] && messageLabel.length) {
  | 
        
        
            | 
            | 
           122 | 
               params[kFIRParameterLabel] = [messageLabel copy];
  | 
        
        
            | 
            | 
           123 | 
             }
  | 
        
        
            | 
            | 
           124 | 
              | 
        
        
            | 
            | 
           125 | 
             NSString *from = analyticsDataMap[kFIRMessagingFromKey];
  | 
        
        
            | 
            | 
           126 | 
             if ([from isKindOfClass:[NSString class]] && [from containsString:@"/topics/"]) {
  | 
        
        
            | 
            | 
           127 | 
               params[kFIRIParameterTopic] = [from copy];
  | 
        
        
            | 
            | 
           128 | 
             }
  | 
        
        
            | 
            | 
           129 | 
              | 
        
        
            | 
            | 
           130 | 
             id timestamp = analyticsDataMap[kAnalyticsMessageTimestamp];
  | 
        
        
            | 
            | 
           131 | 
             if ([timestamp respondsToSelector:@selector(longLongValue)]) {
  | 
        
        
            | 
            | 
           132 | 
               int64_t timestampValue = [timestamp longLongValue];
  | 
        
        
            | 
            | 
           133 | 
               if (timestampValue != 0) {
  | 
        
        
            | 
            | 
           134 | 
                 params[kFIRIParameterMessageTime] = @(timestampValue);
  | 
        
        
            | 
            | 
           135 | 
               }
  | 
        
        
            | 
            | 
           136 | 
             }
  | 
        
        
            | 
            | 
           137 | 
              | 
        
        
            | 
            | 
           138 | 
             if (analyticsDataMap[kAnalyticsMessageUseDeviceTime]) {
  | 
        
        
            | 
            | 
           139 | 
               params[kFIRIParameterMessageDeviceTime] = analyticsDataMap[kAnalyticsMessageUseDeviceTime];
  | 
        
        
            | 
            | 
           140 | 
             }
  | 
        
        
            | 
            | 
           141 | 
              | 
        
        
            | 
            | 
           142 | 
             return params;
  | 
        
        
            | 
            | 
           143 | 
           }
  | 
        
        
            | 
            | 
           144 | 
              | 
        
        
            | 
            | 
           145 | 
           + (void)logUserPropertyForConversionTracking:(NSDictionary *)notification
  | 
        
        
            | 
            | 
           146 | 
                                            toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  | 
        
        
            | 
            | 
           147 | 
             NSInteger shouldTrackConversions = [notification[kAnalyticsTrackConversions] integerValue];
  | 
        
        
            | 
            | 
           148 | 
             if (shouldTrackConversions != 1) {
  | 
        
        
            | 
            | 
           149 | 
               return;
  | 
        
        
            | 
            | 
           150 | 
             }
  | 
        
        
            | 
            | 
           151 | 
              | 
        
        
            | 
            | 
           152 | 
             NSString *composerIdentifier = notification[kFIRMessagingAnalyticsComposerIdentifier];
  | 
        
        
            | 
            | 
           153 | 
             if ([composerIdentifier isKindOfClass:[NSString class]] && composerIdentifier.length) {
  | 
        
        
            | 
            | 
           154 | 
               // Set user property for event.
  | 
        
        
            | 
            | 
           155 | 
               [analytics setUserPropertyWithOrigin:@"fcm"
  | 
        
        
            | 
            | 
           156 | 
                                               name:kFIRIUserPropertyLastNotification
  | 
        
        
            | 
            | 
           157 | 
                                              value:composerIdentifier];
  | 
        
        
            | 
            | 
           158 | 
              | 
        
        
            | 
            | 
           159 | 
               // Set the re-engagement attribution properties.
  | 
        
        
            | 
            | 
           160 | 
               NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3];
  | 
        
        
            | 
            | 
           161 | 
               params[kFIRIParameterSource] = kReengagementSource;
  | 
        
        
            | 
            | 
           162 | 
               params[kFIRIParameterMedium] = kReengagementMedium;
  | 
        
        
            | 
            | 
           163 | 
               params[kFIRIParameterCampaign] = composerIdentifier;
  | 
        
        
            | 
            | 
           164 | 
               [analytics logEventWithOrigin:@"fcm" name:kFIRIEventFirebaseCampaign parameters:params];
  | 
        
        
            | 
            | 
           165 | 
              | 
        
        
            | 
            | 
           166 | 
               FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics003,
  | 
        
        
            | 
            | 
           167 | 
                                       @"%@: Sending event: %@ params: %@", kLogTag,
  | 
        
        
            | 
            | 
           168 | 
                                       kFIRIEventFirebaseCampaign, params);
  | 
        
        
            | 
            | 
           169 | 
              | 
        
        
            | 
            | 
           170 | 
             } else {
  | 
        
        
            | 
            | 
           171 | 
               FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics004,
  | 
        
        
            | 
            | 
           172 | 
                                       @"%@: Failed to set user property: %@ value: %@", kLogTag,
  | 
        
        
            | 
            | 
           173 | 
                                       kFIRIUserPropertyLastNotification, composerIdentifier);
  | 
        
        
            | 
            | 
           174 | 
             }
  | 
        
        
            | 
            | 
           175 | 
           }
  | 
        
        
            | 
            | 
           176 | 
              | 
        
        
            | 
            | 
           177 | 
           + (void)logMessage:(NSDictionary *)notification
  | 
        
        
            | 
            | 
           178 | 
                  toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  | 
        
        
            | 
            | 
           179 | 
             // iOS only because Analytics doesn't support other platforms.
  | 
        
        
            | 
            | 
           180 | 
              | 
        
        
            | 
            | 
           181 | 
           #if TARGET_OS_IOS
  | 
        
        
            | 
            | 
           182 | 
             if (![self canLogNotification:notification]) {
  | 
        
        
            | 
            | 
           183 | 
               return;
  | 
        
        
            | 
            | 
           184 | 
             }
  | 
        
        
            | 
            | 
           185 | 
              | 
        
        
            | 
            | 
           186 | 
             UIApplication *application = [GULAppDelegateSwizzler sharedApplication];
  | 
        
        
            | 
            | 
           187 | 
             if (!application) {
  | 
        
        
            | 
            | 
           188 | 
               return;
  | 
        
        
            | 
            | 
           189 | 
             }
  | 
        
        
            | 
            | 
           190 | 
             UIApplicationState applicationState = application.applicationState;
  | 
        
        
            | 
            | 
           191 | 
             switch (applicationState) {
  | 
        
        
            | 
            | 
           192 | 
               case UIApplicationStateInactive:
  | 
        
        
            | 
            | 
           193 | 
                 // App was in background and in transition to open when user tapped
  | 
        
        
            | 
            | 
           194 | 
                 // on a display notification.
  | 
        
        
            | 
            | 
           195 | 
                 // Needs to check notification is displayed.
  | 
        
        
            | 
            | 
           196 | 
                 if ([[self class] isDisplayNotification:notification]) {
  | 
        
        
            | 
            | 
           197 | 
                   [self logOpenNotification:notification toAnalytics:analytics];
  | 
        
        
            | 
            | 
           198 | 
                 }
  | 
        
        
            | 
            | 
           199 | 
                 break;
  | 
        
        
            | 
            | 
           200 | 
              | 
        
        
            | 
            | 
           201 | 
               case UIApplicationStateActive:
  | 
        
        
            | 
            | 
           202 | 
                 // App was in foreground when it received the notification.
  | 
        
        
            | 
            | 
           203 | 
                 [self logForegroundNotification:notification toAnalytics:analytics];
  | 
        
        
            | 
            | 
           204 | 
                 break;
  | 
        
        
            | 
            | 
           205 | 
              | 
        
        
            | 
            | 
           206 | 
               default:
  | 
        
        
            | 
            | 
           207 | 
                 // App was either in background state or in transition from closed
  | 
        
        
            | 
            | 
           208 | 
                 // to open.
  | 
        
        
            | 
            | 
           209 | 
                 // Needs to check notification is displayed.
  | 
        
        
            | 
            | 
           210 | 
                 if ([[self class] isDisplayNotification:notification]) {
  | 
        
        
            | 
            | 
           211 | 
                   [self logOpenNotification:notification toAnalytics:analytics];
  | 
        
        
            | 
            | 
           212 | 
                 }
  | 
        
        
            | 
            | 
           213 | 
                 break;
  | 
        
        
            | 
            | 
           214 | 
             }
  | 
        
        
            | 
            | 
           215 | 
           #endif
  | 
        
        
            | 
            | 
           216 | 
           }
  | 
        
        
            | 
            | 
           217 | 
              | 
        
        
            | 
            | 
           218 | 
           + (BOOL)isDisplayNotification:(NSDictionary *)notification {
  | 
        
        
            | 
            | 
           219 | 
             NSDictionary *aps = notification[kApsKey];
  | 
        
        
            | 
            | 
           220 | 
             if (!aps || ![aps isKindOfClass:[NSDictionary class]]) {
  | 
        
        
            | 
            | 
           221 | 
               return NO;
  | 
        
        
            | 
            | 
           222 | 
             }
  | 
        
        
            | 
            | 
           223 | 
             NSDictionary *alert = aps[kApsAlertKey];
  | 
        
        
            | 
            | 
           224 | 
             if (!alert) {
  | 
        
        
            | 
            | 
           225 | 
               return NO;
  | 
        
        
            | 
            | 
           226 | 
             }
  | 
        
        
            | 
            | 
           227 | 
             if ([alert isKindOfClass:[NSDictionary class]]) {
  | 
        
        
            | 
            | 
           228 | 
               return alert.allKeys.count > 0;
  | 
        
        
            | 
            | 
           229 | 
             }
  | 
        
        
            | 
            | 
           230 | 
             // alert can be string sometimes (if only body is specified)
  | 
        
        
            | 
            | 
           231 | 
             if ([alert isKindOfClass:[NSString class]]) {
  | 
        
        
            | 
            | 
           232 | 
               return YES;
  | 
        
        
            | 
            | 
           233 | 
             }
  | 
        
        
            | 
            | 
           234 | 
             return NO;
  | 
        
        
            | 
            | 
           235 | 
           }
  | 
        
        
            | 
            | 
           236 | 
              | 
        
        
            | 
            | 
           237 | 
           @end
  |