Jelajahi Sumber

Do Messaging's Analytics logging via Interop (#1902)

Paul Beusterien 7 tahun lalu
induk
melakukan
467354a58f

+ 4 - 0
Example/Firebase.xcodeproj/project.pbxproj

@@ -346,6 +346,7 @@
 		DE26D2931F705F4D004AE1D3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE26D2071F70333E004AE1D3 /* ViewController.swift */; };
 		DE26D2941F705F51004AE1D3 /* AuthCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE26D1FE1F70333E004AE1D3 /* AuthCredentials.swift */; };
 		DE26D2951F705F53004AE1D3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE26D1FD1F70333E004AE1D3 /* AppDelegate.swift */; };
+		DE37C63B2163D5F30025D03E /* FIRMessagingAnalyticsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */; };
 		DE47C0E2207AC87D00B1AEDF /* FIRSampleAppUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = AFC8BAA31EC257D800B8EEAE /* FIRSampleAppUtilities.m */; };
 		DE47C0E7207AC87D00B1AEDF /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; };
 		DE47C114207AC94A00B1AEDF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DE47C107207AC94A00B1AEDF /* GoogleService-Info.plist */; };
@@ -1102,6 +1103,7 @@
 		DE26D25D1F7049F1004AE1D3 /* Auth_ApiTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Auth_ApiTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		DE26D26D1F705C35004AE1D3 /* Auth_EarlGreyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Auth_EarlGreyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		DE26D27D1F705EC7004AE1D3 /* SwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingAnalyticsTest.m; sourceTree = "<group>"; };
 		DE45C6641E7DA8CB009E6ACD /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
 		DE47C0ED207AC87D00B1AEDF /* Messaging_Sample_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Messaging_Sample_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		DE47C107207AC94A00B1AEDF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
@@ -2334,6 +2336,7 @@
 				DE9315D41E8738B70083EDBF /* FIRMessagingSyncMessageManagerTest.m */,
 				DE9315D51E8738B70083EDBF /* FIRMessagingTest.m */,
 				DE9315D71E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.m */,
+				DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */,
 				DE9315D81E8738B70083EDBF /* Info.plist */,
 			);
 			path = Tests;
@@ -4413,6 +4416,7 @@
 				DE9315FF1E8738E60083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */,
 				DE9315F81E8738E60083EDBF /* FIRMessagingDataMessageManagerTest.m in Sources */,
 				DE9316051E8738E60083EDBF /* FIRMessagingTestNotificationUtilities.m in Sources */,
+				DE37C63B2163D5F30025D03E /* FIRMessagingAnalyticsTest.m in Sources */,
 				DE9315F61E8738E60083EDBF /* FIRMessagingConnectionTest.m in Sources */,
 				DE9316041E8738E60083EDBF /* FIRMessagingTest.m in Sources */,
 				DE9315FA1E8738E60083EDBF /* FIRMessagingFakeSocket.m in Sources */,

+ 416 - 0
Example/Messaging/Tests/FIRMessagingAnalyticsTest.m

