From 55708ccfd75688a456edf3029083a973a34a0169 Mon Sep 17 00:00:00 2001 From: Rehan Date: Mon, 1 Dec 2025 18:21:28 +0500 Subject: [PATCH 1/5] chore: remove old arch listener callbacks --- api-extractor-output/customerio-reactnative.api.md | 2 +- example/src/App.tsx | 2 +- src/customerio-inapp.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api-extractor-output/customerio-reactnative.api.md b/api-extractor-output/customerio-reactnative.api.md index 344e0079..e7ad16db 100644 --- a/api-extractor-output/customerio-reactnative.api.md +++ b/api-extractor-output/customerio-reactnative.api.md @@ -100,7 +100,7 @@ export class CustomerIO { export class CustomerIOInAppMessaging implements NativeInAppSpec { dismissMessage(): void; // (undocumented) - registerEventsListener(listener: (event: InAppMessageEvent) => void): EventSubscription; + registerEventsListener(listener: (event: InAppMessageEvent) => void): EventSubscription | undefined; } // Warning: (ae-forgotten-export) The symbol "NativePushSpec" needs to be exported by the entry point index.d.ts diff --git a/example/src/App.tsx b/example/src/App.tsx index 2a950efb..1b8f73fa 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -113,7 +113,7 @@ export default function App({ appName }: { appName: string }) { // Remove listener once unmounted return () => { - inAppEventListener.remove(); + inAppEventListener?.remove(); }; }; loadFromStorage(); diff --git a/src/customerio-inapp.ts b/src/customerio-inapp.ts index 17a03911..477398dc 100644 --- a/src/customerio-inapp.ts +++ b/src/customerio-inapp.ts @@ -31,7 +31,7 @@ const withNativeModule = (fn: (native: CodegenSpec) => R): R => { class CustomerIOInAppMessaging implements NativeInAppSpec { registerEventsListener( listener: (event: InAppMessageEvent) => void - ): EventSubscription { + ): EventSubscription | undefined { const emitter = (data: any) => { // Convert raw native payload to InAppMessageEvent const event = new InAppMessageEvent( From 618ba94008dec55983a046751020a253d802561b Mon Sep 17 00:00:00 2001 From: Rehan Date: Thu, 27 Nov 2025 14:18:33 +0500 Subject: [PATCH 2/5] chore: remove old arch support android --- android/build.gradle | 31 +--- .../sdk/CustomerIOReactNativePackage.kt | 78 ++++++++++ .../sdk/CustomerIOReactNativePackageImpl.kt | 49 ------ ...oduleImpl.kt => NativeCustomerIOModule.kt} | 81 +++++----- .../sdk/extension/ViewExtensions.kt | 0 .../logging/NativeCustomerIOLoggingModule.kt | 90 +++++++++++ .../NativeCustomerIOLoggingModuleImpl.kt | 50 ------ ...er.kt => InlineInAppMessageViewManager.kt} | 23 ++- .../NativeMessagingInAppModule.kt | 73 +++++++++ ...leImpl.kt => NativeMessagingPushModule.kt} | 146 ++++++++++-------- .../messagingpush/PushMessagingExtensions.kt | 6 + .../sdk/CustomerIOReactNativePackage.kt | 60 ------- .../reactnative/sdk/NativeCustomerIOModule.kt | 58 ------- .../logging/NativeCustomerIOLoggingModule.kt | 46 ------ .../InlineInAppMessageViewManager.kt | 14 -- .../NativeMessagingInAppModule.kt | 31 ---- .../NativeMessagingPushModule.kt | 66 -------- .../customer/reactnative/sdk/util/ArchUtil.kt | 18 --- .../sdk/CustomerIOReactNativePackage.kt | 21 --- .../reactnative/sdk/NativeCustomerIOModule.kt | 74 --------- .../sdk/extension/ViewExtensions.kt | 21 --- .../logging/NativeCustomerIOLoggingModule.kt | 44 ------ .../InlineInAppMessageViewManager.kt | 3 - .../NativeMessagingInAppModule.kt | 52 ------- .../NativeMessagingPushModule.kt | 62 -------- example/android/gradle.properties | 5 - 26 files changed, 386 insertions(+), 816 deletions(-) create mode 100644 android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt delete mode 100644 android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackageImpl.kt rename android/src/main/java/io/customer/reactnative/sdk/{NativeCustomerIOModuleImpl.kt => NativeCustomerIOModule.kt} (77%) rename android/src/{newarch => main/java}/io/customer/reactnative/sdk/extension/ViewExtensions.kt (100%) create mode 100644 android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt delete mode 100644 android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModuleImpl.kt rename android/src/main/java/io/customer/reactnative/sdk/messaginginapp/{BaseInlineInAppMessageViewManager.kt => InlineInAppMessageViewManager.kt} (59%) create mode 100644 android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt rename android/src/main/java/io/customer/reactnative/sdk/messagingpush/{NativeMessagingPushModuleImpl.kt => NativeMessagingPushModule.kt} (67%) delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt delete mode 100644 android/src/newarch/io/customer/reactnative/sdk/util/ArchUtil.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/extension/ViewExtensions.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt delete mode 100644 android/src/oldarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt diff --git a/android/build.gradle b/android/build.gradle index 2178160d..53eb7495 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -15,6 +15,7 @@ buildscript { apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +apply plugin: "com.facebook.react" def getExtOrDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['customerio.reactnative.' + name] @@ -24,32 +25,12 @@ def getExtOrIntegerDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['customerio.reactnative.' + name]).toInteger() } -def isNewArchitectureEnabled() { - // Allow customers to override new architecture setting specifically for Customer.io SDK. - // This is useful as a workaround for apps where the new architecture has issues. - // Customers can add 'customerioNewArchEnabled=false' in their gradle.properties - // to force the SDK to use old architecture even when their app has newArchEnabled=true. - // This can be helpful for customers who are using the new architecture in their app, but are on older - // versions of React Native than the one used by the SDK to generate the codegen files. - if (project.hasProperty("customerioNewArchEnabled")) { - return project.customerioNewArchEnabled == "true" - } - // Otherwise, use react-native's newArchEnabled property. - return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" -} - -if (isNewArchitectureEnabled()) { - apply plugin: "com.facebook.react" -} - android { namespace = 'io.customer.reactnative.sdk' compileSdkVersion getExtOrIntegerDefault('compileSdkVersion') defaultConfig { - def isNewArchEnabled = isNewArchitectureEnabled() minSdkVersion getExtOrIntegerDefault('minSdkVersion') targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') - buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchEnabled.toString()) } buildTypes { @@ -64,16 +45,6 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } - sourceSets { - main { - if (isNewArchitectureEnabled()) { - // Include both new architecture source directories and codegen generated files - java.srcDirs += ['src/newarch'] - } else { - java.srcDirs += ['src/oldarch'] - } - } - } } repositories { diff --git a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt new file mode 100644 index 00000000..569df330 --- /dev/null +++ b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt @@ -0,0 +1,78 @@ +package io.customer.reactnative.sdk + +import com.facebook.react.BaseReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.uimanager.ViewManager +import io.customer.reactnative.sdk.logging.NativeCustomerIOLoggingModule +import io.customer.reactnative.sdk.messaginginapp.InlineInAppMessageViewManager +import io.customer.reactnative.sdk.messaginginapp.NativeMessagingInAppModule +import io.customer.reactnative.sdk.messagingpush.NativeMessagingPushModule +import io.customer.reactnative.sdk.util.assertNotNull + +/** + * React Native package for Customer.io SDK that registers all TurboModules and ViewManagers. + * Implements new architecture support for React Native. + */ +class CustomerIOReactNativePackage : BaseReactPackage() { + /** + * Creates the list of view managers for the Customer.io React Native SDK. + */ + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return listOf(InlineInAppMessageViewManager()) + } + + override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { + // Debugging reveals that this method is never called for ViewManagers. + // But since ReactNative docs recommend overriding it, we do so here for ViewManagers. + // See: https://reactnative.dev/docs/fabric-native-components-introduction?platforms=android#4-write-the-reactwebviewpackage + return when (name) { + InlineInAppMessageViewManager.NAME -> InlineInAppMessageViewManager() + NativeCustomerIOLoggingModule.NAME -> NativeCustomerIOLoggingModule(reactContext) + NativeCustomerIOModule.NAME -> NativeCustomerIOModule(reactContext = reactContext) + NativeMessagingInAppModule.NAME -> NativeMessagingInAppModule(reactContext) + NativeMessagingPushModule.NAME -> NativeMessagingPushModule(reactContext) + else -> assertNotNull(value = null) { "Unknown module name: $name" } + } + } + + /** + * Creates a ReactModuleInfo for React module registration with the given configuration. + * Using positional arguments instead of named arguments as named args break on RN 0.76. + */ + private fun createReactModuleInfo( + name: String, + className: String = name, + canOverrideExistingModule: Boolean = false, + needsEagerInit: Boolean = false, + isCxxModule: Boolean = false, + isTurboModule: Boolean = true, + ) = ReactModuleInfo( + name, + className, + canOverrideExistingModule, + needsEagerInit, + isCxxModule, + isTurboModule, + ) + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + // List of all Fabric ViewManagers and TurboModules registered in this package. + // Used by React Native to identify and instantiate the modules. + val moduleNames: List = listOf( + InlineInAppMessageViewManager.NAME, + NativeCustomerIOLoggingModule.NAME, + NativeCustomerIOModule.NAME, + NativeMessagingInAppModule.NAME, + NativeMessagingPushModule.NAME, + ) + return ReactModuleInfoProvider { + // Register all ViewManagers and TurboModules + moduleNames.associateWith { moduleName -> + createReactModuleInfo(name = moduleName) + } + } + } +} diff --git a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackageImpl.kt b/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackageImpl.kt deleted file mode 100644 index 03d85409..00000000 --- a/android/src/main/java/io/customer/reactnative/sdk/CustomerIOReactNativePackageImpl.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.customer.reactnative.sdk - -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.uimanager.ViewManager -import io.customer.reactnative.sdk.logging.NativeCustomerIOLoggingModule -import io.customer.reactnative.sdk.logging.NativeCustomerIOLoggingModuleImpl -import io.customer.reactnative.sdk.messaginginapp.InlineInAppMessageViewManager -import io.customer.reactnative.sdk.messaginginapp.NativeMessagingInAppModule -import io.customer.reactnative.sdk.messaginginapp.NativeMessagingInAppModuleImpl -import io.customer.reactnative.sdk.messagingpush.NativeMessagingPushModule -import io.customer.reactnative.sdk.messagingpush.NativeMessagingPushModuleImpl -import io.customer.reactnative.sdk.util.assertNotNull - -/** - * Registry and factory for Customer.io React Native SDK modules and view managers. - * Provides centralized module creation for both old and new architecture. - * - * This object serves as the single source of truth for all TurboModule registrations - * and their corresponding factory methods. - */ -internal object CustomerIOReactNativePackageImpl { - val turboModuleNames: List - get() = listOf( - NativeCustomerIOLoggingModuleImpl.NAME, - NativeCustomerIOModuleImpl.NAME, - NativeMessagingInAppModuleImpl.NAME, - NativeMessagingPushModuleImpl.NAME, - ) - - @Suppress("UNCHECKED_CAST") - fun createNativeModule( - reactContext: ReactApplicationContext, - name: String - ): M? = when (name) { - NativeCustomerIOLoggingModuleImpl.NAME -> NativeCustomerIOLoggingModule(reactContext) - NativeCustomerIOModuleImpl.NAME -> NativeCustomerIOModule(reactContext = reactContext) - NativeMessagingInAppModuleImpl.NAME -> NativeMessagingInAppModule(reactContext) - NativeMessagingPushModuleImpl.NAME -> NativeMessagingPushModule(reactContext) - else -> assertNotNull(value = null) { "Unknown module name: $name" } - } as? M - - /** - * Creates the list of view managers for the Customer.io React Native SDK. - */ - fun createViewManagers(reactContext: ReactApplicationContext): List> { - return listOf(InlineInAppMessageViewManager()) - } -} diff --git a/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModuleImpl.kt b/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt similarity index 77% rename from android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModuleImpl.kt rename to android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt index 1282c2d2..5a3d0f4a 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModuleImpl.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt @@ -8,8 +8,8 @@ import io.customer.datapipelines.config.ScreenView import io.customer.reactnative.sdk.constant.Keys import io.customer.reactnative.sdk.extension.getTypedValue import io.customer.reactnative.sdk.extension.toMap -import io.customer.reactnative.sdk.messaginginapp.NativeMessagingInAppModuleImpl -import io.customer.reactnative.sdk.messagingpush.NativeMessagingPushModuleImpl +import io.customer.reactnative.sdk.messaginginapp.NativeMessagingInAppModule +import io.customer.reactnative.sdk.messagingpush.NativeMessagingPushModule import io.customer.reactnative.sdk.util.assertNotNull import io.customer.sdk.CustomerIO import io.customer.sdk.CustomerIOBuilder @@ -22,15 +22,13 @@ import io.customer.sdk.events.TrackMetric import io.customer.sdk.events.serializedName /** - * Shared implementation logic for Customer.io Native SDK communication in React Native. - * Contains actual business logic used by both old and new architecture [NativeCustomerIOModule] classes. - * Handles SDK initialization, user identification, event tracking, and device management. + * React Native module implementation for Customer.io Native SDK + * using TurboModules with new architecture. */ -internal object NativeCustomerIOModuleImpl { - const val NAME = "NativeCustomerIO" - - private val logger: Logger - get() = SDKComponent.logger +class NativeCustomerIOModule( + private val reactContext: ReactApplicationContext, +) : NativeCustomerIOSpec(reactContext) { + private val logger: Logger = SDKComponent.logger // Returns CustomerIO instance if initialized, null otherwise, with configurable failure handling. private inline fun getSDKInstanceOrNull( @@ -45,11 +43,8 @@ internal object NativeCustomerIOModuleImpl { logger.error("CustomerIO SDK is not initialized. Please call initialize() first.") } - fun initialize( - reactContext: ReactApplicationContext, - sdkConfig: ReadableMap?, - promise: Promise? - ) { + + override fun initialize(config: ReadableMap?, args: ReadableMap?, promise: Promise?) { // Skip initialization if already initialized if (getSDKInstanceOrNull() != null) { logger.info("CustomerIO SDK is already initialized. Skipping initialization.") @@ -58,7 +53,7 @@ internal object NativeCustomerIOModuleImpl { } try { - val packageConfig = sdkConfig.toMap() + val packageConfig = config.toMap() val cdpApiKey = packageConfig.getTypedValue( Keys.Config.CDP_API_KEY ) ?: throw IllegalArgumentException("CDP API Key is required to initialize Customer.io") @@ -90,14 +85,14 @@ internal object NativeCustomerIOModuleImpl { // Configure push messaging module based on config provided by customer app packageConfig.getTypedValue>(key = "push").let { pushConfig -> - NativeMessagingPushModuleImpl.addNativeModuleFromConfig( + NativeMessagingPushModule.addNativeModuleFromConfig( builder = this, config = pushConfig ?: emptyMap() ) } // Configure in-app messaging module based on config provided by customer app packageConfig.getTypedValue>(key = "inApp")?.let { inAppConfig -> - NativeMessagingInAppModuleImpl.addNativeModuleFromConfig( + NativeMessagingInAppModule.addNativeModuleFromConfig( builder = this, config = inAppConfig, region = region @@ -113,11 +108,7 @@ internal object NativeCustomerIOModuleImpl { } } - fun clearIdentify() { - requireSDKInstance()?.clearIdentify() - } - - fun identify(params: ReadableMap?) { + override fun identify(params: ReadableMap?) { val userId = params?.getString("userId") val traits = params?.getMap("traits") @@ -129,51 +120,55 @@ internal object NativeCustomerIOModuleImpl { userId?.let { requireSDKInstance()?.identify(userId, traits.toMap()) } ?: run { - requireSDKInstance()?.profileAttributes = traits.toMap() + requireSDKInstance()?.setProfileAttributes(traits.toMap()) } } - fun track(name: String?, properties: ReadableMap?) { + override fun clearIdentify() { + requireSDKInstance()?.clearIdentify() + } + + override fun track(name: String?, properties: ReadableMap?) { val eventName = assertNotNull(name) ?: return requireSDKInstance()?.track(eventName, properties.toMap()) } - fun setDeviceAttributes(attributes: ReadableMap?) { - requireSDKInstance()?.deviceAttributes = attributes.toMap() - } + override fun screen(title: String?, properties: ReadableMap?) { + val screenTitle = assertNotNull(title) ?: return - fun setProfileAttributes(attributes: ReadableMap?) { - requireSDKInstance()?.profileAttributes = attributes.toMap() + requireSDKInstance()?.screen(screenTitle, properties.toMap()) } - fun screen(title: String?, properties: ReadableMap?) { - val screenTitle = assertNotNull(title) ?: return + override fun setProfileAttributes(attributes: ReadableMap?) { + requireSDKInstance()?.setProfileAttributes(attributes.toMap()) + } - requireSDKInstance()?.screen(screenTitle, properties.toMap()) + override fun setDeviceAttributes(attributes: ReadableMap?) { + requireSDKInstance()?.setDeviceAttributes(attributes.toMap()) } - fun registerDeviceToken(token: String?) { + override fun registerDeviceToken(token: String?) { val deviceToken = assertNotNull(token) ?: return requireSDKInstance()?.registerDeviceToken(deviceToken) } - fun trackMetric(deliveryId: String?, deviceToken: String?, eventName: String?) { + override fun trackMetric(deliveryID: String?, deviceToken: String?, event: String?) { try { - if (deliveryId == null || deviceToken == null || eventName == null) { + if (deliveryID == null || deviceToken == null || event == null) { throw IllegalArgumentException("Missing required parameters") } - val event = Metric.values().find { - it.serializedName.equals(eventName, true) + val metric = Metric.values().find { + it.serializedName.equals(event, true) } ?: throw IllegalArgumentException("Invalid metric event name") requireSDKInstance()?.trackMetric( event = TrackMetric.Push( - deliveryId = deliveryId, + deliveryId = deliveryID, deviceToken = deviceToken, - metric = event + metric = metric ) ) } catch (e: Exception) { @@ -181,7 +176,11 @@ internal object NativeCustomerIOModuleImpl { } } - fun deleteDeviceToken() { + override fun deleteDeviceToken() { requireSDKInstance()?.deleteDeviceToken() } + + companion object { + internal const val NAME = "NativeCustomerIO" + } } diff --git a/android/src/newarch/io/customer/reactnative/sdk/extension/ViewExtensions.kt b/android/src/main/java/io/customer/reactnative/sdk/extension/ViewExtensions.kt similarity index 100% rename from android/src/newarch/io/customer/reactnative/sdk/extension/ViewExtensions.kt rename to android/src/main/java/io/customer/reactnative/sdk/extension/ViewExtensions.kt diff --git a/android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt b/android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt new file mode 100644 index 00000000..a2b4fe0d --- /dev/null +++ b/android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt @@ -0,0 +1,90 @@ +package io.customer.reactnative.sdk.logging + +import android.util.Log +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableMap +import io.customer.reactnative.sdk.NativeCustomerIOLoggingSpec +import io.customer.sdk.core.di.SDKComponent +import io.customer.sdk.core.util.CioLogLevel + +/** + * React Native module implementation for Customer.io Logging Native SDK + * using TurboModules with new architecture. + */ +class NativeCustomerIOLoggingModule( + reactContext: ReactApplicationContext, +) : NativeCustomerIOLoggingSpec(reactContext) { + override fun getName(): String = NAME + + // Log event emitter function to send events to React Native layer + private var logEventEmitter: ((ReadableMap) -> Unit)? = null + + /** + * Executes the given block and logs any uncaught exceptions using Android logger to protect + * against unexpected crashes and failures. + */ + private fun runWithTryCatch(action: () -> Unit) { + try { + action() + } catch (ex: Exception) { + // Use Android logger to avoid cyclic calls from internal SDK logging + Log.e("[CIO]", "Error in NativeCustomerIOLoggingModule: ${ex.message}", ex) + } + } + + override fun initialize() { + runWithTryCatch { + super.initialize() + setLogEventEmitter { data -> + emitOnCioLogEvent(data) + } + } + } + + override fun invalidate() { + runWithTryCatch { + cleanupLogEventEmitter() + super.invalidate() + } + } + + private fun cleanupLogEventEmitter() { + runWithTryCatch { + // Clear log dispatcher to prevent memory leaks and further events + SDKComponent.logger.setLogDispatcher(null) + this.logEventEmitter = null + } + } + + // Sets the event emitter function used to send log events to React Native. + private fun setLogEventEmitter(emitter: ((ReadableMap) -> Unit)?) { + if (emitter == null) { + // Clear log dispatcher if emitter is null + cleanupLogEventEmitter() + return + } + + SDKComponent.logger.setLogDispatcher { level, message -> + emitLogEvent(level, message) + } + this.logEventEmitter = emitter + } + + // Converts native SDK log events to React Native compatible format and emits them. + private fun emitLogEvent(level: CioLogLevel, message: String) { + // Defensive check: only emit if emitter is available + val emitter = logEventEmitter ?: return + + val data = buildMap { + put("logLevel", level.name.lowercase()) + put("message", message) + } + + emitter.invoke(Arguments.makeNativeMap(data)) + } + + companion object { + internal const val NAME = "NativeCustomerIOLogging" + } +} diff --git a/android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModuleImpl.kt b/android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModuleImpl.kt deleted file mode 100644 index bc275be8..00000000 --- a/android/src/main/java/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModuleImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.customer.reactnative.sdk.logging - -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.ReadableMap -import io.customer.sdk.core.di.SDKComponent -import io.customer.sdk.core.util.CioLogLevel - -/** - * Shared implementation for Customer.io Logging Native SDK module. - * Handles log event dispatching from the native SDK to React Native layer. - * Contains the actual business logic used by both new and old architecture modules. - */ -object NativeCustomerIOLoggingModuleImpl { - const val NAME = "NativeCustomerIOLogging" - - // Log event emitter function to send events to React Native layer - private var logEventEmitter: ((ReadableMap) -> Unit)? = null - - // Sets the event emitter function used to send log events to React Native. - internal fun setLogEventEmitter(emitter: ((ReadableMap) -> Unit)?) { - // Set up log dispatcher only when first emitter is set - if (emitter != null && logEventEmitter == null) { - SDKComponent.logger.setLogDispatcher { level, message -> - emitLogEvent(level, message) - } - } - - this.logEventEmitter = emitter - } - - // Clears the event emitter, should be called during module cleanup - internal fun invalidate() { - // Clear log dispatcher to prevent memory leaks and further events - SDKComponent.logger.setLogDispatcher(null) - this.logEventEmitter = null - } - - // Converts native SDK log events to React Native compatible format and emits them. - private fun emitLogEvent(level: CioLogLevel, message: String) { - // Defensive check: only emit if emitter is available - val emitter = logEventEmitter ?: return - - val data = buildMap { - put("logLevel", level.name.lowercase()) - put("message", message) - } - - emitter.invoke(Arguments.makeNativeMap(data)) - } -} diff --git a/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/BaseInlineInAppMessageViewManager.kt b/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt similarity index 59% rename from android/src/main/java/io/customer/reactnative/sdk/messaginginapp/BaseInlineInAppMessageViewManager.kt rename to android/src/main/java/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt index d45511b3..c976e0e9 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/BaseInlineInAppMessageViewManager.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt @@ -1,8 +1,12 @@ package io.customer.reactnative.sdk.messaginginapp +import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewManagerDelegate import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.viewmanagers.InlineMessageNativeManagerDelegate +import com.facebook.react.viewmanagers.InlineMessageNativeManagerInterface import io.customer.messaginginapp.ui.bridge.WrapperPlatformDelegate /** @@ -11,12 +15,17 @@ import io.customer.messaginginapp.ui.bridge.WrapperPlatformDelegate * Provides common functionality for both old and new React Native architecture * implementations, including view creation, event handling, and property management. */ -abstract class BaseInlineInAppMessageViewManager : +@ReactModule(name = InlineInAppMessageViewManager.NAME) +class InlineInAppMessageViewManager : + InlineMessageNativeManagerInterface, SimpleViewManager() { + private val delegate = InlineMessageNativeManagerDelegate(this) + override fun getName() = NAME + override fun getDelegate(): ViewManagerDelegate = delegate - override fun createViewInstance(context: ThemedReactContext): ReactInlineInAppMessageView { - return ReactInlineInAppMessageView(context) + override fun createViewInstance(reactContext: ThemedReactContext): ReactInlineInAppMessageView { + return ReactInlineInAppMessageView(reactContext) } /** @@ -25,7 +34,7 @@ abstract class BaseInlineInAppMessageViewManager : * - onSizeChange: Triggered when the size of the inline message changes. * - onStateChange: Triggered when the state of the inline message changes. */ - override fun getExportedCustomDirectEventTypeConstants(): MutableMap? { + override fun getExportedCustomDirectEventTypeConstants(): Map { val customEvents = super.getExportedCustomDirectEventTypeConstants() ?: mutableMapOf() val registerCustomEvent = { eventName: String -> customEvents.put(eventName, mapOf("registrationName" to eventName)) @@ -37,11 +46,11 @@ abstract class BaseInlineInAppMessageViewManager : } @ReactProp(name = "elementId") - fun setElementId(view: ReactInlineInAppMessageView, elementId: String?) { - view.elementId = elementId + override fun setElementId(view: ReactInlineInAppMessageView?, value: String?) { + view?.elementId = value } companion object { - const val NAME = "InlineMessageNative" + internal const val NAME = "InlineMessageNative" } } diff --git a/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt b/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt new file mode 100644 index 00000000..93f062dc --- /dev/null +++ b/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt @@ -0,0 +1,73 @@ +package io.customer.reactnative.sdk.messaginginapp + +import com.facebook.react.bridge.ReactApplicationContext +import io.customer.messaginginapp.MessagingInAppModuleConfig +import io.customer.messaginginapp.ModuleMessagingInApp +import io.customer.messaginginapp.di.inAppMessaging +import io.customer.reactnative.sdk.NativeCustomerIOMessagingInAppSpec +import io.customer.reactnative.sdk.constant.Keys +import io.customer.reactnative.sdk.extension.getTypedValue +import io.customer.sdk.CustomerIO +import io.customer.sdk.CustomerIOBuilder +import io.customer.sdk.core.di.SDKComponent +import io.customer.sdk.data.model.Region + +/** + * React Native module implementation for Customer.io In-App Messaging Native SDK + * using TurboModules with new architecture. + */ +class NativeMessagingInAppModule( + reactContext: ReactApplicationContext, +) : NativeCustomerIOMessagingInAppSpec(reactContext) { + private val inAppMessagingModule: ModuleMessagingInApp? + get() = kotlin.runCatching { CustomerIO.instance().inAppMessaging() }.getOrNull() + + private val inAppEventListener = ReactInAppEventListener.shared + + override fun initialize() { + super.initialize() + inAppEventListener.setEventEmitter { data -> + emitOnInAppEventReceived(data) + } + } + + override fun invalidate() { + inAppEventListener.clearEventEmitter() + super.invalidate() + } + + override fun dismissMessage() { + inAppMessagingModule?.dismissMessage() + } + + companion object { + internal const val NAME = "NativeCustomerIOMessagingInApp" + + /** + * Adds InAppMessaging module to native Android SDK based on configuration provided by customer + * app. + * + * @param builder CustomerIOBuilder instance to add InAppMessaging module + * @param config Configuration provided by customer app for InAppMessaging module + * @param region Region to be used for InAppMessaging module + */ + internal fun addNativeModuleFromConfig( + builder: CustomerIOBuilder, + config: Map, + region: Region + ) { + val siteId = config.getTypedValue(Keys.Config.SITE_ID) + if (siteId.isNullOrBlank()) { + SDKComponent.logger.error("Site ID is required to initialize InAppMessaging module") + return + } + + val module = ModuleMessagingInApp( + MessagingInAppModuleConfig.Builder(siteId = siteId, region = region).apply { + setEventListener(eventListener = ReactInAppEventListener.shared) + }.build(), + ) + builder.addCustomerIOModule(module) + } + } +} diff --git a/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModuleImpl.kt b/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt similarity index 67% rename from android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModuleImpl.kt rename to android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt index bc8398c8..38492da6 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModuleImpl.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt @@ -8,6 +8,7 @@ import android.os.Build import androidx.core.content.ContextCompat import com.facebook.react.bridge.ActivityEventListener import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReadableMap import com.facebook.react.modules.core.PermissionAwareActivity @@ -18,9 +19,11 @@ import io.customer.messagingpush.ModuleMessagingPushFCM import io.customer.messagingpush.config.PushClickBehavior import io.customer.messagingpush.di.pushModuleConfig import io.customer.messagingpush.di.pushTrackingUtil +import io.customer.reactnative.sdk.NativeCustomerIOMessagingPushSpec import io.customer.reactnative.sdk.constant.Keys import io.customer.reactnative.sdk.extension.getTypedValue import io.customer.reactnative.sdk.extension.takeIfNotBlank +import io.customer.reactnative.sdk.util.unsupportedOnAndroid import io.customer.sdk.CustomerIO import io.customer.sdk.CustomerIOBuilder import io.customer.sdk.core.di.SDKComponent @@ -28,23 +31,13 @@ import io.customer.sdk.core.util.Logger import java.util.UUID /** - * Shared implementation for Customer.io Push Messaging Native SDK module. - * Contains the actual business logic used by both new and old architecture modules. + * React Native module implementation for Customer.io Push Messaging Native SDK using + * TurboModules with new architecture. */ -object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener { - const val NAME = "NativeCustomerIOMessagingPush" - - /** - * Copying value os [Manifest.permission.POST_NOTIFICATIONS] as constant so we don't have to - * force newer compile sdk versions - */ - private const val POST_NOTIFICATIONS_PERMISSION_NAME = "android.permission.POST_NOTIFICATIONS" - private const val BUILD_VERSION_CODE_TIRAMISU = 33 - private const val POST_NOTIFICATIONS_PERMISSION_REQUEST = 24676 - +class NativeMessagingPushModule( + private val reactContext: ReactApplicationContext, +) : NativeCustomerIOMessagingPushSpec(reactContext), PermissionListener, ActivityEventListener { private val logger: Logger = SDKComponent.logger - private val pushModuleConfig: MessagingPushModuleConfig - get() = SDKComponent.pushModuleConfig /** * Temporarily holds reference for notification request as the request is dependent on Android @@ -52,40 +45,17 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener */ private var notificationRequestPromise: Promise? = null - /** - * Adds push messaging module to native Android SDK based on the configuration provided by - * customer app. - * - * @param builder instance of CustomerIOBuilder to add push messaging module. - * @param config configuration provided by customer app for push messaging module. - */ - fun addNativeModuleFromConfig( - builder: CustomerIOBuilder, - config: Map - ) { - val androidConfig = config.getTypedValue>(key = "android") ?: emptyMap() - // Prefer `android` object for push configurations as it's more specific to Android - // For common push configurations, use `config` object instead of `android` - - // Default push click behavior is to prevent restart of activity in React Native apps - val pushClickBehavior = androidConfig.getTypedValue(Keys.Config.PUSH_CLICK_BEHAVIOR) - ?.takeIfNotBlank() - ?.let { value -> - runCatching { enumValueOf(value) }.getOrNull() - } ?: PushClickBehavior.ACTIVITY_PREVENT_RESTART + override fun initialize() { + super.initialize() + reactContext.addActivityEventListener(this) + } - val module = ModuleMessagingPushFCM( - moduleConfig = MessagingPushModuleConfig.Builder().apply { - setPushClickBehavior(pushClickBehavior = pushClickBehavior) - }.build(), - ) - builder.addCustomerIOModule(module) + override fun invalidate() { + reactContext.removeActivityEventListener(this) + super.invalidate() } - fun getPushPermissionStatus( - reactContext: ReactContext, - promise: Promise?, - ) { + override fun getPushPermissionStatus(promise: Promise?) { val result = checkPushPermissionStatus(reactContext).toReactNativeResult promise?.resolve(result) } @@ -96,15 +66,11 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener * For newer versions, the permission is requested and the promise is resolved after the request * has been completed. * - * @param pushConfigurationOptions configurations options for push notifications, required for - * iOS only, unused on Android. + * @param options configurations options for push notifications, required for iOS only, + * unused on Android. * @param promise to resolve and return the results. */ - fun showPromptForPushNotifications( - reactContext: ReactContext, - pushConfigurationOptions: ReadableMap?, - promise: Promise?, - ) { + override fun showPromptForPushNotifications(options: ReadableMap?, promise: Promise?) { // Skip requesting permissions when already granted if (checkPushPermissionStatus(reactContext) == PermissionStatus.Granted) { promise?.resolve(PermissionStatus.Granted.toReactNativeResult) @@ -141,8 +107,7 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener * @param message push payload received from FCM. * @param handleNotificationTrigger indicating if the local notification should be triggered. */ - fun onMessageReceived( - reactContext: ReactContext, + override fun onMessageReceived( message: ReadableMap?, handleNotificationTrigger: Boolean, promise: Promise?, @@ -168,12 +133,20 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener } } + override fun trackNotificationResponseReceived(payload: ReadableMap?) { + unsupportedOnAndroid(methodName = "trackNotificationResponseReceived") + } + + override fun trackNotificationReceived(payload: ReadableMap?) { + unsupportedOnAndroid(methodName = "trackNotificationReceived") + } + /** * Get the registered device token for the app. * @returns Promise with device token as a string, or error if no token is * registered or the method fails to fetch token. */ - fun getRegisteredDeviceToken(promise: Promise?) { + override fun getRegisteredDeviceToken(promise: Promise?) { try { // Get the device token from SDK val deviceToken: String? = CustomerIO.instance().registeredDeviceToken @@ -195,13 +168,14 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener /** * Checks current permission of push notification permission */ - private fun checkPushPermissionStatus(reactContext: ReactContext): PermissionStatus = + private fun checkPushPermissionStatus(reactContext: ReactContext): PermissionStatus { // Skip requesting permissions for older versions where not required - if (Build.VERSION.SDK_INT < BUILD_VERSION_CODE_TIRAMISU || ContextCompat.checkSelfPermission( + return if (Build.VERSION.SDK_INT < BUILD_VERSION_CODE_TIRAMISU || ContextCompat.checkSelfPermission( reactContext, POST_NOTIFICATIONS_PERMISSION_NAME, ) == PackageManager.PERMISSION_GRANTED ) PermissionStatus.Granted else PermissionStatus.Denied + } /** * Resolves and clears promise with the provided permission status @@ -212,7 +186,9 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener } override fun onRequestPermissionsResult( - requestCode: Int, permissions: Array, grantResults: IntArray, + requestCode: Int, + permissions: Array, + grantResults: IntArray, ): Boolean = when (requestCode) { POST_NOTIFICATIONS_PERMISSION_REQUEST -> { // If request is cancelled, the result arrays are empty. @@ -256,9 +232,51 @@ object NativeMessagingPushModuleImpl : PermissionListener, ActivityEventListener } } - /** - * Maps native class to react native supported type so the result can be passed on to JS/TS classes. - */ - private val PermissionStatus.toReactNativeResult: Any - get() = this.name.uppercase() + companion object { + const val NAME = "NativeCustomerIOMessagingPush" + + /** + * Copying value os [Manifest.permission.POST_NOTIFICATIONS] as constant so we don't have to + * force newer compile sdk versions + */ + private const val POST_NOTIFICATIONS_PERMISSION_NAME = + "android.permission.POST_NOTIFICATIONS" + private const val BUILD_VERSION_CODE_TIRAMISU = 33 + private const val POST_NOTIFICATIONS_PERMISSION_REQUEST = 24676 + + private val pushModuleConfig: MessagingPushModuleConfig + get() = SDKComponent.pushModuleConfig + + /** + * Adds push messaging module to native Android SDK based on the configuration provided by + * customer app. + * + * @param builder instance of CustomerIOBuilder to add push messaging module. + * @param config configuration provided by customer app for push messaging module. + */ + internal fun addNativeModuleFromConfig( + builder: CustomerIOBuilder, + config: Map + ) { + val androidConfig = + config.getTypedValue>(key = "android") ?: emptyMap() + // Prefer `android` object for push configurations as it's more specific to Android + // For common push configurations, use `config` object instead of `android` + + // Default push click behavior is to prevent restart of activity in React Native apps + val pushClickBehavior = + androidConfig.getTypedValue(Keys.Config.PUSH_CLICK_BEHAVIOR) + ?.takeIfNotBlank() + ?.let { value -> + runCatching { enumValueOf(value) }.getOrNull() + } ?: PushClickBehavior.ACTIVITY_PREVENT_RESTART + + val module = ModuleMessagingPushFCM( + moduleConfig = MessagingPushModuleConfig.Builder().apply { + setPushClickBehavior(pushClickBehavior = pushClickBehavior) + }.build(), + ) + builder.addCustomerIOModule(module) + } + } } diff --git a/android/src/main/java/io/customer/reactnative/sdk/messagingpush/PushMessagingExtensions.kt b/android/src/main/java/io/customer/reactnative/sdk/messagingpush/PushMessagingExtensions.kt index ba886ae7..4bbae8d9 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/messagingpush/PushMessagingExtensions.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/messagingpush/PushMessagingExtensions.kt @@ -50,3 +50,9 @@ internal fun ReadableMap.toFCMRemoteMessage(destination: String): RemoteMessage build() } } + +/** + * Maps native class to react native supported type so the result can be passed on to JS/TS classes. + */ +internal val PermissionStatus.toReactNativeResult: Any + get() = this.name.uppercase() diff --git a/android/src/newarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt b/android/src/newarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt deleted file mode 100644 index 89bf2a09..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.customer.reactnative.sdk - -import com.facebook.react.BaseReactPackage -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.module.model.ReactModuleInfo -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.facebook.react.uimanager.ViewManager -import io.customer.reactnative.sdk.messaginginapp.BaseInlineInAppMessageViewManager -import io.customer.reactnative.sdk.messaginginapp.InlineInAppMessageViewManager - -class CustomerIOReactNativePackage : BaseReactPackage() { - override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return CustomerIOReactNativePackageImpl.createViewManagers(reactContext) - } - - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { - // Debugging reveals that this method is never called for ViewManagers. - // But since ReactNative docs recommend overriding it, we do so here for ViewManagers. - // See: https://reactnative.dev/docs/fabric-native-components-introduction?platforms=android#4-write-the-reactwebviewpackage - return when (name) { - BaseInlineInAppMessageViewManager.NAME -> InlineInAppMessageViewManager() - else -> CustomerIOReactNativePackageImpl.createNativeModule(reactContext, name) - } - } - - /** - * Creates a ReactModuleInfo for React module registration with the given configuration. - * Using positional arguments instead of named arguments as named args break on RN 0.76. - */ - private fun createReactModuleInfo( - name: String, - className: String = name, - canOverrideExistingModule: Boolean = false, - needsEagerInit: Boolean = false, - isCxxModule: Boolean = false, - isTurboModule: Boolean = true, - ) = ReactModuleInfo( - name, - className, - canOverrideExistingModule, - needsEagerInit, - isCxxModule, - isTurboModule, - ) - - override fun getReactModuleInfoProvider() = ReactModuleInfoProvider { - buildMap { - // Register TurboModules - CustomerIOReactNativePackageImpl.turboModuleNames.forEach { moduleName -> - put(moduleName, createReactModuleInfo(name = moduleName)) - } - - // Register ViewManagers - val viewManagerName = BaseInlineInAppMessageViewManager.NAME - val viewManagerInfo = createReactModuleInfo(name = viewManagerName) - put(viewManagerName, viewManagerInfo) - } - } -} diff --git a/android/src/newarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt b/android/src/newarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt deleted file mode 100644 index 4503345a..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.customer.reactnative.sdk - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableMap - -/** - * React Native module implementation for Customer.io Native SDK using using - * Turbo Modules with new architecture. - */ -class NativeCustomerIOModule( - private val reactContext: ReactApplicationContext, -) : NativeCustomerIOSpec(reactContext) { - - override fun initialize(config: ReadableMap?, args: ReadableMap?, promise: Promise?) { - NativeCustomerIOModuleImpl.initialize( - reactContext = reactContext, - sdkConfig = config, - promise = promise, - ) - } - - override fun identify(params: ReadableMap?) { - NativeCustomerIOModuleImpl.identify(params) - } - - override fun clearIdentify() { - NativeCustomerIOModuleImpl.clearIdentify() - } - - override fun track(name: String?, properties: ReadableMap?) { - NativeCustomerIOModuleImpl.track(name, properties) - } - - override fun screen(title: String?, properties: ReadableMap?) { - NativeCustomerIOModuleImpl.screen(title, properties) - } - - override fun setProfileAttributes(attributes: ReadableMap?) { - NativeCustomerIOModuleImpl.setProfileAttributes(attributes) - } - - override fun setDeviceAttributes(attributes: ReadableMap?) { - NativeCustomerIOModuleImpl.setDeviceAttributes(attributes) - } - - override fun registerDeviceToken(token: String?) { - NativeCustomerIOModuleImpl.registerDeviceToken(token) - } - - override fun trackMetric(deliveryID: String?, deviceToken: String?, event: String?) { - NativeCustomerIOModuleImpl.trackMetric(deliveryID, deviceToken, event) - } - - override fun deleteDeviceToken() { - NativeCustomerIOModuleImpl.deleteDeviceToken() - } -} diff --git a/android/src/newarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt b/android/src/newarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt deleted file mode 100644 index 49cc423f..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.customer.reactnative.sdk.logging - -import android.util.Log -import com.facebook.react.bridge.ReactApplicationContext -import io.customer.reactnative.sdk.NativeCustomerIOLoggingSpec - -/** - * React Native module implementation for Customer.io Logging Native SDK - * using TurboModules with new architecture. - */ -class NativeCustomerIOLoggingModule( - reactContext: ReactApplicationContext, -) : NativeCustomerIOLoggingSpec(reactContext) { - override fun getName(): String = NativeCustomerIOLoggingModuleImpl.NAME - - /** - * Executes the given block and logs any uncaught exceptions using Android logger to protect - * against unexpected crashes and failures. - */ - private fun runWithTryCatch(action: () -> Unit) { - try { - action() - } catch (ex: Exception) { - // Use Android logger to avoid cyclic calls from internal SDK logging - Log.e("[CIO]", "Error in NativeCustomerIOLoggingModule: ${ex.message}", ex) - } - } - - override fun initialize() { - runWithTryCatch { - super.initialize() - NativeCustomerIOLoggingModuleImpl.setLogEventEmitter { data -> - emitOnCioLogEvent(data) - } - } - } - - override fun invalidate() { - runWithTryCatch { - NativeCustomerIOLoggingModuleImpl.invalidate() - } - runWithTryCatch { - super.invalidate() - } - } -} diff --git a/android/src/newarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt b/android/src/newarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt deleted file mode 100644 index 6b835b24..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.customer.reactnative.sdk.messaginginapp - -import com.facebook.react.module.annotations.ReactModule -import com.facebook.react.uimanager.ViewManagerDelegate -import com.facebook.react.viewmanagers.InlineMessageNativeManagerDelegate -import com.facebook.react.viewmanagers.InlineMessageNativeManagerInterface - -@ReactModule(name = BaseInlineInAppMessageViewManager.NAME) -class InlineInAppMessageViewManager : BaseInlineInAppMessageViewManager(), - InlineMessageNativeManagerInterface { - private val delegate = InlineMessageNativeManagerDelegate(this) - - override fun getDelegate(): ViewManagerDelegate = delegate -} diff --git a/android/src/newarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt b/android/src/newarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt deleted file mode 100644 index 3024167e..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.customer.reactnative.sdk.messaginginapp - -import com.facebook.react.bridge.ReactApplicationContext -import io.customer.reactnative.sdk.NativeCustomerIOMessagingInAppSpec - -/** - * React Native module implementation for Customer.io In-App Messaging Native SDK - * using TurboModules with new architecture. - */ -class NativeMessagingInAppModule( - reactContext: ReactApplicationContext, -) : NativeCustomerIOMessagingInAppSpec(reactContext) { - private val inAppEventListener: ReactInAppEventListener - get() = NativeMessagingInAppModuleImpl.inAppEventListener - - override fun initialize() { - super.initialize() - inAppEventListener.setEventEmitter { data -> - emitOnInAppEventReceived(data) - } - } - - override fun invalidate() { - inAppEventListener.clearEventEmitter() - super.invalidate() - } - - override fun dismissMessage() { - NativeMessagingInAppModuleImpl.dismissMessage() - } -} diff --git a/android/src/newarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt b/android/src/newarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt deleted file mode 100644 index e230cdda..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.customer.reactnative.sdk.messagingpush - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableMap -import io.customer.reactnative.sdk.NativeCustomerIOMessagingPushSpec -import io.customer.reactnative.sdk.util.unsupportedOnAndroid - -/** - * React Native module implementation for Customer.io Push Messaging Native SDK using - * TurboModules with new architecture. - */ -class NativeMessagingPushModule( - private val reactContext: ReactApplicationContext, -) : NativeCustomerIOMessagingPushSpec(reactContext) { - - override fun initialize() { - super.initialize() - reactContext.addActivityEventListener(NativeMessagingPushModuleImpl) - } - - override fun invalidate() { - reactContext.removeActivityEventListener(NativeMessagingPushModuleImpl) - super.invalidate() - } - - override fun onMessageReceived( - message: ReadableMap?, - handleNotificationTrigger: Boolean, - promise: Promise?, - ) { - NativeMessagingPushModuleImpl.onMessageReceived( - reactContext = reactContext, - message = message, - handleNotificationTrigger = handleNotificationTrigger, - promise = promise, - ) - } - - override fun trackNotificationResponseReceived(payload: ReadableMap?) { - unsupportedOnAndroid(methodName = "trackNotificationResponseReceived") - } - - override fun trackNotificationReceived(payload: ReadableMap?) { - unsupportedOnAndroid(methodName = "trackNotificationReceived") - } - - override fun getRegisteredDeviceToken(promise: Promise?) { - NativeMessagingPushModuleImpl.getRegisteredDeviceToken(promise) - } - - override fun showPromptForPushNotifications(options: ReadableMap?, promise: Promise?) { - NativeMessagingPushModuleImpl.showPromptForPushNotifications( - reactContext = reactContext, - pushConfigurationOptions = options, - promise = promise, - ) - } - - override fun getPushPermissionStatus(promise: Promise?) { - NativeMessagingPushModuleImpl.getPushPermissionStatus( - reactContext = reactContext, - promise = promise, - ) - } -} diff --git a/android/src/newarch/io/customer/reactnative/sdk/util/ArchUtil.kt b/android/src/newarch/io/customer/reactnative/sdk/util/ArchUtil.kt deleted file mode 100644 index a48dfe3e..00000000 --- a/android/src/newarch/io/customer/reactnative/sdk/util/ArchUtil.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.customer.reactnative.sdk.util - -import io.customer.reactnative.sdk.BuildConfig -import io.customer.sdk.core.di.SDKComponent - -/** - * Marks a method that's only implemented for legacy architecture support. - * Throws in debug; logs an error in release builds. - */ -fun onlyForLegacyArch(methodName: String) { - val message = "'$methodName' is not required in the New Architecture and should not be called." - - if (BuildConfig.DEBUG) { - error(message) - } else { - SDKComponent.logger.error(message) - } -} diff --git a/android/src/oldarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt b/android/src/oldarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt deleted file mode 100644 index 8e43a539..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/CustomerIOReactNativePackage.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.customer.reactnative.sdk - -import com.facebook.react.ReactPackage -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.uimanager.ViewManager - -class CustomerIOReactNativePackage : ReactPackage { - override fun createNativeModules(reactContext: ReactApplicationContext): List { - return CustomerIOReactNativePackageImpl.turboModuleNames.mapNotNull { name -> - CustomerIOReactNativePackageImpl.createNativeModule( - reactContext = reactContext, - name = name, - ) - } - } - - override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return CustomerIOReactNativePackageImpl.createViewManagers(reactContext) - } -} diff --git a/android/src/oldarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt b/android/src/oldarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt deleted file mode 100644 index 04b91daf..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/NativeCustomerIOModule.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.customer.reactnative.sdk - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.ReadableMap - -/** - * React Native module implementation for Customer.io Native SDK using using old architecture. - */ -class NativeCustomerIOModule( - reactContext: ReactApplicationContext, -) : ReactContextBaseJavaModule(reactContext) { - override fun getName(): String = NativeCustomerIOModuleImpl.NAME - - @ReactMethod - fun initialize( - configJson: ReadableMap, - @Suppress("UNUSED_PARAMETER") sdkArgs: ReadableMap?, - promise: Promise?, - ) { - NativeCustomerIOModuleImpl.initialize( - reactContext = reactApplicationContext, - sdkConfig = configJson, - promise = promise, - ) - } - - @ReactMethod - fun clearIdentify() { - NativeCustomerIOModuleImpl.clearIdentify() - } - - @ReactMethod - fun identify(params: ReadableMap?) { - NativeCustomerIOModuleImpl.identify(params = params) - } - - @ReactMethod - fun track(name: String, attributes: ReadableMap?) { - NativeCustomerIOModuleImpl.track(name, attributes) - } - - @ReactMethod - fun setDeviceAttributes(attributes: ReadableMap?) { - NativeCustomerIOModuleImpl.setDeviceAttributes(attributes) - } - - @ReactMethod - fun setProfileAttributes(attributes: ReadableMap?) { - NativeCustomerIOModuleImpl.setProfileAttributes(attributes) - } - - @ReactMethod - fun screen(name: String, attributes: ReadableMap?) { - NativeCustomerIOModuleImpl.screen(name, attributes) - } - - @ReactMethod - fun registerDeviceToken(token: String) { - NativeCustomerIOModuleImpl.registerDeviceToken(token) - } - - @ReactMethod - fun trackMetric(deliveryID: String, deviceToken: String, event: String) { - NativeCustomerIOModuleImpl.trackMetric(deliveryID, deviceToken, event) - } - - @ReactMethod - fun deleteDeviceToken() { - NativeCustomerIOModuleImpl.deleteDeviceToken() - } -} diff --git a/android/src/oldarch/io/customer/reactnative/sdk/extension/ViewExtensions.kt b/android/src/oldarch/io/customer/reactnative/sdk/extension/ViewExtensions.kt deleted file mode 100644 index c0aed903..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/extension/ViewExtensions.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.customer.reactnative.sdk.extension - -import android.view.View -import com.facebook.react.bridge.WritableMap -import com.facebook.react.uimanager.events.RCTEventEmitter - -/** - * Extension function to send UI events from native Android views to React JS. - * Old Architecture (Paper) compatible implementation using RCTEventEmitter. - * - * @param eventName The name of the event to be sent to React JS - * @param payload Optional data payload to include with the event - */ -internal fun View.sendUIEventToReactJS( - eventName: String, - payload: WritableMap? = null, -) { - reactContext - .getJSModule(RCTEventEmitter::class.java) - .receiveEvent(id, eventName, payload) -} diff --git a/android/src/oldarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt b/android/src/oldarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt deleted file mode 100644 index ea5906af..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/logging/NativeCustomerIOLoggingModule.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.customer.reactnative.sdk.logging - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.modules.core.DeviceEventManagerModule - -/** - * React Native module implementation for Customer.io Logging Native SDK - * using legacy architecture. - */ -class NativeCustomerIOLoggingModule( - private val reactContext: ReactApplicationContext, -) : ReactContextBaseJavaModule(reactContext) { - override fun getName(): String = NativeCustomerIOLoggingModuleImpl.NAME - - private var listenerCount = 0 - - override fun initialize() { - super.initialize() - NativeCustomerIOLoggingModuleImpl.setLogEventEmitter { data -> - if (listenerCount <= 0) return@setLogEventEmitter - - reactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit("CioLogEvent", data) - } - } - - override fun invalidate() { - NativeCustomerIOLoggingModuleImpl.invalidate() - super.invalidate() - } - - @ReactMethod - fun addListener(eventName: String?) { - listenerCount++ - } - - @ReactMethod - fun removeListeners(count: Double) { - listenerCount -= count.toInt() - } -} diff --git a/android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt b/android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt deleted file mode 100644 index 382b83ba..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/InlineInAppMessageViewManager.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.customer.reactnative.sdk.messaginginapp - -class InlineInAppMessageViewManager : BaseInlineInAppMessageViewManager() diff --git a/android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt b/android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt deleted file mode 100644 index 7c8b8dda..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.customer.reactnative.sdk.messaginginapp - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.modules.core.DeviceEventManagerModule - -/** - * React Native module implementation for Customer.io In-App Messaging Native - * SDK using old architecture. - */ -class NativeMessagingInAppModule( - private val reactContext: ReactApplicationContext, -) : ReactContextBaseJavaModule(reactContext) { - override fun getName(): String = NativeMessagingInAppModuleImpl.NAME - - private var listenerCount = 0 - private val inAppEventListener: ReactInAppEventListener - get() = NativeMessagingInAppModuleImpl.inAppEventListener - - override fun initialize() { - super.initialize() - inAppEventListener.setEventEmitter { data -> - if (listenerCount <= 0) return@setEventEmitter - - reactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit("InAppEventListener", data) - } - } - - override fun invalidate() { - inAppEventListener.clearEventEmitter() - super.invalidate() - } - - @ReactMethod - fun dismissMessage() { - NativeMessagingInAppModuleImpl.dismissMessage() - } - - @ReactMethod - fun addListener(eventName: String?) { - listenerCount++ - } - - @ReactMethod - fun removeListeners(count: Double) { - listenerCount -= count.toInt() - } -} diff --git a/android/src/oldarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt b/android/src/oldarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt deleted file mode 100644 index 7e3db553..00000000 --- a/android/src/oldarch/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +++ /dev/null @@ -1,62 +0,0 @@ -package io.customer.reactnative.sdk.messagingpush - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.ReadableMap - -/** - * React Native module implementation for Customer.io Push Messaging Native SDK using old architecture. - */ -class NativeMessagingPushModule( - private val reactContext: ReactApplicationContext, -) : ReactContextBaseJavaModule(reactContext) { - override fun getName(): String = NativeMessagingPushModuleImpl.NAME - - override fun initialize() { - super.initialize() - reactContext.addActivityEventListener(NativeMessagingPushModuleImpl) - } - - override fun invalidate() { - reactContext.removeActivityEventListener(NativeMessagingPushModuleImpl) - super.invalidate() - } - - @ReactMethod - fun onMessageReceived( - message: ReadableMap?, - handleNotificationTrigger: Boolean, - promise: Promise, - ) { - NativeMessagingPushModuleImpl.onMessageReceived( - reactContext = reactContext, - message = message, - handleNotificationTrigger = handleNotificationTrigger, - promise = promise, - ) - } - - @ReactMethod - fun getRegisteredDeviceToken(promise: Promise) { - NativeMessagingPushModuleImpl.getRegisteredDeviceToken(promise) - } - - @ReactMethod - fun showPromptForPushNotifications(options: ReadableMap?, promise: Promise) { - NativeMessagingPushModuleImpl.showPromptForPushNotifications( - reactContext = reactContext, - pushConfigurationOptions = options, - promise = promise, - ) - } - - @ReactMethod - fun getPushPermissionStatus(promise: Promise) { - NativeMessagingPushModuleImpl.getPushPermissionStatus( - reactContext = reactContext, - promise = promise, - ) - } -} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index f1827141..24ef29be 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -47,8 +47,3 @@ edgeToEdgeEnabled=false # This is useful for testing new features or bug fixes before they are released. # Set to 'local' to use the local version of the SDK. # cioSDKVersionAndroid=local - -# Customer.io SDK allows overriding new architecture setting. -# This is useful as a workaround for apps where the new architecture has issues. -# Set to 'false' to force the SDK to use old architecture even when newArchEnabled=true. -# customerioNewArchEnabled=false From a835779bf8996ad20bd2b38940e0827909a407d6 Mon Sep 17 00:00:00 2001 From: Rehan Date: Tue, 23 Dec 2025 01:28:28 +0500 Subject: [PATCH 3/5] post rebase --- .../sdk/messaginginapp/NativeMessagingInAppModule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt b/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt index 93f062dc..fbd2cc85 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt @@ -22,7 +22,7 @@ class NativeMessagingInAppModule( private val inAppMessagingModule: ModuleMessagingInApp? get() = kotlin.runCatching { CustomerIO.instance().inAppMessaging() }.getOrNull() - private val inAppEventListener = ReactInAppEventListener.shared + private val inAppEventListener = ReactInAppEventListener.instance override fun initialize() { super.initialize() @@ -64,7 +64,7 @@ class NativeMessagingInAppModule( val module = ModuleMessagingInApp( MessagingInAppModuleConfig.Builder(siteId = siteId, region = region).apply { - setEventListener(eventListener = ReactInAppEventListener.shared) + setEventListener(eventListener = ReactInAppEventListener.instance) }.build(), ) builder.addCustomerIOModule(module) From 025f9eca9bf3f172882634851cf16de6bd913d77 Mon Sep 17 00:00:00 2001 From: Rehan Date: Tue, 23 Dec 2025 01:47:20 +0500 Subject: [PATCH 4/5] Revert "chore: remove old arch listener callbacks" This reverts commit 55708ccfd75688a456edf3029083a973a34a0169. --- api-extractor-output/customerio-reactnative.api.md | 2 +- example/src/App.tsx | 2 +- src/customerio-inapp.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api-extractor-output/customerio-reactnative.api.md b/api-extractor-output/customerio-reactnative.api.md index e7ad16db..344e0079 100644 --- a/api-extractor-output/customerio-reactnative.api.md +++ b/api-extractor-output/customerio-reactnative.api.md @@ -100,7 +100,7 @@ export class CustomerIO { export class CustomerIOInAppMessaging implements NativeInAppSpec { dismissMessage(): void; // (undocumented) - registerEventsListener(listener: (event: InAppMessageEvent) => void): EventSubscription | undefined; + registerEventsListener(listener: (event: InAppMessageEvent) => void): EventSubscription; } // Warning: (ae-forgotten-export) The symbol "NativePushSpec" needs to be exported by the entry point index.d.ts diff --git a/example/src/App.tsx b/example/src/App.tsx index 1b8f73fa..2a950efb 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -113,7 +113,7 @@ export default function App({ appName }: { appName: string }) { // Remove listener once unmounted return () => { - inAppEventListener?.remove(); + inAppEventListener.remove(); }; }; loadFromStorage(); diff --git a/src/customerio-inapp.ts b/src/customerio-inapp.ts index 477398dc..17a03911 100644 --- a/src/customerio-inapp.ts +++ b/src/customerio-inapp.ts @@ -31,7 +31,7 @@ const withNativeModule = (fn: (native: CodegenSpec) => R): R => { class CustomerIOInAppMessaging implements NativeInAppSpec { registerEventsListener( listener: (event: InAppMessageEvent) => void - ): EventSubscription | undefined { + ): EventSubscription { const emitter = (data: any) => { // Convert raw native payload to InAppMessageEvent const event = new InAppMessageEvent( From b20e27412436082a6694bb3b4f0c523fe3336808 Mon Sep 17 00:00:00 2001 From: Rehan Date: Tue, 23 Dec 2025 13:05:41 +0500 Subject: [PATCH 5/5] make logger lazy --- .../java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt | 3 ++- .../reactnative/sdk/messagingpush/NativeMessagingPushModule.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt b/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt index 5a3d0f4a..4e1cba09 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/NativeCustomerIOModule.kt @@ -28,7 +28,8 @@ import io.customer.sdk.events.serializedName class NativeCustomerIOModule( private val reactContext: ReactApplicationContext, ) : NativeCustomerIOSpec(reactContext) { - private val logger: Logger = SDKComponent.logger + private val logger: Logger + get() = SDKComponent.logger // Returns CustomerIO instance if initialized, null otherwise, with configurable failure handling. private inline fun getSDKInstanceOrNull( diff --git a/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt b/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt index 38492da6..9633511c 100644 --- a/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +++ b/android/src/main/java/io/customer/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt @@ -37,7 +37,8 @@ import java.util.UUID class NativeMessagingPushModule( private val reactContext: ReactApplicationContext, ) : NativeCustomerIOMessagingPushSpec(reactContext), PermissionListener, ActivityEventListener { - private val logger: Logger = SDKComponent.logger + private val logger: Logger + get() = SDKComponent.logger /** * Temporarily holds reference for notification request as the request is dependent on Android