diff --git a/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt b/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt index 55e9724..8e3aa6e 100644 --- a/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt @@ -26,6 +26,7 @@ import com.mparticle.commerce.* import com.mparticle.consent.CCPAConsent import com.mparticle.consent.ConsentState import com.mparticle.consent.GDPRConsent +import com.mparticle.internal.Logger import com.mparticle.rokt.CacheConfig import com.mparticle.rokt.RoktConfig import com.mparticle.rokt.RoktEmbeddedView @@ -235,6 +236,7 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware result.success(true) } "roktSelectPlacements" -> this.roktSelectPlacements(call, result) + "roktSelectShoppableAds" -> this.roktSelectShoppableAds(call, result) "roktPurchaseFinalized" -> this.roktPurchaseFinalized(call, result) else -> { result.notImplemented() @@ -806,6 +808,12 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware } } + private fun roktSelectShoppableAds(call: MethodCall, result: Result) { + // Parity with RN bridge: Android API is exposed but not implemented yet. + Logger.warning("selectShoppableAds is not yet supported on Android") + result.success(true) + } + private fun String.toColorMode(): RoktConfig.ColorMode = when (this) { "dark" -> RoktConfig.ColorMode.DARK diff --git a/ios/Classes/SwiftMparticleFlutterSdkPlugin.swift b/ios/Classes/SwiftMparticleFlutterSdkPlugin.swift index 72f8048..a02658b 100644 --- a/ios/Classes/SwiftMparticleFlutterSdkPlugin.swift +++ b/ios/Classes/SwiftMparticleFlutterSdkPlugin.swift @@ -561,6 +561,27 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin { print("Incorrect argument for \(call.method) iOS method") result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing placementId or catalogItemId or success", details: nil)) } + case "roktSelectShoppableAds": + if let callArguments = call.arguments as? [String: Any], + let identifier = callArguments["identifier"] as? String { + let attributes = callArguments["attributes"] as? [String: String] ?? [:] + var roktConfig: RoktConfig? + if let configMap = callArguments["config"] as? [String: Any] { + roktConfig = buildRoktConfig(configMap: configMap) + } + + roktEventHandler.subscribeToEvents(identifier: identifier) + MParticle.sharedInstance().rokt.selectShoppableAds( + identifier, + attributes: attributes, + config: roktConfig, + onEvent: nil + ) + result(true) + } else { + print("Incorrect argument for \(call.method) iOS method") + result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing identifier", details: nil)) + } default: print("mParticle flutter SDK for iOS does not support \(call.method)") } diff --git a/ios/mparticle_flutter_sdk.podspec b/ios/mparticle_flutter_sdk.podspec index b113d0d..a0d5366 100644 --- a/ios/mparticle_flutter_sdk.podspec +++ b/ios/mparticle_flutter_sdk.podspec @@ -15,10 +15,8 @@ mParticle Flutter Wrapper s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - # SDK 9.0 split requires both umbrella and ObjC pods. + # SDK 9.0 umbrella pod pulls required transitive dependencies. s.dependency 'mParticle-Apple-SDK', '~> 9.0' - s.dependency 'mParticle-Apple-SDK-ObjC', '~> 9.0' - s.dependency 'RoktContracts', '~> 0.1' s.platform = :ios, '15.6' # Flutter.framework does not contain a i386 slice. diff --git a/lib/mparticle_flutter_sdk.dart b/lib/mparticle_flutter_sdk.dart index 39b51f3..b629ca0 100644 --- a/lib/mparticle_flutter_sdk.dart +++ b/lib/mparticle_flutter_sdk.dart @@ -331,6 +331,24 @@ class Rokt { return await _channel.invokeMethod('roktSelectPlacements', params); } + /// Selects shoppable ads with a [identifier], optional [attributes], and optional [roktConfig]. + /// + /// This method is currently implemented only on iOS. + /// + /// Android keeps a no-op bridge for API compatibility, and web does not + /// implement this method yet. + Future selectShoppableAds({ + required String identifier, + Map? attributes, + RoktConfig? roktConfig, + }) async { + return await _channel.invokeMethod('roktSelectShoppableAds', { + 'identifier': identifier, + 'attributes': attributes, + 'config': _roktConfigToMap(config: roktConfig), + }); + } + /// Notifies Rokt that a purchase has been finalized /// /// Use this method to inform Rokt that a purchase has been completed or failed diff --git a/test/mparticle_flutter_sdk_test.dart b/test/mparticle_flutter_sdk_test.dart index 39a06f0..e27c27f 100644 --- a/test/mparticle_flutter_sdk_test.dart +++ b/test/mparticle_flutter_sdk_test.dart @@ -502,5 +502,33 @@ void main() { 'success': true, })); }); + + test('rokt select shoppable ads', () async { + final roktConfig = RoktConfig( + colorMode: ColorMode.dark, + cacheConfig: CacheConfig( + cacheDurationInSeconds: 100, + cacheAttributes: {'key1': 'value1'})); + + await mp.rokt.selectShoppableAds( + identifier: 'identifier1', + attributes: {'attr1': 'val1'}, + roktConfig: roktConfig, + ); + + expect( + methodCall, + isMethodCall('roktSelectShoppableAds', arguments: { + 'identifier': 'identifier1', + 'attributes': {'attr1': 'val1'}, + 'config': { + 'colorMode': 'dark', + 'cacheConfig': { + 'cacheDurationInSeconds': 100, + 'cacheAttributes': {'key1': 'value1'} + } + }, + })); + }); }); }