@@ -0,0 +1,416 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <OCMock/OCMock.h>
+#import <XCTest/XCTest.h>
+
+#import "FIRMessagingAnalytics.h"
+#import <FirebaseAnalyticsInterop/FIRInteropEventNames.h>
+#import <FirebaseAnalyticsInterop/FIRInteropParameterNames.h>
+
+static NSString *const kFIRParameterLabel = @"label";
+static NSString *const kReengagementSource = @"Firebase";
+static NSString *const kReengagementMedium = @"notification";
+static NSString *const kFIREventOriginFCM = @"fcm";
+static const NSTimeInterval kAsyncTestTimout = 0.5;
+
+typedef void (^FakeAnalyticsLogEventHandler)(NSString *origin,
+                                             NSString *name,
+                                             NSDictionary *parameters);
+typedef void (^FakeAnalyticsUserPropertyHandler)(NSString *origin,
+                                                 NSString *name,
+                                                 id value);
+
+@interface FakeAnalytics : NSObject <FIRAnalyticsInterop>
+
+- (instancetype)initWithEventHandler:(FakeAnalyticsLogEventHandler)eventHandler;
+- (instancetype)initWithUserPropertyHandler:(FakeAnalyticsUserPropertyHandler)userPropertyHandler;
+
+@end
+
+@implementation FakeAnalytics
+
+static FakeAnalyticsLogEventHandler _eventHandler;
+static FakeAnalyticsLogEventHandler _userPropertyHandler;
+
+- (instancetype)initWithEventHandler:(FakeAnalyticsLogEventHandler)eventHandler {
+  self = [super init];
+  if (self) {
+    _eventHandler = eventHandler;
+  }
+  return self;
+}
+
+- (instancetype)initWithUserPropertyHandler:(FakeAnalyticsUserPropertyHandler)userPropertyHandler {
+  self = [super init];
+  if (self) {
+    _userPropertyHandler = userPropertyHandler;
+  }
+  return self;
+}
+
+- (void)logEventWithOrigin:(nonnull NSString *)origin
+                      name:(nonnull NSString *)name
+                parameters:(nullable NSDictionary<NSString *, id> *)parameters {
+  if (_eventHandler) {
+    _eventHandler(origin, name, parameters);
+  }
+}
+
+- (void)setUserPropertyWithOrigin:(nonnull NSString *)origin
+                             name:(nonnull NSString *)name
+                            value:(nonnull id)value {
+  if (_userPropertyHandler) {
+    _userPropertyHandler(origin, name, value);
+  }
+}
+
+// Stubs
+- (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName
+                      clearEventName:(nonnull NSString *)clearEventName
+                clearEventParameters:(nonnull NSDictionary *)clearEventParameters {
+}
+
+- (nonnull NSArray<FIRAConditionalUserProperty *> *)
+conditionalUserProperties:(nonnull NSString *)origin
+propertyNamePrefix:(nonnull NSString *)propertyNamePrefix {
+  return nil;
+}
+
+- (NSInteger)maxUserProperties:(nonnull NSString *)origin {
+  return -1;
+}
+
+- (void)setConditionalUserProperty:(nonnull FIRAConditionalUserProperty *)conditionalUserProperty {
+}
+
+@end
+
+@interface FIRMessagingAnalytics (ExposedForTest)
++ (BOOL)canLogNotification:(NSDictionary *)notification;
++ (NSMutableDictionary *)paramsForEvent:(NSString *)event
+                       withNotification:(NSDictionary *)notification;
++ (void)logAnalyticsEventWithOrigin:(NSString *)origin
+                               name:(NSString *)name
+                         parameters:(NSDictionary *)params
+                        toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
++ (void)logUserPropertyForConversionTracking:(NSDictionary *)notification
+                                 toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
++ (void)logOpenNotification:(NSDictionary *)notification
+                toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
++ (void)logForegroundNotification:(NSDictionary *)notification
+                      toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics ;;
++ (void)logEvent:(NSString *)event
+withNotification:(NSDictionary *)notification
+     toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics ;;
+
+@end
+
+@interface FIRMessagingAnalyticsTest : XCTestCase
+
+@property(nonatomic, readwrite, strong) id logClassMock;
+
+@end
+
+@implementation FIRMessagingAnalyticsTest
+
+- (void)setUp {
+  [super setUp];
+  self.logClassMock = OCMClassMock([FIRMessagingAnalytics class]);
+}
+
+- (void)tearDown {
+  [self.logClassMock stopMocking];
+  [super tearDown];
+}
+
+- (void)testCanLogNotification {
+  NSDictionary *notification = @{};
+  XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
+
+  notification = @{@"google.c.a.e" : @1};
+  XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
+
+  notification = @{@"aps" : @{@"alert" : @"to check the reporting format"}};
+  XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
+
+  notification = @{@"aps" : @{@"alert" : @"to check the reporting format"}, @"google.c.a.e" : @"0"};
+  XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
+
+  notification = @{
+                   @"aps" : @{@"alert" : @"to check the reporting format"},
+                   @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                   @"gcm.n.e" : @"1",
+                   @"google.c.a.c_id" : @"575315420755741863",
+                   @"google.c.a.e" : @"1",
+                   @"google.c.a.ts" : @"1522880044",
+                   @"google.c.a.udt" : @"0"
+                   };
+  XCTAssertTrue([FIRMessagingAnalytics canLogNotification:notification]);
+
+  notification = @{
+                   @"aps" : @{@"content-available" : @"1"},
+                   @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                   @"google.c.a.e" : @"1",
+                   @"google.c.a.ts" : @"1522880044",
+                   @"google.c.a.udt" : @"0"
+                   };
+  XCTAssertTrue([FIRMessagingAnalytics canLogNotification:notification]);
+}
+
+- (void)testEmptyNotification {
+  NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@"" withNotification:@{}];
+  XCTAssertNil(params);
+}
+
+- (void)testNoParamsIfAnalyticsIsNotEnabled {
+  NSDictionary *notification = @{
+                                 @"aps" : @{@"alert" : @"to check the reporting format"},
+                                 @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                                 @"gcm.n.e" : @"1",
+                                 @"google.c.a.c_id" : @"575315420755741863",
+                                 @"google.c.a.ts" : @"1522880044",
+                                 @"google.c.a.udt" : @"0"
+                                 };
+
+  NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@"" withNotification:notification];
+  XCTAssertNil(params);
+}
+
+- (void)testNoParamsIfEmpty {
+  NSDictionary *notification = @{
+                                 @"google.c.a.e" : @"1",
+                                 };
+  NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@""
+                                                     withNotification:notification];
+  XCTAssertNotNil(params);
+
+  XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
+  FakeAnalytics *analytics = [[FakeAnalytics alloc]
+      initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
+        XCTAssertEqualObjects(origin, kFIREventOriginFCM);
+        XCTAssertEqualObjects(name, @"_cmp");
+        XCTAssertEqual([parameters count], 0);
+        [expectation fulfill];
+      }];
+  [FIRMessagingAnalytics logEvent:kFIRIEventFirebaseCampaign
+                 withNotification:notification
+                      toAnalytics:analytics];
+  [self waitForExpectationsWithTimeout:kAsyncTestTimout handler:nil];
+}
+- (void)testParamForEventAndNotification {
+  NSDictionary *notification = @{
+                                 @"aps" : @{@"alert" : @"to check the reporting format"},
+                                 @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                                 @"gcm.n.e" : @"1",
+                                 @"google.c.a.c_l" : @"Hello World",
+                                 @"google.c.a.c_id" : @"575315420755741863",
+                                 @"google.c.a.e" : @"1",
+                                 @"google.c.a.ts" : @"1522880044",
+                                 @"google.c.a.udt" : @"0",
+                                 @"google.c.a.m_l" : @"developer's customized label",
+                                 @"from" : @"/topics/news",
+                                 };
+
+  NSMutableDictionary *params =
+  [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
+  XCTAssertNotNil(params);
+  XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
+  XCTAssertEqualObjects(params[kFIRIParameterMessageName], @"Hello World");
+  XCTAssertEqualObjects(params[kFIRParameterLabel], @"developer's customized label");
+  XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
+  XCTAssertEqualObjects([params[kFIRIParameterMessageTime] stringValue], @"1522880044");
+  XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"0");
+}
+
+- (void)testInvalidDataInParamsForLogging {
+  NSString *composerIdentifier = @"Hellow World";
+  NSDictionary *notification = @{
+                                 @"google.c.a.e" : @(YES),
+                                 @"google.c.a.c_l" : composerIdentifier,
+                                 @"google.c.a.c_id" : @"575315420755741863",
+                                 @"google.c.a.m_l" : @"developer's customized label",
+                                 @"google.c.a.ts" : @"1522880044",
+                                 @"from" : @"/topics/news",
+                                 @"google.c.a.udt" : @"0",
+                                 };
+  NSMutableDictionary *params =
+  [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
+  XCTAssertNil(params);
+
+  notification = @{
+                   @"google.c.a.e" : @"1",
+                   @"google.c.a.c_l" : [composerIdentifier dataUsingEncoding:NSUTF8StringEncoding],
+                   @"google.c.a.c_id" : @"575315420755741863",
+                   @"google.c.a.m_l" : @"developer's customized label",
+                   @"google.c.a.ts" : @"1522880044",
+                   @"from" : @"/topics/news",
+                   @"google.c.a.udt" : @"0",
+                   };
+  params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
+  XCTAssertNil(params[kFIRIParameterMessageName]);
+  XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
+  XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
+
+  notification = @{
+                   @"google.c.a.e" : @"1",
+                   @"google.c.a.c_l" : composerIdentifier,
+                   @"google.c.a.c_id" : @(575315420755741863),
+                   @"google.c.a.m_l" : @"developer's customized label",
+                   @"google.c.a.ts" : @"1522880044",
+                   @"from" : @"/topics/news",
+                   @"google.c.a.udt" : @"0",
+                   };
+  params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
+  XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
+  XCTAssertNil(params[kFIRIParameterMessageIdentifier]);
+  XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
+
+  notification = @{
+                   @"google.c.a.e" : @"1",
+                   @"google.c.a.c_l" : composerIdentifier,
+                   @"google.c.a.c_id" : @"575315420755741863",
+                   @"google.c.a.m_l" : @"developer's customized label",
+                   @"google.c.a.ts" : @"0",
+                   @"from" : @"/topics/news",
+                   @"google.c.a.udt" : @"12345678",
+                   };
+  params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
+  XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
+  XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
+  XCTAssertEqualObjects(params[kFIRParameterLabel], @"developer's customized label");
+  XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
+  XCTAssertNil(params[kFIRIParameterMessageTime]);
+  XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"12345678");
+
+  notification = @{
+                   @"google.c.a.e" : @"1",
+                   @"google.c.a.c_l" : composerIdentifier,
+                   @"google.c.a.c_id" : @"575315420755741863",
+                   @"google.c.a.m_l" : @"developer's customized label",
+                   @"google.c.a.ts" : @(0),
+                   @"from" : @"/topics/news",
+                   @"google.c.a.udt" : @"12345678",
+                   };
+  params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
+  XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
+  XCTAssertNil(params[kFIRIParameterMessageTime]);
+  XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"12345678");
+}
+
+- (void)testConversionTracking {
+  // Notification contains "google.c.a.tc" key.
+  NSDictionary *notification = @{
+                                 @"aps" : @{@"alert" : @"to check the reporting format"},
+                                 @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                                 @"gcm.n.e" : @"1",
+                                 @"google.c.a.c_l" : @"Hello World",
+                                 @"google.c.a.c_id" : @"575315420755741863",
+                                 @"google.c.a.e" : @"1",
+                                 @"google.c.a.ts" : @"1522880044",
+                                 @"google.c.a.udt" : @"0",
+                                 @"google.c.a.m_l" : @"developer's customized label",
+                                 @"google.c.a.tc" : @"1",
+                                 @"from" : @"/topics/news",
+                                 };
+  NSDictionary *params = @{
+                           kFIRIParameterSource : kReengagementSource,
+                           kFIRIParameterMedium : kReengagementMedium,
+                           kFIRIParameterCampaign : @"575315420755741863"
+                           };
+  __block XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
+  FakeAnalytics *analytics = [[FakeAnalytics alloc]
+                              initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
+                                XCTAssertEqualObjects(origin, kFIREventOriginFCM);
+                                XCTAssertEqualObjects(name, @"_cmp");
+                                XCTAssertEqualObjects(parameters, params);
+                                [expectation fulfill];
+                                expectation = nil;
+                              }];
+  [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
+  [self waitForExpectationsWithTimeout:kAsyncTestTimout handler:nil];
+}
+
+- (void)testConversionTrackingUserProperty {
+  // Notification contains "google.c.a.tc" key.
+  NSDictionary *notification = @{
+                                 @"aps" : @{@"alert" : @"to check the reporting format"},
+                                 @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                                 @"gcm.n.e" : @"1",
+                                 @"google.c.a.c_l" : @"Hello World",
+                                 @"google.c.a.c_id" : @"575315420755741863",
+                                 @"google.c.a.e" : @"1",
+                                 @"google.c.a.ts" : @"1522880044",
+                                 @"google.c.a.udt" : @"0",
+                                 @"google.c.a.m_l" : @"developer's customized label",
+                                 @"google.c.a.tc" : @"1",
+                                 @"from" : @"/topics/news",
+                                 };
+
+  XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
+  FakeAnalytics *analytics = [[FakeAnalytics alloc]
+                              initWithUserPropertyHandler:^(NSString *origin, NSString *name, id value) {
+                                XCTAssertEqualObjects(origin, kFIREventOriginFCM);
+                                XCTAssertEqualObjects(name, @"_ln");
+                                XCTAssertEqualObjects(value, @"575315420755741863");
+                                [expectation fulfill];
+                              }];
+  [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
+  [self waitForExpectationsWithTimeout:kAsyncTestTimout handler:nil];
+}
+
+- (void)testNoConversionTracking {
+  // Notification contains "google.c.a.tc" key.
+  NSDictionary *notification = @{
+                                 @"aps" : @{@"alert" : @"to check the reporting format"},
+                                 @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
+                                 @"gcm.n.e" : @"1",
+                                 @"google.c.a.c_l" : @"Hello World",
+                                 @"google.c.a.c_id" : @"575315420755741863",
+                                 @"google.c.a.e" : @"1",
+                                 @"google.c.a.ts" : @"1522880044",
+                                 @"google.c.a.udt" : @"0",
+                                 @"google.c.a.m_l" : @"developer's customized label",
+                                 @"from" : @"/topics/news",
+                                 };
+  FakeAnalytics *analytics = [[FakeAnalytics alloc]
+                              initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
+                                XCTAssertTrue(NO);
+                              }];
+  [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
+}
+
+- (void)testLogMessage {
+  NSDictionary *notification = @{
+                                 @"google.c.a.e" : @"1",
+                                 };
+  [FIRMessagingAnalytics logMessage:notification toAnalytics:nil];
+  OCMVerify([self.logClassMock logForegroundNotification:notification toAnalytics:nil]);
+}
+
+- (void)testLogOpenNotification {
+  NSDictionary *notification = @{
+                                 @"google.c.a.e" : @"1",
+                                 };
+  [FIRMessagingAnalytics logOpenNotification:notification toAnalytics:nil];
+
+  OCMVerify([self.logClassMock logUserPropertyForConversionTracking:notification toAnalytics:nil]);
+  OCMVerify([self.logClassMock logEvent:kFIRIEventNotificationOpen
+                       withNotification:notification
+                            toAnalytics:nil]);
+}
+
+@end

+ 2 - 2
Example/Messaging/Tests/FIRMessagingLinkHandlingTest.m

@@ -18,6 +18,7 @@
 
 #import <OCMock/OCMock.h>
 
+#import <FirebaseCore/FIRApp.h>
 #import "FIRMessaging.h"
 #import "FIRMessagingConstants.h"
 #import "FIRMessagingTestNotificationUtilities.h"
@@ -39,8 +40,7 @@
 
 - (void)setUp {
   [super setUp];
-
-  _messaging = [[FIRMessaging alloc] initPrivately];
+  _messaging = [FIRMessaging messaging];
 }
 
 - (void)tearDown {

+ 1 - 0
Example/Messaging/Tests/FIRMessagingServiceTest.m

@@ -25,6 +25,7 @@
 #import "FIRMessagingTopicsCommon.h"
 #import "InternalHeaders/FIRMessagingInternalUtilities.h"
 #import "NSError+FIRMessaging.h"
+#import <FirebaseCore/FIRAppInternal.h>
 
 static NSString *const kFakeToken =
     @"fE1e1PZJFSQ:APA91bFAOjp1ahBWn9rTlbjArwBEm_"

+ 14 - 12
Example/Messaging/Tests/FIRMessagingTest.m

@@ -33,8 +33,6 @@ extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
 @property(nonatomic, readwrite, strong) FIRInstanceID *instanceID;
 @property(nonatomic, readwrite, strong) NSUserDefaults *messagingUserDefaults;
 
-- (instancetype)initWithInstanceID:(FIRInstanceID *)instanceID
-                      userDefaults:(NSUserDefaults *)defaults;
 // Direct Channel Methods
 - (void)updateAutomaticClientConnection;
 - (BOOL)shouldBeConnectedAutomatically;
@@ -46,6 +44,7 @@ extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
 @property(nonatomic, readonly, strong) FIRMessaging *messaging;
 @property(nonatomic, readwrite, strong) id mockMessaging;
 @property(nonatomic, readwrite, strong) id mockInstanceID;
+@property(nonatomic, readwrite, strong) id realInstanceID;
 @property(nonatomic, readwrite, strong) id mockFirebaseApp;
 
 @end
@@ -54,19 +53,21 @@ extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
 
 - (void)setUp {
   [super setUp];
+  _messaging = [FIRMessaging messaging];
   _mockFirebaseApp = OCMClassMock([FIRApp class]);
-  OCMStub([_mockFirebaseApp defaultApp]).andReturn(_mockFirebaseApp);
-
-  _messaging = [[FIRMessaging alloc] initWithInstanceID:[FIRInstanceID instanceID]
-                                           userDefaults:[NSUserDefaults standardUserDefaults]];
-  _mockMessaging = OCMPartialMock(self.messaging);
+   OCMStub([_mockFirebaseApp defaultApp]).andReturn(_mockFirebaseApp);
   _mockInstanceID = OCMPartialMock(self.messaging.instanceID);
-  self.messaging.instanceID = _mockInstanceID;
+  _realInstanceID = self.messaging.instanceID;
+  self.messaging.instanceID = self.mockInstanceID;
   [[NSUserDefaults standardUserDefaults]
       removePersistentDomainForName:[NSBundle mainBundle].bundleIdentifier];
 }
 
 - (void)tearDown {
+  self.messaging.shouldEstablishDirectChannel = NO;
+  self.messaging.defaultFcmToken = nil;
+  self.messaging.instanceID = self.realInstanceID;
+  self.messaging.apnsTokenData = nil;
   [_mockMessaging stopMocking];
   [_mockInstanceID stopMocking];
   [_mockFirebaseApp stopMocking];
@@ -138,7 +139,7 @@ extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
   UIApplication *app = [UIApplication sharedApplication];
   id mockApp = OCMPartialMock(app);
   [[[mockApp stub] andReturnValue:@(UIApplicationStateActive)] applicationState];
-  BOOL shouldBeConnected = [_mockMessaging shouldBeConnectedAutomatically];
+  BOOL shouldBeConnected = [_messaging shouldBeConnectedAutomatically];
   XCTAssertTrue(shouldBeConnected);
 }
 
@@ -155,7 +156,7 @@ extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
   UIApplication *app = [UIApplication sharedApplication];
   id mockApp = OCMPartialMock(app);
   [[[mockApp stub] andReturnValue:@(UIApplicationStateActive)] applicationState];
-  BOOL shouldBeConnected = [_mockMessaging shouldBeConnectedAutomatically];
+  BOOL shouldBeConnected = [_messaging shouldBeConnectedAutomatically];
   XCTAssertFalse(shouldBeConnected);
 }
 
@@ -186,12 +187,13 @@ extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
       [self expectationWithDescription:@"Included APNS Token data in options dict."];
   // Inspect the 'options' dictionary to tell whether our expectation was fulfilled
   [[[self.mockInstanceID stub] andDo:^(NSInvocation *invocation) {
-  NSDictionary *options;
-  [invocation getArgument:&options atIndex:4];
+    NSDictionary *options;
+    [invocation getArgument:&options atIndex:4];
     if (options[@"apns_token"] != nil) {
       [expectation fulfill];
     }
   }] tokenWithAuthorizedEntity:OCMOCK_ANY scope:OCMOCK_ANY options:OCMOCK_ANY handler:OCMOCK_ANY];
+  self.messaging.instanceID = self.mockInstanceID;
   [self.messaging retrieveFCMTokenForSenderID:@"123456"
                                    completion:^(NSString * _Nullable FCMToken,
                                                 NSError * _Nullable error) {}];

+ 10 - 0
Firebase/Messaging/FIRMMessageCode.h

@@ -178,4 +178,14 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) {
   kFIRMessagingMessageCodeUtilities000 = 18000,  // I-FCM018000
   kFIRMessagingMessageCodeUtilities001 = 18001,  // I-FCM018001
   kFIRMessagingMessageCodeUtilities002 = 18002,  // I-FCM018002
+  // FIRMessagingAnalytics.m
+  kFIRMessagingMessageCodeAnalytics000 = 19000,  // I-FCM019000
+  kFIRMessagingMessageCodeAnalytics001 = 19001,  // I-FCM019001
+  kFIRMessagingMessageCodeAnalytics002 = 19002,  // I-FCM019002
+  kFIRMessagingMessageCodeAnalytics003 = 19003,  // I-FCM019003
+  kFIRMessagingMessageCodeAnalytics004 = 19004,  // I-FCM019004
+  kFIRMessagingMessageCodeAnalytics005 = 19005,  // I-FCM019005
+  kFIRMessagingMessageCodeAnalyticsInvalidEvent = 19006,  // I-FCM019006
+  kFIRMessagingMessageCodeAnalytics007 = 19007,  // I-FCM019007
+  kFIRMessagingMessageCodeAnalyticsCouldNotInvokeAnalyticsLog = 19008,  // I-FCM019008
 };

