From b641fd8b94ab023c8a3577dd47ebdf02e431a4d4 Mon Sep 17 00:00:00 2001 From: Muhammad Rehan Date: Tue, 1 Nov 2022 00:58:47 +0500 Subject: [PATCH] fix: support for expo client --- .../sdk/CustomerIOReactNativeInstance.kt | 23 ++- .../sdk/CustomerIOReactNativeModule.kt | 7 +- .../customer/reactnative/sdk/constant/Keys.kt | 6 + .../sdk/storage/PreferencesStorage.kt | 18 +- customerio-reactnative.podspec | 4 +- ios/CustomerioReactnative.m | 2 +- ios/CustomerioReactnative.swift | 17 +- package.json | 1 + src/CustomerioConfig.tsx | 8 +- src/CustomerioTracking.tsx | 159 ++++++++++-------- 10 files changed, 153 insertions(+), 92 deletions(-) diff --git a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeInstance.kt b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeInstance.kt index 8095dc65..21c6d20b 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeInstance.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeInstance.kt @@ -30,7 +30,7 @@ object CustomerIOReactNativeInstance { context = context, environment = preferencesStorage.loadEnvironmentSettings(), configuration = preferencesStorage.loadConfigurationSettings(), - sdkVersion = preferencesStorage.loadSDKVersion(), + packageConfig = preferencesStorage.loadPackageConfigurations(), ) logger.info("Customer.io instance initialized successfully from preferences") } catch (ex: Exception) { @@ -43,7 +43,7 @@ object CustomerIOReactNativeInstance { context: Context, environment: Map, configuration: Map?, - sdkVersion: String?, + packageConfig: Map?, ): CustomerIO { val siteId = environment.getString(Keys.Environment.SITE_ID) val apiKey = environment.getString(Keys.Environment.API_KEY) @@ -60,7 +60,7 @@ object CustomerIOReactNativeInstance { region = region, appContext = context.applicationContext as Application, ).apply { - setClient(Client.ReactNative(sdkVersion = sdkVersion ?: "n/a")) + setClient(client = getUserAgentClient(packageConfig = packageConfig)) setupConfig(configuration) addCustomerIOModule(module = configureModuleMessagingPushFCM(configuration)) if (!organizationId.isNullOrBlank()) { @@ -69,6 +69,23 @@ object CustomerIOReactNativeInstance { }.build() } + private fun getUserAgentClient(packageConfig: Map?): Client { + val sourceSDK = packageConfig?.getProperty( + Keys.PackageConfig.SOURCE_SDK + )?.takeIfNotBlank() + val sourceSDKVersion = packageConfig?.getProperty( + Keys.PackageConfig.SOURCE_SDK_VERSION + )?.takeIfNotBlank() ?: packageConfig?.getProperty( + Keys.PackageConfig.SOURCE_SDK_VERSION_COMPAT + )?.takeIfNotBlank() ?: "n/a" + return when { + sourceSDK?.equals( + other = "expo", ignoreCase = true, + ) == true -> Client.Expo(sdkVersion = sourceSDKVersion) + else -> Client.ReactNative(sdkVersion = sourceSDKVersion) + } + } + private fun CustomerIO.Builder.setupConfig(config: Map?): CustomerIO.Builder { if (config == null) return this diff --git a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeModule.kt b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeModule.kt index 67ef8a86..b3fe8553 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeModule.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativeModule.kt @@ -39,7 +39,7 @@ class CustomerIOReactNativeModule( fun initialize( environment: ReadableMap, configuration: ReadableMap? = null, - sdkVersion: String? = null, + packageConfiguration: ReadableMap? = null, ) { if (isInstanceValid()) { logger.info("Customer.io instance already initialized, reinitializing") @@ -47,18 +47,19 @@ class CustomerIOReactNativeModule( val env = environment.toMap() val config = configuration?.toMap() + val packageConfig = packageConfiguration?.toMap() preferencesStorage.saveSettings( environment = env, configuration = config, - sdkVersion = sdkVersion + packageConfig = packageConfig ) try { customerIO = CustomerIOReactNativeInstance.initialize( context = reactApplicationContext, environment = env, configuration = config, - sdkVersion = sdkVersion, + packageConfig = packageConfig, ) logger.info("Customer.io instance initialized successfully from app") } catch (ex: Exception) { diff --git a/android/src/main/java/io/customer/reactnative/sdk/constant/Keys.kt b/android/src/main/java/io/customer/reactnative/sdk/constant/Keys.kt index ae5fccd0..5a07ef17 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/constant/Keys.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/constant/Keys.kt @@ -16,4 +16,10 @@ internal object Keys { const val BACKGROUND_QUEUE_MIN_NUMBER_OF_TASKS = "backgroundQueueMinNumberOfTasks" const val BACKGROUND_QUEUE_SECONDS_DELAY = "backgroundQueueSecondsDelay" } + + object PackageConfig { + const val SOURCE_SDK = "source" + const val SOURCE_SDK_VERSION = "version" + const val SOURCE_SDK_VERSION_COMPAT = "sdkVersion" + } } diff --git a/android/src/main/java/io/customer/reactnative/sdk/storage/PreferencesStorage.kt b/android/src/main/java/io/customer/reactnative/sdk/storage/PreferencesStorage.kt index 3a7953a3..c6726e49 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/storage/PreferencesStorage.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/storage/PreferencesStorage.kt @@ -23,6 +23,9 @@ class PreferencesStorage(context: Context) { BACKGROUND_QUEUE_SECONDS_DELAY, ) } + private val packageConfigKeys = with(Keys.PackageConfig) { + arrayOf(SOURCE_SDK, SOURCE_SDK_VERSION) + } init { sharedPref = context.getSharedPreferences( @@ -33,7 +36,7 @@ class PreferencesStorage(context: Context) { fun saveSettings( environment: Map, configuration: Map?, - sdkVersion: String?, + packageConfig: Map?, ) = with(sharedPref.edit()) { for (key in environmentKeys) { putString(key, environment[key]?.toString()?.encodeToBase64()) @@ -43,7 +46,11 @@ class PreferencesStorage(context: Context) { putString(key, configuration[key]?.toString()?.encodeToBase64()) } } - putString(SDK_VERSION_KEY, sdkVersion?.encodeToBase64()) + if (packageConfig != null) { + for (key in packageConfigKeys) { + putString(key, packageConfig[key]?.toString()?.encodeToBase64()) + } + } apply() } @@ -62,7 +69,10 @@ class PreferencesStorage(context: Context) { return map } - fun loadSDKVersion() = sharedPref.getString(SDK_VERSION_KEY, null)?.decodeFromBase64() + fun loadPackageConfigurations() = loadSettings( + keys = packageConfigKeys.plus(Keys.PackageConfig.SOURCE_SDK_VERSION_COMPAT), + ) + fun loadEnvironmentSettings() = loadSettings(environmentKeys) fun loadConfigurationSettings() = loadSettings(configKeys) { key, value -> with(Keys.Config) { @@ -81,8 +91,6 @@ class PreferencesStorage(context: Context) { } companion object { - private const val SDK_VERSION_KEY = "sdkVersion" - private fun String.encodeToBase64(): String { return Base64.encodeToString(toByteArray(Charsets.UTF_8), Base64.NO_WRAP) } diff --git a/customerio-reactnative.podspec b/customerio-reactnative.podspec index 31b1162b..89fea1d8 100644 --- a/customerio-reactnative.podspec +++ b/customerio-reactnative.podspec @@ -16,6 +16,6 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m,mm,swift}" s.dependency "React-Core" - s.dependency "CustomerIOTracking", '~> 1.2.0' - s.dependency "CustomerIOMessagingInApp", '~> 1.2.0' + s.dependency "CustomerIOTracking", '~> 1.2.1' + s.dependency "CustomerIOMessagingInApp", '~> 1.2.1' end diff --git a/ios/CustomerioReactnative.m b/ios/CustomerioReactnative.m index 35f8fdfe..d673728c 100644 --- a/ios/CustomerioReactnative.m +++ b/ios/CustomerioReactnative.m @@ -4,7 +4,7 @@ @interface RCT_EXTERN_MODULE(CustomerioReactnative, NSObject) RCT_EXTERN_METHOD(initialize: (nonnull NSDictionary *) env configData : (NSDictionary *) configData - pversion: (nonnull NSString *) pversion) + packageConfig: (nonnull NSDictionary *) packageConfig) RCT_EXTERN_METHOD(identify: (nonnull NSString *) identifier body : (NSDictionary *) body) diff --git a/ios/CustomerioReactnative.swift b/ios/CustomerioReactnative.swift index a2435f9b..2e1ca352 100644 --- a/ios/CustomerioReactnative.swift +++ b/ios/CustomerioReactnative.swift @@ -13,13 +13,24 @@ class CustomerioReactnative: NSObject { /** Initialize the package before sending any calls to the package */ - @objc(initialize:configData:pversion:) - func initialize(env: Dictionary, configData: Dictionary, pversion: String) -> Void { + @objc(initialize:configData:packageConfig:) + func initialize(env: Dictionary, configData: Dictionary, packageConfig: Dictionary) -> Void { + guard let siteId = env["siteId"] as? String, let apiKey = env["apiKey"] as? String, let region = env["region"] as? String, let organizationId = env["organizationId"] as? String else { return } + + guard let pversion = packageConfig["version"] as? String, let source = packageConfig["source"] as? String else { + return + } + + var sdkSource = SdkWrapperConfig.Source.reactNative + if source.lowercased() == "expo" { + sdkSource = SdkWrapperConfig.Source.expo + } + CustomerIO.initialize(siteId: siteId, apiKey: apiKey, region: Region.getLocation(from: region)) { config in - config._sdkWrapperConfig = SdkWrapperConfig(source: SdkWrapperConfig.Source.reactNative, version: pversion ) + config._sdkWrapperConfig = SdkWrapperConfig(source: sdkSource, version: pversion ) config.autoTrackDeviceAttributes = configData["autoTrackDeviceAttributes"] as! Bool config.logLevel = CioLogLevel.getLogValue(for: configData["logLevel"] as! Int) config.autoTrackPushEvents = configData["autoTrackPushEvents"] as! Bool diff --git a/package.json b/package.json index a5baa68f..dcf627b3 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "types": "lib/typescript/index.d.ts", "react-native": "src/index", "source": "src/index", + "expoVersion": "", "files": [ "src", "lib", diff --git a/src/CustomerioConfig.tsx b/src/CustomerioConfig.tsx index cd8f06b5..b209175e 100644 --- a/src/CustomerioConfig.tsx +++ b/src/CustomerioConfig.tsx @@ -26,7 +26,13 @@ class CustomerIOEnv { organizationId: string = "" } +class PackageConfig { + version: string = "" + source: string = "" +} + export { CustomerioConfig, - CustomerIOEnv + CustomerIOEnv, + PackageConfig } \ No newline at end of file diff --git a/src/CustomerioTracking.tsx b/src/CustomerioTracking.tsx index 886a7f4a..3a1ab8bb 100644 --- a/src/CustomerioTracking.tsx +++ b/src/CustomerioTracking.tsx @@ -1,5 +1,9 @@ import { NativeModules, Platform } from 'react-native'; -import { CustomerioConfig, CustomerIOEnv } from './CustomerioConfig'; +import { + CustomerioConfig, + CustomerIOEnv, + PackageConfig, +} from './CustomerioConfig'; import { Region } from './CustomerioEnum'; var pjson = require('../package.json'); @@ -9,9 +13,8 @@ const LINKING_ERROR = '- You rebuilt the app after installing the package\n' + '- You are not using Expo managed workflow\n'; - /** - * Get CustomerioReactnative native module + * Get CustomerioReactnative native module */ const CustomerioReactnative = NativeModules.CustomerioReactnative ? NativeModules.CustomerioReactnative @@ -24,88 +27,96 @@ const CustomerioReactnative = NativeModules.CustomerioReactnative } ); - class CustomerIO { /** - * To initialize the package using workspace credentials - * such as siteId, APIKey and region as optional. - * - * @param env use CustomerIOEnv class to set environment variables such as siteId, apiKey, region, org id + * To initialize the package using workspace credentials + * such as siteId, APIKey and region as optional. + * + * @param env use CustomerIOEnv class to set environment variables such as siteId, apiKey, region, org id * @param config set config for the package eg trackApiUrl etc - * @returns + * @returns */ - static initialize(env: CustomerIOEnv, config: CustomerioConfig = new CustomerioConfig()) { + static initialize( + env: CustomerIOEnv, + config: CustomerioConfig = new CustomerioConfig() + ) { + let pversion = pjson.version ?? ''; + let expoVersion = pjson.expoVersion ?? ''; + + const packageConfig = new PackageConfig(); + packageConfig.source = 'ReactNative'; + packageConfig.version = pversion; + if (expoVersion != '') { + packageConfig.source = 'Expo'; + packageConfig.version = expoVersion; + } - let pversion = pjson.version ?? "" - return CustomerioReactnative.initialize(env, config, pversion) + return CustomerioReactnative.initialize(env, config, packageConfig); } - /** - * Identify a person using a unique identifier, eg. email id. - * Note that you can identify only 1 profile at a time. In case, multiple - * identifiers are attempted to be identified, then the last identified profile - * will be removed automatically. - * - * @param identifier unique identifier for a profile - * @param body (Optional) data to identify a profile - */ - static identify(identifier: string, body: Object) { - CustomerioReactnative.identify(identifier, body) - } + /** + * Identify a person using a unique identifier, eg. email id. + * Note that you can identify only 1 profile at a time. In case, multiple + * identifiers are attempted to be identified, then the last identified profile + * will be removed automatically. + * + * @param identifier unique identifier for a profile + * @param body (Optional) data to identify a profile + */ + static identify(identifier: string, body: Object) { + CustomerioReactnative.identify(identifier, body); + } - /** - * Call this function to stop identifying a person. - * - * If a profile exists, clearIdentify will stop identifying the profile. - * If no profile exists, request to clearIdentify will be ignored. - */ - static clearIdentify() { - CustomerioReactnative.clearIdentify() - } + /** + * Call this function to stop identifying a person. + * + * If a profile exists, clearIdentify will stop identifying the profile. + * If no profile exists, request to clearIdentify will be ignored. + */ + static clearIdentify() { + CustomerioReactnative.clearIdentify(); + } - /** - * To track user events like loggedIn, addedItemToCart etc. - * You may also track events with additional yet optional data. - * - * @param name event name to be tracked - * @param data (Optional) data to be sent with event - */ - static track(name: string, data : Object) { - CustomerioReactnative.track(name, data) - } + /** + * To track user events like loggedIn, addedItemToCart etc. + * You may also track events with additional yet optional data. + * + * @param name event name to be tracked + * @param data (Optional) data to be sent with event + */ + static track(name: string, data: Object) { + CustomerioReactnative.track(name, data); + } - /** - * Use this function to send custom device attributes - * such as app preferences, timezone etc - * - * @param data device attributes data - */ - static setDeviceAttributes(data : Object) { - CustomerioReactnative.setDeviceAttributes(data) - } + /** + * Use this function to send custom device attributes + * such as app preferences, timezone etc + * + * @param data device attributes data + */ + static setDeviceAttributes(data: Object) { + CustomerioReactnative.setDeviceAttributes(data); + } - /** - * Set custom user profile information such as user preference, specific - * user actions etc - * - * @param data additional attributes for a user profile - */ - static setProfileAttributes(data : Object) { - CustomerioReactnative.setProfileAttributes(data) - } + /** + * Set custom user profile information such as user preference, specific + * user actions etc + * + * @param data additional attributes for a user profile + */ + static setProfileAttributes(data: Object) { + CustomerioReactnative.setProfileAttributes(data); + } - /** - * Track screen events to record the screens a user visits - * - * @param name name of the screen user visited - * @param data (Optional) any additional data to be sent - */ - static screen(name : string, data : Object) { - CustomerioReactnative.screen(name, data) - } + /** + * Track screen events to record the screens a user visits + * + * @param name name of the screen user visited + * @param data (Optional) any additional data to be sent + */ + static screen(name: string, data: Object) { + CustomerioReactnative.screen(name, data); } +} - export { - CustomerIO, - Region - } \ No newline at end of file +export { CustomerIO, Region };