From d7f3074bca5356c09090b94eb07f2b441d59bd99 Mon Sep 17 00:00:00 2001 From: Muhammad Rehan Date: Fri, 8 Nov 2024 11:25:19 +0500 Subject: [PATCH] chore: update event tracking parameters (#164) --- .../customer/customer_io/CustomerIoPlugin.kt | 14 +++++------ .../io/customer/customer_io/constant/Keys.kt | 4 ++++ apps/amiapp_flutter/lib/main.dart | 2 +- .../lib/src/screens/dashboard.dart | 10 ++++---- .../lib/src/screens/events.dart | 2 +- .../lib/src/screens/settings.dart | 3 +-- ios/Classes/Keys.swift | 4 ++++ ios/Classes/SwiftCustomerIoPlugin.swift | 24 ++++++++----------- lib/customer_io.dart | 6 ++--- lib/customer_io_const.dart | 5 ++++ lib/customer_io_method_channel.dart | 6 ++--- lib/customer_io_platform_interface.dart | 2 +- test/customer_io_method_channel_test.dart | 6 ++--- test/customer_io_test.dart | 8 +++---- test/customer_io_test.mocks.dart | 4 ++-- 15 files changed, 53 insertions(+), 47 deletions(-) diff --git a/android/src/main/kotlin/io/customer/customer_io/CustomerIoPlugin.kt b/android/src/main/kotlin/io/customer/customer_io/CustomerIoPlugin.kt index 4e1fb75..8dbc1cc 100644 --- a/android/src/main/kotlin/io/customer/customer_io/CustomerIoPlugin.kt +++ b/android/src/main/kotlin/io/customer/customer_io/CustomerIoPlugin.kt @@ -170,18 +170,16 @@ class CustomerIoPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } private fun track(params: Map) { - // TODO: Fix track implementation - /* - val name = params.getString(Keys.Tracking.EVENT_NAME) - val attributes = - params.getProperty>(Keys.Tracking.ATTRIBUTES) ?: emptyMap() + val name = requireNotNull(params.getAsTypeOrNull(Keys.Tracking.NAME)) { + "Event name is required to track event" + } + val properties = params.getAsTypeOrNull>(Keys.Tracking.PROPERTIES) - if (attributes.isEmpty()) { + if (properties.isNullOrEmpty()) { CustomerIO.instance().track(name) } else { - CustomerIO.instance().track(name, attributes) + CustomerIO.instance().track(name, properties) } - */ } private fun registerDeviceToken(params: Map) { diff --git a/android/src/main/kotlin/io/customer/customer_io/constant/Keys.kt b/android/src/main/kotlin/io/customer/customer_io/constant/Keys.kt index 81670dc..c2cab20 100644 --- a/android/src/main/kotlin/io/customer/customer_io/constant/Keys.kt +++ b/android/src/main/kotlin/io/customer/customer_io/constant/Keys.kt @@ -1,5 +1,6 @@ package io.customer.customer_io.constant +// TODO: Cleanup this file later when all commented methods are implemented internal object Keys { object Methods { @@ -24,5 +25,8 @@ internal object Keys { const val DELIVERY_ID = "deliveryId" const val DELIVERY_TOKEN = "deliveryToken" const val METRIC_EVENT = "metricEvent" + + const val NAME = "name" + const val PROPERTIES = "properties" } } diff --git a/apps/amiapp_flutter/lib/main.dart b/apps/amiapp_flutter/lib/main.dart index afcda9a..860e2ac 100644 --- a/apps/amiapp_flutter/lib/main.dart +++ b/apps/amiapp_flutter/lib/main.dart @@ -41,7 +41,7 @@ void main() async { onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) async { // Callback from `flutter_local_notifications` plugin for when a local notification is clicked. // Unfortunately, we are only able to get the payload object for the local push, not anything else such as title or body. - CustomerIO.instance.track(name: "local push notification clicked", attributes: {"payload": notificationResponse.payload}); + CustomerIO.instance.track(name: "local push notification clicked", properties: {"payload": notificationResponse.payload}); } ); diff --git a/apps/amiapp_flutter/lib/src/screens/dashboard.dart b/apps/amiapp_flutter/lib/src/screens/dashboard.dart index 1dd34a2..5b4308f 100644 --- a/apps/amiapp_flutter/lib/src/screens/dashboard.dart +++ b/apps/amiapp_flutter/lib/src/screens/dashboard.dart @@ -59,12 +59,12 @@ class _DashboardScreenState extends State { // Setup 3rd party SDK, flutter-fire. // We install this SDK into sample app to make sure the CIO SDK behaves as expected when there is another SDK installed that handles push notifications. FirebaseMessaging.instance.getInitialMessage().then((initialMessage) { - CustomerIO.instance.track(name: "push clicked", attributes: {"push": initialMessage?.notification?.title, "app-state": "killed"}); + CustomerIO.instance.track(name: "push clicked", properties: {"push": initialMessage?.notification?.title, "app-state": "killed"}); }); // ...while app was in the background (but not killed). FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { - CustomerIO.instance.track(name: "push clicked", attributes: {"push": message.notification?.title, "app-state": "background"}); + CustomerIO.instance.track(name: "push clicked", properties: {"push": message.notification?.title, "app-state": "background"}); }); // Important that a 3rd party SDK can receive callbacks when a push is received while app in background. @@ -72,7 +72,7 @@ class _DashboardScreenState extends State { // Note: A push will not be shown on the device while app is in foreground. This is a FCM behavior, not a CIO SDK behavior. // If you send a push using Customer.io with the FCM service setup in Customer.io, the push will be shown on the device. FirebaseMessaging.onMessage.listen((RemoteMessage message) { - CustomerIO.instance.track(name: "push received", attributes: {"push": message.notification?.title, "app-state": "foreground"}); + CustomerIO.instance.track(name: "push received", properties: {"push": message.notification?.title, "app-state": "foreground"}); }); super.initState(); @@ -113,7 +113,7 @@ class _DashboardScreenState extends State { CustomerIO.instance.track( name: 'In-App Event', - attributes: attributes, + properties: attributes, ); } @@ -176,7 +176,7 @@ class _ActionList extends StatelessWidget { if (attributes == null) { CustomerIO.instance.track(name: eventName); } else { - CustomerIO.instance.track(name: eventName, attributes: attributes); + CustomerIO.instance.track(name: eventName, properties: attributes); } context.showSnackBar('Event sent successfully'); } diff --git a/apps/amiapp_flutter/lib/src/screens/events.dart b/apps/amiapp_flutter/lib/src/screens/events.dart index cb79ec5..1f5551b 100644 --- a/apps/amiapp_flutter/lib/src/screens/events.dart +++ b/apps/amiapp_flutter/lib/src/screens/events.dart @@ -112,7 +112,7 @@ class _CustomEventScreenState extends State { : {propertyName: _propertyValueController.text}; CustomerIO.instance.track( name: _eventNameController.text, - attributes: attributes); + properties: attributes); _onEventTracked(); } }, diff --git a/apps/amiapp_flutter/lib/src/screens/settings.dart b/apps/amiapp_flutter/lib/src/screens/settings.dart index 4da705a..36887eb 100644 --- a/apps/amiapp_flutter/lib/src/screens/settings.dart +++ b/apps/amiapp_flutter/lib/src/screens/settings.dart @@ -242,8 +242,7 @@ class _SettingsScreenState extends State { semanticsLabel: 'BQ Seconds Delay Input', hintText: '30', valueController: _flushIntervalValueController, - keyboardType: const TextInputType.numberWithOptions( - decimal: true), + keyboardType: TextInputType.number, validator: (value) { bool isBlank = value?.trim().isNotEmpty != true; if (!isBlank) { diff --git a/ios/Classes/Keys.swift b/ios/Classes/Keys.swift index bc15524..de7738b 100644 --- a/ios/Classes/Keys.swift +++ b/ios/Classes/Keys.swift @@ -1,5 +1,6 @@ import Foundation +// TODO: Cleanup this file later when all commented methods are implemented struct Keys { struct Methods{ @@ -23,6 +24,9 @@ struct Keys { static let deliveryId = "deliveryId" static let deliveryToken = "deliveryToken" static let metricEvent = "metricEvent" + + static let name = "name" + static let properties = "properties" } struct Environment{ diff --git a/ios/Classes/SwiftCustomerIoPlugin.swift b/ios/Classes/SwiftCustomerIoPlugin.swift index bc5e665..c86bab5 100644 --- a/ios/Classes/SwiftCustomerIoPlugin.swift +++ b/ios/Classes/SwiftCustomerIoPlugin.swift @@ -92,21 +92,17 @@ public class SwiftCustomerIoPlugin: NSObject, FlutterPlugin { } private func track(params : Dictionary) { - // TODO: Fix track implementation - /* - guard let name = params[Keys.Tracking.eventName] as? String - else { - return - } - - guard let attributes = params[Keys.Tracking.attributes] as? Dictionary else{ - CustomerIO.shared.track(name: name) - return - } - - CustomerIO.shared.track(name: name, data: attributes) - */ + guard let name = params[Keys.Tracking.name] as? String else { + logger.error("Missing event name in: \(params) for key: \(Keys.Tracking.name)") + return + } + + guard let properties = params[Keys.Tracking.properties] as? Dictionary else { + CustomerIO.shared.track(name: name) + return + } + CustomerIO.shared.track(name: name, properties: properties) } func screen(params : Dictionary) { diff --git a/lib/customer_io.dart b/lib/customer_io.dart index 142bcd8..76cb517 100644 --- a/lib/customer_io.dart +++ b/lib/customer_io.dart @@ -105,10 +105,10 @@ class CustomerIO { /// You may also track events with additional yet optional data. /// /// @param name event name to be tracked - /// @param attributes (Optional) params to be sent with event + /// @param properties (Optional) params to be sent with event void track( - {required String name, Map attributes = const {}}) { - return _platform.track(name: name, attributes: attributes); + {required String name, Map properties = const {}}) { + return _platform.track(name: name, properties: properties); } /// Track a push metric diff --git a/lib/customer_io_const.dart b/lib/customer_io_const.dart index c6ca7a1..8478dc5 100644 --- a/lib/customer_io_const.dart +++ b/lib/customer_io_const.dart @@ -1,3 +1,5 @@ +// TODO: Cleanup this file later when all commented methods are implemented + class MethodConsts { static const String initialize = "initialize"; static const String identify = "identify"; @@ -23,4 +25,7 @@ class TrackingConsts { static const String metricEvent = "metricEvent"; static const String message = "message"; static const String handleNotificationTrigger = "handleNotificationTrigger"; + + static const String name = "name"; + static const String properties = "properties"; } diff --git a/lib/customer_io_method_channel.dart b/lib/customer_io_method_channel.dart index 9510b99..4089c00 100644 --- a/lib/customer_io_method_channel.dart +++ b/lib/customer_io_method_channel.dart @@ -95,11 +95,11 @@ class CustomerIOMethodChannel extends CustomerIOPlatform { @override void track( {required String name, - Map attributes = const {}}) async { + Map properties = const {}}) async { try { final payload = { - TrackingConsts.eventName: name, - TrackingConsts.attributes: attributes + TrackingConsts.name: name, + TrackingConsts.properties: properties }; methodChannel.invokeMethod(MethodConsts.track, payload); } on PlatformException catch (exception) { diff --git a/lib/customer_io_platform_interface.dart b/lib/customer_io_platform_interface.dart index f85f0d5..1e44480 100644 --- a/lib/customer_io_platform_interface.dart +++ b/lib/customer_io_platform_interface.dart @@ -45,7 +45,7 @@ abstract class CustomerIOPlatform extends PlatformInterface { } void track( - {required String name, Map attributes = const {}}) { + {required String name, Map properties = const {}}) { throw UnimplementedError('track() has not been implemented.'); } diff --git a/test/customer_io_method_channel_test.dart b/test/customer_io_method_channel_test.dart index 2552c46..cd92143 100644 --- a/test/customer_io_method_channel_test.dart +++ b/test/customer_io_method_channel_test.dart @@ -76,12 +76,12 @@ void main() { test('track() should call platform method with correct arguments', () async { final Map args = { - 'eventName': 'test_event', - 'attributes': {'eventData': 2} + 'name': 'test_event', + 'properties': {'eventData': 2} }; final customerIO = CustomerIOMethodChannel(); - customerIO.track(name: args['eventName'], attributes: args['attributes']); + customerIO.track(name: args['name'], properties: args['properties']); expectMethodInvocationArguments('track', args); }); diff --git a/test/customer_io_test.dart b/test/customer_io_test.dart index 69476eb..1d7b2ab 100644 --- a/test/customer_io_test.dart +++ b/test/customer_io_test.dart @@ -131,19 +131,19 @@ void main() { test('track() calls platform', () { const name = 'itemAddedToCart'; final attributes = {'item': 'shoes'}; - CustomerIO.instance.track(name: name, attributes: attributes); - verify(mockPlatform.track(name: name, attributes: attributes)) + CustomerIO.instance.track(name: name, properties: attributes); + verify(mockPlatform.track(name: name, properties: attributes)) .called(1); }); test('track() correct arguments are passed', () { const name = 'itemAddedToCart'; final givenAttributes = {'name': 'John Doe'}; - CustomerIO.instance.track(name: name, attributes: givenAttributes); + CustomerIO.instance.track(name: name, properties: givenAttributes); expect( verify(mockPlatform.track( name: captureAnyNamed("name"), - attributes: captureAnyNamed("attributes"), + properties: captureAnyNamed("properties"), )).captured, [name, givenAttributes], ); diff --git a/test/customer_io_test.mocks.dart b/test/customer_io_test.mocks.dart index f306ef0..768a9cd 100644 --- a/test/customer_io_test.mocks.dart +++ b/test/customer_io_test.mocks.dart @@ -81,7 +81,7 @@ class MockTestCustomerIoPlatform extends _i1.Mock @override void track({ required String? name, - Map? attributes = const {}, + Map? properties = const {}, }) => super.noSuchMethod( Invocation.method( @@ -89,7 +89,7 @@ class MockTestCustomerIoPlatform extends _i1.Mock [], { #name: name, - #attributes: attributes, + #properties: properties, }, ), returnValueForMissingStub: null,