+ 54 - 34
Firebase/Messaging/FIRMessaging.m

@@ -23,6 +23,7 @@
 
 #import <UIKit/UIKit.h>
 
+#import "FIRMessagingAnalytics.h"
 #import "FIRMessagingClient.h"
 #import "FIRMessagingConstants.h"
 #import "FIRMessagingContextManagerService.h"
@@ -38,7 +39,13 @@
 #import "FIRMessagingVersionUtilities.h"
 #import "FIRMessaging_Private.h"
 
+#import <FirebaseAnalyticsInterop/FIRAnalyticsInterop.h>
 #import <FirebaseCore/FIRAppInternal.h>
+#import <FirebaseCore/FIRComponent.h>
+#import <FirebaseCore/FIRComponentContainer.h>
+#import <FirebaseCore/FIRComponentRegistrant.h>
+#import <FirebaseCore/FIRCoreConfigurable.h>
+#import <FirebaseCore/FIRDependency.h>
 #import <FirebaseInstanceID/FirebaseInstanceID.h>
 #import <GoogleUtilities/GULReachabilityChecker.h>
 
@@ -141,40 +148,48 @@ NSString *const kFIRMessagingPlistAutoInitEnabled =
 /// which can happen if the user inadvertently calls `appDidReceiveMessage` along with us
 /// calling it implicitly during swizzling.
 @property(nonatomic, readwrite, strong) NSMutableSet *loggedMessageIDs;
