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 9daacd5..7f05b29 100644 --- a/android/src/main/kotlin/io/customer/customer_io/CustomerIOPlugin.kt +++ b/android/src/main/kotlin/io/customer/customer_io/CustomerIOPlugin.kt @@ -6,6 +6,7 @@ import androidx.annotation.NonNull import io.customer.customer_io.bridge.NativeModuleBridge import io.customer.customer_io.bridge.nativeMapArgs import io.customer.customer_io.bridge.nativeNoArgs +import io.customer.customer_io.geofence.CustomerIOGeofence import io.customer.customer_io.location.CustomerIOLocation import io.customer.customer_io.messaginginapp.CustomerIOInAppMessaging import io.customer.customer_io.messagingpush.CustomerIOPushMessaging @@ -54,10 +55,15 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { modules = buildList { add(CustomerIOPushMessaging(flutterPluginBinding)) add(CustomerIOInAppMessaging(flutterPluginBinding)) - // Location module is optional - enabled via customerio_location_enabled gradle property + // Location module is optional - enabled via customerio_location_enabled gradle + // property. CIO_LOCATION_ENABLED also covers geofence (geofence implies location). if (BuildConfig.CIO_LOCATION_ENABLED) { add(CustomerIOLocation(flutterPluginBinding)) } + // Geofence module is optional - enabled via customerio_geofence_enabled gradle property + if (BuildConfig.CIO_GEOFENCE_ENABLED) { + add(CustomerIOGeofence(flutterPluginBinding)) + } } // Attach modules to engine @@ -231,15 +237,32 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { ) } } - // Configure location module based on config provided by customer app - args.getAs>(key = "location")?.let { locationConfig -> + // Configure location module. Geofence implies location, so register location + // (with the app's config if given, otherwise defaults) whenever location or + // geofence is configured, since geofence relies on its location fixes. The build + // flags guard each block so the optional bridge classes (which reference native + // artifacts that are only linked when enabled) are never touched otherwise. + val locationConfig = args.getAs>(key = "location") + val geofenceConfig = args.getAs>(key = "geofence") + if (BuildConfig.CIO_LOCATION_ENABLED && (locationConfig != null || geofenceConfig != null)) { modules.filterIsInstance().forEach { it.configureModule( builder = this, - config = locationConfig, + config = locationConfig ?: emptyMap(), ) } } + // Configure geofence module (runs automatically once registered) + if (BuildConfig.CIO_GEOFENCE_ENABLED) { + geofenceConfig?.let { config -> + modules.filterIsInstance().forEach { + it.configureModule( + builder = this, + config = config, + ) + } + } + } }.build() logger.info("Customer.io instance initialized successfully from app") diff --git a/android/src/main/kotlin/io/customer/customer_io/geofence/CustomerIOGeofence.kt b/android/src/main/kotlin/io/customer/customer_io/geofence/CustomerIOGeofence.kt new file mode 100644 index 0000000..80799ad --- /dev/null +++ b/android/src/main/kotlin/io/customer/customer_io/geofence/CustomerIOGeofence.kt @@ -0,0 +1,29 @@ +package io.customer.customer_io.geofence + +import io.customer.customer_io.bridge.NativeModuleBridge +import io.customer.geofence.GeofenceModuleConfig +import io.customer.geofence.ModuleGeofence +import io.customer.sdk.CustomerIOBuilder +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodChannel + +/** + * Flutter bridge for the geofence module. Geofence has no Flutter-facing methods — + * it runs automatically once registered — so this only wires the native module into + * the SDK builder. The reference to [ModuleGeofence] is isolated here so it is loaded + * only when the geofence dependency is bundled. + * + * Geofence depends on the location module; registration of location is handled by the + * plugin when geofence is configured. + */ +internal class CustomerIOGeofence( + pluginBinding: FlutterPlugin.FlutterPluginBinding, +) : NativeModuleBridge { + override val moduleName: String = "Geofence" + override val flutterCommunicationChannel: MethodChannel = + MethodChannel(pluginBinding.binaryMessenger, "customer_io_geofence") + + override fun configureModule(builder: CustomerIOBuilder, config: Map) { + builder.addCustomerIOModule(ModuleGeofence(GeofenceModuleConfig.Builder().build())) + } +} diff --git a/ios/customer_io/Sources/customer_io/CustomerIOPlugin.swift b/ios/customer_io/Sources/customer_io/CustomerIOPlugin.swift index d8483d7..d743084 100644 --- a/ios/customer_io/Sources/customer_io/CustomerIOPlugin.swift +++ b/ios/customer_io/Sources/customer_io/CustomerIOPlugin.swift @@ -6,6 +6,9 @@ import UIKit #if canImport(CioLocation) import CioLocation #endif +#if canImport(CioLocationGeofence) +import CioLocationGeofence +#endif public class CustomerIOPlugin: NSObject, FlutterPlugin { private var methodChannel: FlutterMethodChannel! @@ -167,9 +170,18 @@ public class CustomerIOPlugin: NSObject, FlutterPlugin { let sdkConfigBuilder = try SDKConfigBuilder.create(from: params) #if canImport(CioLocation) - // Add location module to config builder if location config is provided - if let locationConfig = params["location"] as? [String: AnyHashable] { - let trackingModeValue = locationConfig["trackingMode"] as? String + let locationConfig = params["location"] as? [String: AnyHashable] + #if canImport(CioLocationGeofence) + let geofenceConfigured = params["geofence"] as? [String: AnyHashable] != nil + #else + let geofenceConfigured = false + #endif + + // Add location module when location or geofence is configured. Geofence implies + // location: it relies on the location module's fixes, so register location (with + // the app's config if given, otherwise defaults) whenever geofence is enabled. + if locationConfig != nil || geofenceConfigured { + let trackingModeValue = locationConfig?["trackingMode"] as? String let mode: LocationTrackingMode switch trackingModeValue?.uppercased() { case "OFF": @@ -181,6 +193,13 @@ public class CustomerIOPlugin: NSObject, FlutterPlugin { } _ = sdkConfigBuilder.addModule(LocationModule(config: LocationConfig(mode: mode))) } + + #if canImport(CioLocationGeofence) + // Geofence runs automatically once registered; relies on the location module above. + if geofenceConfigured { + _ = sdkConfigBuilder.addModule(GeofenceModule()) + } + #endif #endif CustomerIO.initialize(withConfig: sdkConfigBuilder.build())