You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
8.3 KiB

  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. #import "Firebase/Messaging/FIRMessagingAnalytics.h"
  17. #import <FirebaseAnalyticsInterop/FIRInteropEventNames.h>
  18. #import <FirebaseAnalyticsInterop/FIRInteropParameterNames.h>
  19. #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  20. #import <GoogleUtilities/GULAppDelegateSwizzler.h>
  21. #import "Firebase/Messaging/FIRMessagingLogger.h"
  22. static NSString *const kLogTag = @"FIRMessagingAnalytics";
  23. // aps Key
  24. static NSString *const kApsKey = @"aps";
  25. static NSString *const kApsAlertKey = @"alert";
  26. static NSString *const kApsSoundKey = @"sound";
  27. static NSString *const kApsBadgeKey = @"badge";
  28. static NSString *const kApsContentAvailableKey = @"badge";
  29. // Data Key
  30. static NSString *const kDataKey = @"data";
  31. // Messaging From Key
  32. static NSString *const kFIRMessagingFromKey = @"from";
  33. static NSString *const kFIRParameterLabel = @"label";
  34. static NSString *const kReengagementSource = @"Firebase";
  35. static NSString *const kReengagementMedium = @"notification";
  36. // Analytics
  37. static NSString *const kAnalyticsEnabled = @"google.c.a." @"e";
  38. static NSString *const kAnalyticsComposerIdentifier = @"google.c.a." @"c_id";
  39. static NSString *const kAnalyticsComposerLabel = @"google.c.a." @"c_l";
  40. static NSString *const kAnalyticsMessageLabel = @"google.c.a." @"m_l";
  41. static NSString *const kAnalyticsMessageTimestamp = @"google.c.a." @"ts";
  42. static NSString *const kAnalyticsMessageUseDeviceTime = @"google.c.a." @"udt";
  43. static NSString *const kAnalyticsTrackConversions = @"google.c.a." @"tc";
  44. @implementation FIRMessagingAnalytics
  45. + (BOOL)canLogNotification:(NSDictionary *)notification {
  46. if (!notification.count) {
  47. // Payload is empty
  48. return NO;
  49. }
  50. NSString *isAnalyticsLoggingEnabled = notification[kAnalyticsEnabled];
  51. if (![isAnalyticsLoggingEnabled isKindOfClass:[NSString class]] ||
  52. ![isAnalyticsLoggingEnabled isEqualToString:@"1"]) {
  53. // Analytics logging is not enabled
  54. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics001,
  55. @"Analytics logging is disabled. Do not log event.");
  56. return NO;
  57. }
  58. return YES;
  59. }
  60. + (void)logOpenNotification:(NSDictionary *)notification
  61. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  62. [self logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  63. [self logEvent:kFIRIEventNotificationOpen
  64. withNotification:notification
  65. toAnalytics:analytics];
  66. }
  67. + (void)logForegroundNotification:(NSDictionary *)notification
  68. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  69. [self logEvent:kFIRIEventNotificationForeground
  70. withNotification:notification
  71. toAnalytics:analytics];
  72. }
  73. + (void)logEvent:(NSString *)event
  74. withNotification:(NSDictionary *)notification
  75. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  76. if (!event.length) {
  77. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalyticsInvalidEvent,
  78. @"Can't log analytics with empty event.");
  79. return;
  80. }
  81. NSMutableDictionary *params = [self paramsForEvent:event withNotification:notification];
  82. [analytics logEventWithOrigin:@"fcm" name:event parameters:params];
  83. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics005,
  84. @"%@: Sending event: %@ params: %@", kLogTag, event, params);
  85. }
  86. + (NSMutableDictionary *)paramsForEvent:(NSString *)event
  87. withNotification:(NSDictionary *)notification {
  88. NSDictionary *analyticsDataMap = notification;
  89. if (!analyticsDataMap.count) {
  90. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics000,
  91. @"No data found in notification. Will not log any analytics events.");
  92. return nil;
  93. }
  94. if (![self canLogNotification:analyticsDataMap]) {
  95. return nil;
  96. }
  97. NSMutableDictionary *params = [NSMutableDictionary dictionary];
  98. NSString *composerIdentifier = analyticsDataMap[kAnalyticsComposerIdentifier];
  99. if ([composerIdentifier isKindOfClass:[NSString class]] && composerIdentifier.length) {
  100. params[kFIRIParameterMessageIdentifier] = [composerIdentifier copy];
  101. }
  102. NSString *composerLabel = analyticsDataMap[kAnalyticsComposerLabel];
  103. if ([composerLabel isKindOfClass:[NSString class]] && composerLabel.length) {
  104. params[kFIRIParameterMessageName] = [composerLabel copy];
  105. }
  106. NSString *messageLabel = analyticsDataMap[kAnalyticsMessageLabel];
  107. if ([messageLabel isKindOfClass:[NSString class]] && messageLabel.length) {
  108. params[kFIRParameterLabel] = [messageLabel copy];
  109. }
  110. NSString *from = analyticsDataMap[kFIRMessagingFromKey];
  111. if ([from isKindOfClass:[NSString class]] && [from containsString:@"/topics/"]) {
  112. params[kFIRIParameterTopic] = [from copy];
  113. }
  114. id timestamp = analyticsDataMap[kAnalyticsMessageTimestamp];
  115. if ([timestamp respondsToSelector:@selector(longLongValue)]) {
  116. int64_t timestampValue = [timestamp longLongValue];
  117. if (timestampValue != 0) {
  118. params[kFIRIParameterMessageTime] = @(timestampValue);
  119. }
  120. }
  121. if (analyticsDataMap[kAnalyticsMessageUseDeviceTime]) {
  122. params[kFIRIParameterMessageDeviceTime] = analyticsDataMap[kAnalyticsMessageUseDeviceTime];
  123. }
  124. return params;
  125. }
  126. + (void)logUserPropertyForConversionTracking:(NSDictionary *)notification
  127. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  128. NSInteger shouldTrackConversions = [notification[kAnalyticsTrackConversions] integerValue];
  129. if (shouldTrackConversions != 1) {
  130. return;
  131. }
  132. NSString *composerIdentifier = notification[kAnalyticsComposerIdentifier];
  133. if ([composerIdentifier isKindOfClass:[NSString class]] && composerIdentifier.length) {
  134. // Set user property for event.
  135. [analytics setUserPropertyWithOrigin:@"fcm"
  136. name:kFIRIUserPropertyLastNotification
  137. value:composerIdentifier];
  138. // Set the re-engagement attribution properties.
  139. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3];
  140. params[kFIRIParameterSource] = kReengagementSource;
  141. params[kFIRIParameterMedium] = kReengagementMedium;
  142. params[kFIRIParameterCampaign] = composerIdentifier;
  143. [analytics logEventWithOrigin:@"fcm" name:kFIRIEventFirebaseCampaign parameters:params];
  144. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics003,
  145. @"%@: Sending event: %@ params: %@", kLogTag,
  146. kFIRIEventFirebaseCampaign, params);
  147. } else {
  148. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics004,
  149. @"%@: Failed to set user property: %@ value: %@", kLogTag,
  150. kFIRIUserPropertyLastNotification, composerIdentifier);
  151. }
  152. }
  153. + (void)logMessage:(NSDictionary *)notification
  154. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
  155. // iOS onlly because Analytics doesn't support tvOS.
  156. #if TARGET_OS_IOS
  157. if (![self canLogNotification:notification]) {
  158. return;
  159. }
  160. UIApplication *application = [GULAppDelegateSwizzler sharedApplication];
  161. if (!application) {
  162. return;
  163. }
  164. UIApplicationState applicationState = application.applicationState;
  165. switch (applicationState) {
  166. case UIApplicationStateInactive:
  167. // App was either in background(suspended) or inactive and user tapped on a display
  168. // notification.
  169. [self logOpenNotification:notification toAnalytics:analytics];
  170. break;
  171. case UIApplicationStateActive:
  172. // App was in foreground when it received the notification.
  173. [self logForegroundNotification:notification toAnalytics:analytics];
  174. break;
  175. default:
  176. // Only a silent notification (i.e. 'content-available' is true) can be received while the app
  177. // is in the background. These messages aren't loggable anyway.
  178. break;
  179. }
  180. #endif
  181. }
  182. @end