+@property(nonatomic, readwrite, strong) id<FIRAnalyticsInterop> _Nullable analytics;
 
-- (instancetype)initWithInstanceID:(FIRInstanceID *)instanceID
-                      userDefaults:(NSUserDefaults *)defaults NS_DESIGNATED_INITIALIZER;
+@end
+
+// Messaging doesn't provide any functionality to other components,
+// so it provides a private, empty protocol that it conforms to and use it for registration.
+
+@protocol FIRMessagingInstanceProvider
+@end
 
+@interface FIRMessaging () <FIRMessagingInstanceProvider,
+                            FIRCoreConfigurable,
+                            FIRComponentRegistrant>
 @end
 
 @implementation FIRMessaging
 
 + (FIRMessaging *)messaging {
-  static FIRMessaging *messaging;
+  FIRApp *defaultApp = [FIRApp defaultApp];  // Missing configure will be logged here.
+  id<FIRMessagingInstanceProvider> messaging =
+      FIR_COMPONENT(FIRMessagingInstanceProvider, defaultApp.container);
+
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    messaging = [[FIRMessaging alloc] initPrivately];
-    [messaging start];
+    [(FIRMessaging *)messaging start];
   });
-  return messaging;
+  return (FIRMessaging *)messaging;
 }
 
-- (instancetype)initWithInstanceID:(FIRInstanceID *)instanceID
-                      userDefaults:(NSUserDefaults *)defaults {
+- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics
+                   withInstanceID:(FIRInstanceID *)instanceID
+                 withUserDefaults:(NSUserDefaults *)defaults {
   self = [super init];
   if (self != nil) {
     _loggedMessageIDs = [NSMutableSet set];
     _instanceID = instanceID;
     _messagingUserDefaults = defaults;
+    _analytics = analytics;
   }
   return self;
 }
 
-- (instancetype)initPrivately {
-  return [self initWithInstanceID:[FIRInstanceID instanceID]
-                     userDefaults:[NSUserDefaults standardUserDefaults]];
-}
-
 - (void)dealloc {
   [self.reachability stop];
   [[NSNotificationCenter defaultCenter] removeObserver:self];
@@ -184,24 +199,38 @@ NSString *const kFIRMessagingPlistAutoInitEnabled =
 #pragma mark - Config
 
 + (void)load {
-  [[NSNotificationCenter defaultCenter] addObserver:self
-                                           selector:@selector(didReceiveConfigureSDKNotification:)
-                                               name:kFIRAppReadyToConfigureSDKNotification
-                                             object:nil];
+  [FIRApp registerAsConfigurable:self];
+  [FIRComponentContainer registerAsComponentRegistrant:self];
+}
+
++ (nonnull NSArray<FIRComponent *> *)componentsToRegister {
+  FIRDependency *analyticsDep =
+      [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop) isRequired:NO];
+  FIRComponentCreationBlock creationBlock =
+      ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
+    // Ensure it's cached so it returns the same instance every time messaging is called.
+    *isCacheable = YES;
+    id<FIRAnalyticsInterop> analytics = FIR_COMPONENT(FIRAnalyticsInterop, container);
+        return [[FIRMessaging alloc] initWithAnalytics:analytics
+                                        withInstanceID:[FIRInstanceID instanceID]
+                                      withUserDefaults:[NSUserDefaults standardUserDefaults]];
+  };
+  FIRComponent *messagingProvider =
+      [FIRComponent componentWithProtocol:@protocol(FIRMessagingInstanceProvider)
+                      instantiationTiming:FIRInstantiationTimingLazy
+                             dependencies:@[ analyticsDep ]
+                           creationBlock:creationBlock];
+
+  return @[ messagingProvider ];
 }
 
-+ (void)didReceiveConfigureSDKNotification:(NSNotification *)notification {
-  NSDictionary *appInfoDict = notification.userInfo;
-  NSNumber *isDefaultApp = appInfoDict[kFIRAppIsDefaultAppKey];
-  if (![isDefaultApp boolValue]) {
++ (void)configureWithApp:(FIRApp *)app {
+  if (!app.isDefaultApp) {
     // Only configure for the default FIRApp.
     FIRMessagingLoggerDebug(kFIRMessagingMessageCodeFIRApp001,
                             @"Firebase Messaging only works with the default app.");
     return;
   }
-
-  NSString *appName = appInfoDict[kFIRAppNameKey];
-  FIRApp *app = [FIRApp appNamed:appName];
   [[FIRMessaging messaging] configureMessaging:app];
 }
 
@@ -367,16 +396,7 @@ NSString *const kFIRMessagingPlistAutoInitEnabled =
   }
 
   if (!isOldMessage) {
-    Class firMessagingLogClass = NSClassFromString(@"FIRMessagingLog");
-    SEL logMessageSelector = NSSelectorFromString(@"logMessage:");
-
-    if ([firMessagingLogClass respondsToSelector:logMessageSelector]) {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-      [firMessagingLogClass performSelector:logMessageSelector
-                                 withObject:message];
-    }
-#pragma clang diagnostic pop
+    [FIRMessagingAnalytics logMessage:message toAnalytics:_analytics];
     [self handleContextManagerMessage:message];
     [self handleIncomingLinkIfNeededFromMessage:message];
   }

+ 44 - 0
Firebase/Messaging/FIRMessagingAnalytics.h

@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import <FirebaseAnalyticsInterop/FIRAnalyticsInterop.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Provides integration between FIRMessaging and Analytics.
+ *
+ * All Analytics dependencies should be kept in this class, and missing dependencies should be
+ * handled gracefully.
+ *
+ */
+@interface FIRMessagingAnalytics : NSObject
+
+/**
+ * Determine whether a notification has the properties to be loggable to Analytics.
+ * If so, send the notification.
+ * @param notification The notification payload from APNs
+ * @param analytics The class to be used as the receiver of the logging method
+ */
+
++ (void)logMessage:(NSDictionary *)notification
+       toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 225 - 0
Firebase/Messaging/FIRMessagingAnalytics.m

@@ -0,0 +1,225 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <UIKit/UIKit.h>
+
+#import "FIRMessagingAnalytics.h"
+#import "FIRMessagingLogger.h"
+
+#import <FirebaseAnalyticsInterop/FIRInteropEventNames.h>
+#import <FirebaseAnalyticsInterop/FIRInteropParameterNames.h>
+#import <GoogleUtilities/GULAppEnvironmentUtil.h>
+
+static NSString *const kLogTag = @"FIRMessagingAnalytics";
+
+// aps Key
+static NSString *const kApsKey = @"aps";
+static NSString *const kApsAlertKey = @"alert";
+static NSString *const kApsSoundKey = @"sound";
+static NSString *const kApsBadgeKey = @"badge";
+static NSString *const kApsContentAvailableKey = @"badge";
+
+// Data Key
+static NSString *const kDataKey = @"data";
+
+// Messaging From Key
+static NSString *const kFIRMessagingFromKey = @"from";
+
+static NSString *const kFIRParameterLabel = @"label";
+
+static NSString *const kReengagementSource = @"Firebase";
+static NSString *const kReengagementMedium = @"notification";
+
+// Analytics
+static NSString *const kAnalyticsEnabled =              @"google.c.a." @"e";
+static NSString *const kAnalyticsComposerIdentifier =   @"google.c.a." @"c_id";
+static NSString *const kAnalyticsComposerLabel =        @"google.c.a." @"c_l";
+static NSString *const kAnalyticsMessageLabel =         @"google.c.a." @"m_l";
+static NSString *const kAnalyticsMessageTimestamp =     @"google.c.a." @"ts";
+static NSString *const kAnalyticsMessageUseDeviceTime = @"google.c.a." @"udt";
+static NSString *const kAnalyticsTrackConversions =     @"google.c.a." @"tc";
+
+@implementation FIRMessagingAnalytics
+
++ (BOOL)canLogNotification:(NSDictionary *)notification {
+  if (!notification.count) {
+    // Payload is empty
+    return NO;
+  }
+  NSString *isAnalyticsLoggingEnabled = notification[kAnalyticsEnabled];
+  if (![isAnalyticsLoggingEnabled isKindOfClass:[NSString class]] ||
+      ![isAnalyticsLoggingEnabled isEqualToString:@"1"]) {
+    // Analytics logging is not enabled
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics001,
+                            @"Analytics logging is disabled. Do not log event.");
+    return NO;
+  }
+  return YES;
+}
+
++ (void)logOpenNotification:(NSDictionary *)notification
+                toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
+  [self logUserPropertyForConversionTracking:notification toAnalytics:analytics];
+  [self logEvent:kFIRIEventNotificationOpen
+withNotification:notification
+     toAnalytics:analytics];
+}
+
++ (void)logForegroundNotification:(NSDictionary *)notification
+                      toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
+  [self logEvent:kFIRIEventNotificationForeground
+withNotification:notification
+     toAnalytics:analytics];
+}
+
++ (void)logEvent:(NSString *)event
+withNotification:(NSDictionary *)notification
+     toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
+  if (!event.length) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalyticsInvalidEvent,
+                             @"Can't log analytics with empty event.");
+    return;
+  }
+  NSMutableDictionary *params = [self paramsForEvent:event withNotification:notification];
+
+  [analytics logEventWithOrigin:@"fcm" name:event parameters:params];
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics005,
+                           @"%@: Sending event: %@ params: %@", kLogTag, event, params);
+}
+
++ (NSMutableDictionary *)paramsForEvent:(NSString *)event
+                       withNotification:(NSDictionary *)notification {
+  NSDictionary *analyticsDataMap = notification;
+  if (!analyticsDataMap.count) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics000,
+                             @"No data found in notification. Will not log any analytics events.");
+    return nil;
+  }
+
+  if (![self canLogNotification:analyticsDataMap]) {
+    return nil;
+  }
+
+  NSMutableDictionary *params = [NSMutableDictionary dictionary];
+  NSString *composerIdentifier = analyticsDataMap[kAnalyticsComposerIdentifier];
+  if ([composerIdentifier isKindOfClass:[NSString class]] && composerIdentifier.length) {
+    params[kFIRIParameterMessageIdentifier] = [composerIdentifier copy];
+  }
+
+  NSString *composerLabel = analyticsDataMap[kAnalyticsComposerLabel];
+  if ([composerLabel isKindOfClass:[NSString class]] && composerLabel.length) {
+    params[kFIRIParameterMessageName] = [composerLabel copy];
+  }
+
+  NSString *messageLabel = analyticsDataMap[kAnalyticsMessageLabel];
+  if ([messageLabel isKindOfClass:[NSString class]] && messageLabel.length) {
+    params[kFIRParameterLabel] = [messageLabel copy];
+  }
+
+  NSString *from = analyticsDataMap[kFIRMessagingFromKey];
+  if ([from isKindOfClass:[NSString class]] && [from containsString:@"/topics/"]) {
+    params[kFIRIParameterTopic] = [from copy];
+  }
+
+  id timestamp = analyticsDataMap[kAnalyticsMessageTimestamp];
+  if ([timestamp respondsToSelector:@selector(longLongValue)]) {
+    int64_t timestampValue = [timestamp longLongValue];
+    if (timestampValue != 0) {
+      params[kFIRIParameterMessageTime] = @(timestampValue);
+    }
+  }
+
+  if (analyticsDataMap[kAnalyticsMessageUseDeviceTime]) {
+    params[kFIRIParameterMessageDeviceTime] = analyticsDataMap[kAnalyticsMessageUseDeviceTime];
+  }
+
+  return params;
+}
+
++ (void)logUserPropertyForConversionTracking:(NSDictionary *)notification
+                                 toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
+  NSInteger shouldTrackConversions = [notification[kAnalyticsTrackConversions] integerValue];
+  if (shouldTrackConversions != 1) {
+    return;
+  }
+
+  NSString *composerIdentifier = notification[kAnalyticsComposerIdentifier];
+  if ([composerIdentifier isKindOfClass:[NSString class]] && composerIdentifier.length) {
+    // Set user property for event.
+    [analytics setUserPropertyWithOrigin:@"fcm"
+                                    name:kFIRIUserPropertyLastNotification
+                                   value:composerIdentifier];
+
+    // Set the re-engagement attribution properties.
+    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3];
+    params[kFIRIParameterSource] = kReengagementSource;
+    params[kFIRIParameterMedium] = kReengagementMedium;
+    params[kFIRIParameterCampaign] = composerIdentifier;
+    [analytics logEventWithOrigin:@"fcm" name:kFIRIEventFirebaseCampaign parameters:params];
+
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics003,
+                             @"%@: Sending event: %@ params: %@", kLogTag,
+                             kFIRIEventFirebaseCampaign, params);
+
+  } else {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeAnalytics004,
+                             @"%@: Failed to set user property: %@ value: %@", kLogTag,
+                             kFIRIUserPropertyLastNotification, composerIdentifier);
+  }
+}
+
++ (void)logMessage:(NSDictionary *)notification
+       toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
+  if (![self canLogNotification:notification]) {
+    return;
+  }
+
+  UIApplication *application = [self currentUIApplication];
+  if (!application) {
+    return;
+  }
+  UIApplicationState applicationState = application.applicationState;
+  switch (applicationState) {
+    case UIApplicationStateInactive:
+      // App was either in background(suspended) or inactive and user tapped on a display
+      // notification.
+      [self logOpenNotification:notification toAnalytics:analytics];
+      break;
+
+    case UIApplicationStateActive:
+      // App was in foreground when it received the notification.
+      [self logForegroundNotification:notification toAnalytics:analytics];
+      break;
+
+    default:
+      // Only a silent notification (i.e. 'content-available' is true) can be received while the app
+      // is in the background. These messages aren't loggable anyway.
+      break;
+  }
+}
+
++ (UIApplication *)currentUIApplication {
+  Class applicationClass = nil;
+  if (![GULAppEnvironmentUtil isAppExtension]) {
+    Class cls = NSClassFromString(@"UIApplication");
+    if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
+      applicationClass = cls;
+    }
+  }
+  return [applicationClass sharedApplication];
+}
+
+@end

+ 1 - 1
Firebase/Messaging/Public/FIRMessaging.h

@@ -59,7 +59,7 @@ typedef void (^FIRMessagingTopicOperationCompletion)(NSError *_Nullable error);
 
 /**
  *  The completion handler invoked once the data connection with FIRMessaging is
- *  established.  The data connection is used to send a continous stream of
+ *  established.  The data connection is used to send a continuous stream of
  *  data and all the FIRMessaging data notifications arrive through this connection.
  *  Once the connection is established we invoke the callback with `nil` error.
  *  Correspondingly if we get an error while trying to establish a connection

+ 1 - 1
FirebaseAnalyticsInterop.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseAnalyticsInterop'
-  s.version          = '1.0.0'
+  s.version          = '1.1.0'
   s.summary          = 'Interfaces that allow other Firebase SDKs to use Analytics functionality.'
 
   s.description      = <<-DESC

+ 2 - 1
FirebaseMessaging.podspec

@@ -36,7 +36,8 @@ device, and it is completely free.
       'FIRMessaging_LIB_VERSION=' + String(s.version)
   }
   s.framework = 'SystemConfiguration'
-  s.dependency 'FirebaseCore', '~> 5.0'
+  s.ios.dependency 'FirebaseAnalyticsInterop', '~> 1.1'
+  s.dependency 'FirebaseCore', '~> 5.1'
   s.dependency 'FirebaseInstanceID', '~> 3.0'
   s.dependency 'GoogleUtilities/Reachability', '~> 5.2'
   s.dependency 'GoogleUtilities/Environment', '~> 5.2'

+ 26 - 0
Interop/Analytics/Public/FIRInteropEventNames.h

@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/// @file FIRInteropEventNames.h
+
+/// Notification open event name.
+static NSString *const kFIRIEventNotificationOpen = @"_no";
+
+/// Notification foreground event name.
+static NSString *const kFIRIEventNotificationForeground = @"_nf";
+
+/// Campaign event name.
+static NSString *const kFIRIEventFirebaseCampaign = @"_cmp";

+ 18 - 0
Interop/Analytics/Public/FIRInteropParameterNames.h

@@ -51,3 +51,21 @@ static NSString *const kFIRIParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedi
 /// </pre>
 static NSString *const kFIRIParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) =
     @"campaign";
+
+/// Message identifier.
+static NSString *const kFIRIParameterMessageIdentifier = @"_nmid";
+
+/// Message name.
+static NSString *const kFIRIParameterMessageName = @"_nmn";
+
+/// Message send time.
+static NSString *const kFIRIParameterMessageTime = @"_nmt";
+
+/// Message device time.
+static NSString *const kFIRIParameterMessageDeviceTime = @"_ndt";
+
+/// Topic message.
+static NSString *const kFIRIParameterTopic = @"_nt";
+
+/// Stores the message_id of the last notification opened by the app.
+static NSString *const kFIRIUserPropertyLastNotification = @"_ln";

+ 2 - 2
scripts/pod_lib_lint.sh

@@ -37,8 +37,8 @@ fi
 # or Core APIs change. GoogleUtilities.podspec and FirebaseCore.podspec should be
 # manually pushed to a temporary Specs repo. See
 # https://guides.cocoapods.org/making/private-cocoapods.
-#ALT_SOURCES="--sources=git@github.com:paulb777/Specs.git,https://github.com/CocoaPods/Specs.git"
-ALT_SOURCES=
+#ALT_SOURCES="--sources=https://github.com/paulb777/Specs.git,https://github.com/CocoaPods/Specs.git"
+ALT_SOURCES="--sources=https://github.com/paulb777/Specs.git,https://github.com/CocoaPods/Specs.git"
 
 podspec="$1"
 if [[ $# -gt 1 ]]; then