diff --git a/README.md b/README.md index 8deb4e96..7e71946c 100644 --- a/README.md +++ b/README.md @@ -27,60 +27,59 @@ This repository is a monorepo. It contains a collection of Adobe Experience Plat First, make sure that `Flutter` is [installed](https://docs.flutter.dev/get-started/install). -Now to install the package, run: +### Installing using Terminal: + +Install the package, run: ```bash cd MyFlutterApp flutter pub add flutter_{plugin_name} ``` -This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get): +This will automatically update your package's pubspec.yaml with the dependency, and run an implicit `flutter pub get`. + +### Installing Manually: + +Alternatively, Editing pubspec.yaml manually with dependencies. ``` dependencies: flutter_{plugin_name}: ^{latest_version} ``` - -Now import the plugin in your Dart code as follows: +Run: ``` -import 'package:flutter_{extension}/flutter_{plugin_name}.dart' +flutter pub get ``` -Install instructions for each respective plugin can be found in each plugin's readme: `/plugins/{plugin_name}/README.md`. Start by installing `flutter_aepcore` which is a dependency for all other extensions. - -## Usage - -### Initializing - -Initializing the SDK should be done in native code (AppDelegate / SceneDelegate for iOS and Application class for Android). Documentation for initializing the SDK can be found [here](https://developer.adobe.com/client-sdks/documentation/getting-started/get-the-sdk/#2-add-initialization-code). The linked documentation initalizes the User Profile extension which is not required or supported in Flutter. - -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. +#### iOS development -#### iOS: +For iOS development, after installing the plugin packages, download the pod dependencies by running the following command to link the libraries to your Xcode project : -Add the initialization code in [AppDelegate.m or AppDelegate.swift](/example/ios/Runner/AppDelegate.m#L9) file of the generated iOS project. +```bash +cd ios && pod install && cd .. +``` +To update native dependencies to latest available versions, run the following command: -#### Android: -Create an [Application class](/example/android/app/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepsdk_example/MyApplication.java) which extends [FlutterApplication](https://api.flutter.dev/javadoc/io/flutter/app/FlutterApplication.html) and add the initialization code. Change your [AndroidManifest.xml](/example/android/app/src/main/AndroidManifest.xml#L9) to reference this new class. +```bash +cd ios && pod update && cd .. +``` +## Importing the Plugin -Once you have added the initialization code to your app, be sure to set the SDK wrapper type to Flutter before you start the SDK. +For both installation methods, you need to import the package in your **Dart** code as follows: -###### iOS: -Swift: -```swift -MobileCore.setWrapperType(.flutter) ``` - -Objective-C: -```objective-c -[AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; +import 'package:flutter_{extension}/flutter_{plugin_name}.dart' ``` -###### Android: -```java -MobileCore.setWrapperType(WrapperType.FLUTTER); -``` +## Initializing + +Then, initialize the SDK using the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) + +> [!NOTE] +> Starting from Adobe Experience Platform Flutter **5.x**, there is no longer a need to initialize the SDK on the [native platforms](https://github.com/adobe/aepsdk_flutter/tree/v4.x?tab=readme-ov-file#usage), as was required in earlier versions. ## Tests diff --git a/docs/migration.md b/docs/migration.md index 8fa6aa50..39a9c73b 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -11,7 +11,7 @@ Update your `pubspec.yml` file to point to the new plugin as so: dependencies: - flutter_acpcore: ^2.0.0 -+ flutter_aepcore: ^4.0.0 ++ flutter_aepcore: ^5.0.0 ... ``` @@ -27,135 +27,53 @@ Updated plugins can be found in this repository under [plugins/](https://github. | Place Services| NA | | Place Monitor | NA | -## Update SDK initialization - -Remove the deprecated registration code and the extensions that are not supported in AEP Flutter libraries. - -### Android +## Update import plugins ```diff -import com.adobe.marketing.mobile.AdobeCallback; -import com.adobe.marketing.mobile.Identity; -import com.adobe.marketing.mobile.InvalidInitException; -import com.adobe.marketing.mobile.Lifecycle; -import com.adobe.marketing.mobile.LoggingMode; -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Signal; -import com.adobe.marketing.mobile.Assurance; -import com.adobe.marketing.mobile.UserProfile; -... -import android.app.Application; -import io.flutter.app.FlutterApplication; -... -public class MyApplication extends FlutterApplication { -... - @Override - public void on Create(){ - super.onCreate(); - ... - MobileCore.setApplication(this); - MobileCore.setLogLevel(LoggingMode.DEBUG); - MobileCore.setWrapperType(WrapperType.FLUTTER); - -- try { -- Identity.registerExtension(); -- Lifecycle.registerExtension(); -- Signal.registerExtension(); -- Assurance.registerExtension(); -- UserProfile.registerExtension(); -- Analytics.registerExtension(); -- Places.registerExtension(); -- MobileCore.start(new AdobeCallback () { -- @Override -- public void call(Object o) { -- MobileCore.configureWithAppID("yourAppID"); -- } -- }); -- } catch (InvalidInitException e) { - - List> extensions = Arrays.asList( - Identity.EXTENSION, - Lifecycle.EXTENSION, - Signal.EXTENSION, - Assurance.EXTENSION, - UserProfile.EXTENSION - ); - MobileCore.registerExtensions(extensions, o -> MobileCore.configureWithAppID("YourEnvironmentFileID")); - ... - } - } -} +- import 'package:flutter_acpcore/flutter_acpcore.dart'; ++ import 'package:flutter_aepcore/flutter_aepcore.dart'; ``` -### iOS -> Note: For iOS app, after installing the AEP-prefixed packages, please update native dependecies by running the following command: `cd ios && pod update && cd ..` -```objectivec -// 1. remove the following header files -//#import "ACPCore.h" -//#import "ACPUserProfile.h" -//#import "ACPIdentity.h" -//#import "ACPLifecycle.h" -//#import "ACPSignal.h" +## Update SDK initialization -// 2. import AEP extensions -@import AEPCore; -@import AEPUserProfile; -@import AEPLifecycle; -@import AEPIdentity; -@import AEPServices; -@import AEPSignal; -@import AEPAssurance; -// --- 2. end ---- +> [!NOTE] + > Starting from Adobe Experience Platform Flutter **5.x**, there is no longer a need to initialize the SDK on the [native platforms](https://github.com/adobe/aepsdk_flutter/tree/v4.x?tab=readme-ov-file#usage), as was required in earlier versions. -... -@implementation AppDelegate --(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // 3. remove the following code for initializing ACP SDKs - - // [ACPCore setLogLevel:ACPMobileLogLevelDebug]; - // [ACPCore configureWithAppId:@"yourAppID"]; - // [ACPUserProfile registerExtension]; - // [ACPIdentity registerExtension]; - // [ACPLifecycle registerExtension]; - // [ACPSignal registerExtension]; - // [ACPAnalytics registerExtension]; - - // const UIApplicationState appState = application.applicationState; - // [ACPCore start:^{ - // if (appState != UIApplicationStateBackground) { - // [ACPCore lifecycleStart:nil]; - // } - // }]; - - // 4. add code to initializing AEP SDKs - - [AEPMobileCore setLogLevel: AEPLogLevelDebug]; - [AEPMobileCore configureWithAppId:@"yourAppID"]; - - const UIApplicationState appState = application.applicationState; - - [AEPMobileCore registerExtensions: @[ - AEPMobileLifecycle.class, - AEPMobileSignal.class, - AEPMobileIdentity.class, - AEPMobileUserProfile.class, - AEPMobileAssurance.class, - ] completion:^{ - if (appState != UIApplicationStateBackground) { - [AEPMobileCore lifecycleStart:nil}]; - } - }]; - // --- 4. end ---- - - ... - return YES; -} +Remove all the ACP registration code and the extensions code on the native `Android` and `iOS` platforms. + +Initialize AEP SDK in the **Dart** application: + +**Example** + +```dart +class _HomePageState extends State { + /// Initialize the Adobe Experience Platform Mobile SDK inside the initState method. + @override + void initState() { + super.initState(); + _initializeAEPMobileSdk(); + } + + Future _initializeAEPMobileSdk() async { + MobileCore.setLogLevel(LogLevel.trace); + MobileCore.initializeWithAppId(appId:"YOUR_APP_ID"); + + // For more granular control over the initial options, you can use the following sample code: + // InitOptions initOptions = InitOptions( + // appId: "YOUR_APP_ID", + // lifecycleAutomaticTrackingEnabled: true, + // lifecycleAdditionalContextData: {"key": "value"}, + // appGroupIOS: "group.com.example", + // ); -@end + // MobileCore.initialize(initOptions: initOptions); + } ``` + Refer to the initializing details info [here](https://github.com/adobe/aepsdk_flutter/tree/main?tab=readme-ov-file#initializing). + ## Update API usage and references for each extension ### Core diff --git a/example/README.md b/example/README.md index 71ba2007..371d01af 100644 --- a/example/README.md +++ b/example/README.md @@ -11,7 +11,7 @@ A few resources to get you started if this is your first Flutter project: - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) -For help getting started with Flutter, view our +For help getting started with Flutter, view the [online documentation](https://flutter.dev/docs), which offers tutorials, samples, guidance on mobile development, and a full API reference. @@ -19,9 +19,7 @@ samples, guidance on mobile development, and a full API reference. ## How to run the example app: ### Add your App Id: -In `ios/Runner/AppDelegate.m`, find the call to `configureWithAppId` and add your app id. - -In `android/**/MyApplication.java`, find the call to `configureWithAppId` and add your app id. +In `lib/main.dart`, locate the call to `MobileCore.initializeWithAppId(appId:"YOUR_APP_ID")` and replace `"YOUR_APP_ID"` with your property App Id. #### Run instructions for Android: diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 2060fa4f..b04a7983 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdk 34 namespace 'com.adobe.marketing.mobile.flutter.flutter_aepsdk_example' diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index b15f762a..4428352b 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,6 @@ additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - diff --git a/example/android/app/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepsdk_example/MyApplication.java b/example/android/app/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepsdk_example/MyApplication.java deleted file mode 100644 index ebc70f35..00000000 --- a/example/android/app/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepsdk_example/MyApplication.java +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -package com.adobe.marketing.mobile.flutter.flutter_aepsdk_example; - -import android.app.Activity; -import android.os.Bundle; - -import com.adobe.marketing.mobile.Assurance; -import com.adobe.marketing.mobile.Edge; -import com.adobe.marketing.mobile.Extension; -import com.adobe.marketing.mobile.Lifecycle; -import com.adobe.marketing.mobile.LoggingMode; -import com.adobe.marketing.mobile.Messaging; -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Signal; -import com.adobe.marketing.mobile.UserProfile; -import com.adobe.marketing.mobile.WrapperType; -import com.adobe.marketing.mobile.edge.consent.Consent; -import com.adobe.marketing.mobile.edge.bridge.EdgeBridge; - -import java.util.Arrays; -import java.util.List; - -import io.flutter.app.FlutterApplication; - -public class MyApplication extends FlutterApplication { - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - private final String ENVIRONMENT_FILE_ID = "YOUR-APP-ID"; - - @Override - public void onCreate() { - super.onCreate(); - - MobileCore.setApplication(this); - MobileCore.setLogLevel(LoggingMode.VERBOSE); - MobileCore.setWrapperType(WrapperType.FLUTTER); - - List> extensions = Arrays.asList( - com.adobe.marketing.mobile.edge.identity.Identity.EXTENSION, - com.adobe.marketing.mobile.Identity.EXTENSION, - EdgeBridge.EXTENSION, - Lifecycle.EXTENSION, - Signal.EXTENSION, - Edge.EXTENSION, - Assurance.EXTENSION, - Consent.EXTENSION, - UserProfile.EXTENSION, - Messaging.EXTENSION - ); - MobileCore.registerExtensions(extensions, o -> MobileCore.configureWithAppID(ENVIRONMENT_FILE_ID)); - - registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { /*no-op*/ } - - @Override - public void onActivityStarted(Activity activity) { /*no-op*/ } - - @Override - public void onActivityResumed(Activity activity) { - MobileCore.setApplication(MyApplication.this); - MobileCore.lifecycleStart(null); - } - - @Override - public void onActivityPaused(Activity activity) { - MobileCore.lifecyclePause(); - } - - @Override - public void onActivityStopped(Activity activity) { /*no-op*/ } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { /*no-op*/ } - - @Override - public void onActivityDestroyed(Activity activity) { /*no-op*/ } - }); - - } -} diff --git a/example/android/build.gradle b/example/android/build.gradle index ebaa36d2..81cb4849 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,13 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' - } -} allprojects { repositories { diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14fb..d72a278a 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory +plugins { + id "com.android.application" version "8.2.2" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false + id "dev.flutter.flutter-plugin-loader" version "1.0.0" } + +include ":app" \ No newline at end of file diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 4f8d4d24..8c6e5614 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3900ca46..998c3d04 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,12 +1,12 @@ PODS: - - AEPAssurance (5.0.0): + - AEPAssurance (5.0.1): - AEPCore (< 6.0.0, >= 5.0.0) - AEPServices (< 6.0.0, >= 5.0.0) - - AEPCore (5.0.0): + - AEPCore (5.4.0): - AEPRulesEngine (< 6.0.0, >= 5.0.0) - - AEPServices (< 6.0.0, >= 5.0.0) - - AEPEdge (5.0.1): - - AEPCore (< 6.0.0, >= 5.0.0) + - AEPServices (< 6.0.0, >= 5.4.0) + - AEPEdge (5.0.3): + - AEPCore (< 6.0.0, >= 5.3.1) - AEPEdgeIdentity (< 6.0.0, >= 5.0.0) - AEPEdgeBridge (5.0.0): - AEPCore (< 6.0.0, >= 5.0.0) @@ -15,48 +15,48 @@ PODS: - AEPEdge (< 6.0.0, >= 5.0.0) - AEPEdgeIdentity (5.0.0): - AEPCore (< 6.0.0, >= 5.0.0) - - AEPIdentity (5.0.0): - - AEPCore (< 6.0.0, >= 5.0.0) - - AEPLifecycle (5.0.0): - - AEPCore (< 6.0.0, >= 5.0.0) - - AEPMessaging (5.0.0): - - AEPCore (< 6.0.0, >= 5.0.0) - - AEPEdge (< 6.0.0, >= 5.0.0) + - AEPIdentity (5.4.0): + - AEPCore (< 6.0.0, >= 5.4.0) + - AEPLifecycle (5.4.0): + - AEPCore (< 6.0.0, >= 5.4.0) + - AEPMessaging (5.6.0): + - AEPCore (< 6.0.0, >= 5.3.0) + - AEPEdge (< 6.0.0, >= 5.0.2) - AEPEdgeIdentity (< 6.0.0, >= 5.0.0) - - AEPServices (< 6.0.0, >= 5.0.0) + - AEPServices (< 6.0.0, >= 5.3.0) - AEPRulesEngine (5.0.0) - - AEPServices (5.0.0) - - AEPSignal (5.0.0): - - AEPCore (< 6.0.0, >= 5.0.0) + - AEPServices (5.4.0) + - AEPSignal (5.4.0): + - AEPCore (< 6.0.0, >= 5.4.0) - AEPUserProfile (5.0.0): - AEPCore (< 6.0.0, >= 5.0.0) - Flutter (1.0.0) - - flutter_aepassurance (4.0.2): + - flutter_aepassurance (5.0.0): - AEPAssurance (~> 5.0) - Flutter - - flutter_aepcore (4.0.2): - - AEPCore (~> 5.0) - - AEPIdentity (~> 5.0) - - AEPLifecycle (~> 5.0) - - AEPSignal (~> 5.0) + - flutter_aepcore (5.0.0): + - AEPCore (< 6.0.0, >= 5.4.0) + - AEPIdentity (< 6.0.0, >= 5.4.0) + - AEPLifecycle (< 6.0.0, >= 5.4.0) + - AEPSignal (< 6.0.0, >= 5.4.0) - Flutter - - flutter_aepedge (4.1.0): + - flutter_aepedge (5.0.0): - AEPEdge (~> 5.0) - Flutter - - flutter_aepedgebridge (4.0.2): + - flutter_aepedgebridge (5.0.0): - AEPEdgeBridge (~> 5.0) - Flutter - - flutter_aepedgeconsent (4.0.2): + - flutter_aepedgeconsent (5.0.0): - AEPEdgeConsent (~> 5.0) - Flutter - - flutter_aepedgeidentity (4.0.2): + - flutter_aepedgeidentity (5.0.0): - AEPEdgeIdentity (~> 5.0) - Flutter - - flutter_aepmessaging (4.0.2): + - flutter_aepmessaging (5.0.0): - AEPCore (~> 5.0) - AEPMessaging (~> 5.0) - Flutter - - flutter_aepuserprofile (4.0.2): + - flutter_aepuserprofile (5.0.0): - AEPUserProfile (~> 5.0) - Flutter @@ -108,28 +108,28 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_aepuserprofile/ios" SPEC CHECKSUMS: - AEPAssurance: 7f260ded4df38a70a06efebade8c33a3e3221984 - AEPCore: f1c3e9238bb12e7e1103f4407c341ebc65aeab5b - AEPEdge: 0873041dfb29f3126260f2dc16d548a1fefbe0c4 + AEPAssurance: df04baeace42befb0cc213fd6cdfe51651d11ba6 + AEPCore: 8cdc5390163f2b761e7f3eed1cae92b8a11c0237 + AEPEdge: 105afc7958acd7c016d57f7ac1d6f632bf05e6ee AEPEdgeBridge: be78be4885ae420ef21bda91707d5eff9510ef70 AEPEdgeConsent: d7db1d19eb4c1e2146360ed3c8df315f671b26d5 AEPEdgeIdentity: 3161ff33434586962946912d6b8e9e8fca1c4d23 - AEPIdentity: a65c1eba43a06f01b0dab191b27a53a81adada57 - AEPLifecycle: d4e0e1e86d6225d87203875d67f56c48f7ab7f67 - AEPMessaging: 63ea36bff1476ee61578df6f9f439df02a7283c9 + AEPIdentity: 44513e103592e8cc60508dde9f0b049c82897c07 + AEPLifecycle: 619ce7bf7b64a2caff7a172e9f12f0b6da636142 + AEPMessaging: 34d796c0a078c3e669b88150aae7dc468a308dcd AEPRulesEngine: fe5800653a4bee07b1e41e61b4d5551f0dba557b - AEPServices: e42e5118128e81c0f797fdfb1dc9c4a714d644b8 - AEPSignal: b146a3d4e5af51ff588f4f1ffbd40f1541325143 + AEPServices: ce27e3f2de75a9b24b3d27c7b5f1bb519724f8cb + AEPSignal: 9b34890eea1b81714aa45b7d7f8deb82206b0ca2 AEPUserProfile: cf36305d683d993d528337a46b7a269029b63e5d Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_aepassurance: 11a6c900c96349d4f02fe08422d02f7cf1af59ba - flutter_aepcore: 0a093e948da3875a946af14349d2c42e86d24f17 - flutter_aepedge: 0fb7613b6b18fbad9a33ba4abe0e27f56a17d073 - flutter_aepedgebridge: da0eed62c02a86225c491025a26771b39b6a32ba - flutter_aepedgeconsent: 0127015a09f9cd3e7fb9db3f618ae0bd15d092e3 - flutter_aepedgeidentity: f24ed3965f19fec2a86ac8c62357b13e53cabc61 - flutter_aepmessaging: 1f34e7f2e17cdd819590daea9156fe1cf03e29fe - flutter_aepuserprofile: c27eee2e59d8312bf6a0f3f54f16ef07cc485a58 + flutter_aepassurance: eec6156fe1f6324f05546b7df34f3f0d18187f71 + flutter_aepcore: 5de99ca989c8f31c5a715ae5c2bf864b01518936 + flutter_aepedge: 060db412454a60e1a970b3bdf649543c94a25fc0 + flutter_aepedgebridge: 717aa8ee9628396627b586f6210928381c02002a + flutter_aepedgeconsent: 0266639bef63eaf49b2c95f6d462d593002729e9 + flutter_aepedgeidentity: 1ff55516371c78016543bd6b1963943c0bb5f21a + flutter_aepmessaging: a702f2691b29880d723aa0ff214a271fc6bdd381 + flutter_aepuserprofile: c9c84653ece987b2ed5c9c8d49735c79aa362314 PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index a2d2dd3e..26170598 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -166,7 +166,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -238,6 +238,7 @@ files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6e..e67b2808 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ #import -@import AEPCore; -@import AEPLifecycle; -@import AEPIdentity; -@import AEPServices; -@import AEPSignal; -@import AEPAssurance; -@import AEPEdge; -@import AEPEdgeIdentity; -@import AEPEdgeConsent; -@import AEPEdgeBridge; -@import AEPMessaging; -@import AEPUserProfile; @interface AppDelegate : FlutterAppDelegate diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m index 3adfe850..826276c2 100644 --- a/example/ios/Runner/AppDelegate.m +++ b/example/ios/Runner/AppDelegate.m @@ -17,52 +17,9 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [GeneratedPluginRegistrant registerWithRegistry:self]; - [self initSDK:application]; // Override point for customization after application launch. return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (void)initSDK:(UIApplication *) application { - [AEPMobileCore setLogLevel:AEPLogLevelTrace]; - [AEPMobileCore setPrivacyStatus:AEPPrivacyStatusOptedIn]; - [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - NSString* ENVIRONMENT_FILE_ID = @"YOUR-APP-ID"; - - const UIApplicationState appState = application.applicationState; - - NSArray *extensionsToRegister = @[AEPMobileIdentity.class, - AEPMobileLifecycle.class, - AEPMobileSignal.class, - AEPMobileAssurance.class, - AEPMobileEdge.class, - AEPMobileEdgeIdentity.class, - AEPMobileEdgeConsent.class, - AEPMobileEdgeBridge.class, - AEPMobileMessaging.class, - AEPMobileUserProfile.class]; - - [AEPMobileCore registerExtensions:extensionsToRegister completion:^{ - if (appState != UIApplicationStateBackground) { - [AEPMobileCore lifecycleStart:nil]; - } - }]; - - [AEPMobileCore configureWithAppId: ENVIRONMENT_FILE_ID]; -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - [AEPMobileCore lifecyclePause]; -} - - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - [AEPMobileCore lifecycleStart:nil]; -} - @end diff --git a/example/lib/core.dart b/example/lib/core.dart index cbe87683..8cd1d566 100644 --- a/example/lib/core.dart +++ b/example/lib/core.dart @@ -154,7 +154,7 @@ class _MyAppState extends State { ), ElevatedButton( child: Text("MobileCore.setLogLevel"), - onPressed: () => MobileCore.setLogLevel(LogLevel.error), + onPressed: () => MobileCore.setLogLevel(LogLevel.debug), ), ElevatedButton( child: Text("MobileCore.setPrivacyStatus(...)"), diff --git a/example/lib/main.dart b/example/lib/main.dart index 919d85a7..25c7cf85 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,8 +9,8 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:flutter_aepcore/flutter_aepcore.dart'; import 'messaging.dart'; import 'core.dart'; import 'assurance.dart'; @@ -21,17 +21,46 @@ import 'edgeIdentity.dart'; import 'edgebridge.dart'; import 'userprofile.dart'; -void main() { + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); runApp(MaterialApp( - home: HomePage(), + home: HomePage(), )); } -class HomePage extends StatelessWidget { +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + /// Initialize the Adobe Experience Platform Mobile SDK inside the initState method. + @override + void initState() { + super.initState(); + _initializeAEPMobileSdk(); + } + + Future _initializeAEPMobileSdk() async { + MobileCore.setLogLevel(LogLevel.trace); + MobileCore.initializeWithAppId(appId:"YOUR_APP_ID"); + + // For more granular control over the initial options, you can use the following sample code: + // InitOptions initOptions = InitOptions( + // appId: "YOUR_APP_ID", + // lifecycleAutomaticTrackingEnabled: true, + // lifecycleAdditionalContextData: {"key": "value"}, + // appGroupIOS: "group.com.example", + // ); + + // MobileCore.initialize(initOptions: initOptions); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( + appBar: AppBar( title: const Text('Flutter AEP SDK'), ), body: Center( diff --git a/example/pubspec.lock b/example/pubspec.lock index 3059ff1c..3614fc6c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -68,56 +68,56 @@ packages: path: "../plugins/flutter_aepassurance" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_aepcore: dependency: "direct main" description: path: "../plugins/flutter_aepcore" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_aepedge: dependency: "direct main" description: path: "../plugins/flutter_aepedge" relative: true source: path - version: "4.1.0" + version: "5.0.0" flutter_aepedgebridge: dependency: "direct main" description: path: "../plugins/flutter_aepedgebridge" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_aepedgeconsent: dependency: "direct main" description: path: "../plugins/flutter_aepedgeconsent" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_aepedgeidentity: dependency: "direct main" description: path: "../plugins/flutter_aepedgeidentity" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_aepmessaging: dependency: "direct main" description: path: "../plugins/flutter_aepmessaging" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_aepuserprofile: dependency: "direct main" description: path: "../plugins/flutter_aepuserprofile" relative: true source: path - version: "4.0.2" + version: "5.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -127,26 +127,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" matcher: dependency: transitive description: @@ -159,18 +159,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" path: dependency: transitive description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.2" vector_math: dependency: transitive description: @@ -244,9 +244,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.5" sdks: - dart: ">=3.2.0-0 <4.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b73a14dc..1e504c38 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_aepsdk_example description: Demonstrates how to use the flutter_aep plugins. publish_to: "none" -version: 2.0.1 +version: 2.0.2 environment: sdk: ">=2.12.0 <4.0.0" @@ -14,21 +14,21 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" - flutter_aepassurance: ">= 4.0.0 <5.0.0" + flutter_aepassurance: ">=5.0.0 <6.0.0" - flutter_aepedge: ">= 4.0.0 <5.0.0" + flutter_aepedge: ">=5.0.0 <6.0.0" - flutter_aepedgeconsent: ">= 4.0.0 <5.0.0" + flutter_aepedgeconsent: ">=5.0.0 <6.0.0" - flutter_aepedgeidentity: ">= 4.0.0 <5.0.0" + flutter_aepedgeidentity: ">=5.0.0 <6.0.0" - flutter_aepedgebridge: ">= 4.0.0 <5.0.0" + flutter_aepedgebridge: ">=5.0.0 <6.0.0" - flutter_aepmessaging: ">= 4.0.0 <5.0.0" + flutter_aepmessaging: ">=5.0.0 <6.0.0" - flutter_aepuserprofile: ">= 4.0.0 <5.0.0" + flutter_aepuserprofile: ">=5.0.0 <6.0.0" dependency_overrides: flutter_aepcore: diff --git a/plugins/flutter_aepassurance/README.md b/plugins/flutter_aepassurance/README.md index 5e47bc80..6753f159 100644 --- a/plugins/flutter_aepassurance/README.md +++ b/plugins/flutter_aepassurance/README.md @@ -4,49 +4,67 @@ `flutter_aepassurance` is a flutter plugin for the iOS and Android [Adobe Experience Platform Assurance SDK](https://developer.adobe.com/client-sdks/documentation/platform-assurance/) to allow for integration with Flutter applications. Functionality to enable the Assurance extension is provided entirely through Dart documented below. -## Installation +## Prerequisites + +The Assurance plugin has the following peer dependency, which must be installed prior to installing it: -First, make sure that the [flutter_aepcore](https://github.com/adobe/aepsdk_flutter/blob/main/plugins/flutter_aepcore/README.md) plugin is installed, as flutter_aepassurance depends on it. +- [flutter_aepcore](https://github.com/adobe/aepsdk_flutter/blob/main/plugins/flutter_aepcore/README.md) + +## Installation Install instructions for this package can be found [here](https://pub.dev/packages/flutter_aepassurance/install). > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests +## Usage -Run: +For more detailed information on the Assurance APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/platform-assurance/) -```bash -flutter test +### Importing the extension: +```dart +import 'package:flutter_aepassurance/flutter_aepassurance.dart'; ``` +### Initializing with SDK: -## Usage -### Assurance +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) -For more detailed information on the Assurance APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/platform-assurance/) +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. -##### Registering the extension with AEPCore: +## API reference - > Note: It is required to initialize the SDK via native code inside your AppDelegate and MainApplication for iOS and Android respectively. +### extensionVersion +Returns the SDK version of the Assurance extension. -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. +**Syntax** +```dart +static Future get extensionVersion +``` +**Example** +```dart +String version = await Assurance.extensionVersion; +``` -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. +### startSession +Starting an Assurance session: -##### Importing the SDK: +**Syntax** +``` +static Future startSession(String url) +``` +**Example** ```dart -import 'package:flutter_aepassurance/flutter_aepassurance.dart'; +Assurance.startSession(url); ``` -##### Getting Assurance version: - ```dart -String version = await Assurance.extensionVersion; - ``` +## Tests -##### Starting a Assurance session: - ```dart -Assurance.startSession(url); - ``` +Run: + +```bash +flutter test +``` ## Contributing See [CONTRIBUTING](https://github.com/adobe/aepsdk_flutter/blob/main/CONTRIBUTING.md) diff --git a/plugins/flutter_aepassurance/android/build.gradle b/plugins/flutter_aepassurance/android/build.gradle index 23c663e5..96383be1 100644 --- a/plugins/flutter_aepassurance/android/build.gradle +++ b/plugins/flutter_aepassurance/android/build.gradle @@ -38,5 +38,6 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:assurance:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api "com.adobe.marketing.mobile:assurance" } diff --git a/plugins/flutter_aepassurance/ios/flutter_aepassurance.podspec b/plugins/flutter_aepassurance/ios/flutter_aepassurance.podspec index eb24ff85..9cc5bf2f 100644 --- a/plugins/flutter_aepassurance/ios/flutter_aepassurance.podspec +++ b/plugins/flutter_aepassurance/ios/flutter_aepassurance.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_aepassurance' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform support for Flutter apps.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepassurance/pubspec.yaml b/plugins/flutter_aepassurance/pubspec.yaml index 71d26d65..a870f8d6 100644 --- a/plugins/flutter_aepassurance/pubspec.yaml +++ b/plugins/flutter_aepassurance/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_aepassurance description: Official Adobe Experience Platform support for Flutter apps. Assurance is a new, innovative product from Adobe to help you easily validate SDK implementations. -version: 4.0.2 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepassurance @@ -13,7 +13,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepassurance/test/flutter_aepassurance_test.dart b/plugins/flutter_aepassurance/test/flutter_aepassurance_test.dart index 0cc8215c..0b528b90 100644 --- a/plugins/flutter_aepassurance/test/flutter_aepassurance_test.dart +++ b/plugins/flutter_aepassurance/test/flutter_aepassurance_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Assurance.extensionVersion; @@ -50,12 +54,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Assurance.startSession(testUrl); diff --git a/plugins/flutter_aepcore/README.md b/plugins/flutter_aepcore/README.md index 5c8a9894..88fec909 100644 --- a/plugins/flutter_aepcore/README.md +++ b/plugins/flutter_aepcore/README.md @@ -6,12 +6,12 @@ ## Contents - [Installation](#installation) -- [Usage](#usage) - - [Initializing](#initializing) - - [Core](#core) - - [Identity](#identity) - - [Lifecycle](#lifecycle) - - [Signal](#signal) +- [Importing the plugins](#importing-the-plugins) +- [Initializing](#initializing) +- [Core](#core) +- [Identity](#identity) +- [Lifecycle](#lifecycle) +- [Signal](#signal) - [Tests](#tests) ## Installation @@ -20,43 +20,161 @@ Install instructions for this package can be found [here](https://pub.dev/packag > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Usage +## Importing the Plugins -### Initializing +Import the package in your **Dart** code as follows: -Initializing the SDK should be done in native code (AppDelegate / SceneDelegate for iOS and Application class for Android). Documentation for initializing the SDK can be found [here](https://developer.adobe.com/client-sdks/documentation/getting-started/get-the-sdk/#2-add-initialization-code). +```dart +import 'package:flutter_{extension}/flutter_{plugin_name}.dart' +``` -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. +## Initializing -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](#initializewithappid) +- [MobileCore.initialize(initOptions)](#initialize) -### Core +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. + +## Core For more detailed information on the Core APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/mobile-core/) -##### Importing Core: +## Importing Core: +In your Flutter application, import the Core package as follows: + ```dart import 'package:flutter_aepcore/flutter_aepcore.dart'; ``` -##### Getting Core version: - ```dart +## API reference + +### extensionVersion +Returns the SDK version of the Core extension. + +**Syntax** +```dart +Future get extensionVersion +``` + +**Example** +```dart String version = await MobileCore.extensionVersion; - ``` +``` + +### initializeWithAppId +Initialize the AEP SDK by automatically registering all extensions bundled with the application and enabling automatic lifecycle tracking. + +appId: Configures the SDK with the provided mobile property environment ID configured from the Data Collection UI. + +**Syntax** +```dart +static Future initializeWithAppId({required String appId}) +``` + +**Example** +```dart +class _HomePageState extends State { + /// Initialize the Adobe Experience Platform Mobile SDK inside the initState method. + @override + void initState() { + super.initState(); + _initializeAEPMobileSdk(); + } + + Future _initializeAEPMobileSdk() async { + MobileCore.setLogLevel(LogLevel.trace); + MobileCore.initializeWithAppId(appId:"YOUR_APP_ID"); + } +} +``` + +> [!NOTE] +> Starting from Adobe Experience Platform Flutter **5.x**, there is no longer a need to initialize the SDK on the [native platforms](https://github.com/adobe/aepsdk_flutter/tree/v4.x?tab=readme-ov-file#usage), as was required in earlier versions. + +### initialize +Initialize the AEP SDK by automatically registering all extensions bundled with the application and enabling automatic lifecycle tracking. This API also allows further customization by accepting InitOptions. + +InitOptions: Allow customization of the default initialization behavior. Refer to the [InitOptions](#initoptions). + +**Syntax** +```dart +static Future initialize({required InitOptions initOptions}) +``` + +**Example** +```dart -##### Updating the SDK configuration: +class _HomePageState extends State { + /// Initialize the Adobe Experience Platform Mobile SDK inside the initState method. + @override + void initState() { + super.initState(); + _initializeAEPMobileSdk(); + } + + Future _initializeAEPMobileSdk() async { + MobileCore.setLogLevel(LogLevel.trace); + + try { + InitOptions initOptions = InitOptions( + appId: "YOUR-APP-ID", + lifecycleAutomaticTrackingEnabled: true, + lifecycleAdditionalContextData: {"key": "value"} + ); + + MobileCore.initialize(initOptions: initOptions); + print("Adobe Experience Platform Mobile SDK was initialized"); + } catch (e) { + print("Failed to initialize Adobe Experience Platform Mobile SDK: $e"); + } + } +} +``` +#### InitOptions +The InitOptions class defines the options for initializing the AEP SDK. It currently supports the following options: + +* appID – The App ID used to retrieve remote configurations from Adobe servers. +* lifecycleAutomaticTrackingEnabled – A boolean flag to enable or disable automatic lifecycle tracking +* lifecycleAdditionalContextData – A map containing extra context data to be sent with the lifecycle start event. +* appGroup (iOS only) – A string representing the App Group identifier for sharing data between app extensions and the main application. + +### updateConfiguration +Update the configuration programmatically by passing configuration keys and values to override the existing configuration. + +**Syntax** +```dart +static Future updateConfiguration(Map configMap) +``` + +**Example** ```dart MobileCore.updateConfiguration({"key" : "value"}); ``` -##### Clearing configuration updates back to original configuration: +### clearUpdatedConfiguration +Clearing configuration updates back to original configuration. +**Syntax** +```dart +static Future clearUpdatedConfiguration() +``` + +**Example** ```dart MobileCore.clearUpdatedConfiguration(); ``` -##### Controlling the log level of the SDK: +### setLogLevel +Control the log level of the SDK. + +**Syntax** +```dart +static Future setLogLevel(LogLevel mode) +``` + +**Example** ```dart MobileCore.setLogLevel(LogLevel.error); MobileCore.setLogLevel(LogLevel.warning); @@ -64,7 +182,15 @@ MobileCore.setLogLevel(LogLevel.debug); MobileCore.setLogLevel(LogLevel.trace); ``` -##### Getting the current privacy status: +### get privacyStatus +Get the current privacy status. + +**Syntax** +```dart +static Future get privacyStatus +``` + +**Example** ```dart PrivacyStatus result; @@ -75,14 +201,30 @@ try { } ``` -##### Setting the privacy status: +### setPrivacyStatus +Set the privacy status. + +**Syntax** +```dart +static Future setPrivacyStatus(PrivacyStatus privacyStatus) +``` + +**Example** ```dart MobileCore.setPrivacyStatus(PrivacyStatus.opt_in); MobileCore.setPrivacyStatus(PrivacyStatus.opt_out); MobileCore.setPrivacyStatus(PrivacyStatus.unknown); ``` -##### Getting the SDK identities: +### get sdkIdentities +Get the SDK identities. + +**Syntax** +```dart +static Future get sdkIdentities +``` + +**Example** ```dart String result = ""; @@ -93,7 +235,15 @@ try { } ``` -##### Dispatching an Event Hub event: +### dispatchEvent +Dispatch an Event Hub event. + +**Syntax** +```dart +static Future dispatchEvent(Event event) +``` + +**Example** ```dart final Event event = Event({ "eventName": "testEventName", @@ -108,7 +258,15 @@ try { } ``` -##### Dispatching an Event Hub event with callback: +### dispatchEventWithResponseCallback +Dispatch an Event Hub event with callback. + +**Syntax** +```dart +static Future dispatchEventWithResponseCallback +``` + +**Example** ```dart Event result; final Event event = Event({ @@ -124,23 +282,47 @@ final Event event = Event({ } ``` -##### Reset identities +### resetIdentities +The resetIdentities method requests that each extension resets the identities it owns and each extension responds to this request uniquely. + +**Syntax** +```dart +static Future resetIdentities() +``` + +**Example** ```dart MobileCore.resetIdentities() ``` -##### Track app actions +### trackAction +Track event actions that occur in your application. > [!IMPORTANT] > trackAction is supported through [Edge Bridge](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedgebridge) and [Edge Network](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedge) extensions. +**Syntax** +```dart +static Future trackAction +``` + +**Example** ```dart MobileCore.trackAction("myAction", data: {"key1": "value1"}); ``` -##### Track app states + +### trackState +Track states that represent screens or views in your application. + > [!IMPORTANT] > trackState is supported through [Edge Bridge](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedgebridge) and [Edge Network](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedge) extensions. +**Syntax** +```dart +static Future trackState +``` + +**Example** ```dart MobileCore.trackState("myState", data: {"key1": "value1"}); ``` @@ -149,32 +331,67 @@ MobileCore.trackState("myState", data: {"key1": "value1"}); For more information on the Core Identity APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/mobile-core/identity/). -##### Importing Identity: +### Importing Identity: +In your Flutter application, import the Identity package as follows: + ```dart import 'package:flutter_aepcore/flutter_aepidentity.dart'; ``` -##### Getting Identity version: +## API reference + +### extensionVersion +Returns the SDK version of the Identity extension. + +**Syntax** +```dart +Future get extensionVersion +``` + +**Example** ```dart String version = await Identity.extensionVersion; ``` -##### Sync Identifier: +### syncIdentifier +Updates the given customer ID with the Adobe Experience Cloud ID Service. + +**Syntax** +```dart +static Future syncIdentifier +``` + +**Example** ```dart Identity.syncIdentifier("identifierType", "identifier", MobileVisitorAuthenticationState.authenticated); ``` -##### Sync Identifiers: +### syncIdentifiers +Updates the given customer IDs with the Adobe Experience Cloud ID Service. + +**Syntax** +```dart +static Future syncIdentifiers(Map identifiers) +``` + +**Example** ```dart Identity.syncIdentifiers({"idType1":"idValue1", "idType2":"idValue2", "idType3":"idValue3"}); ``` -##### Sync Identifiers with Authentication State: +### syncIdentifiersWithAuthState +Updates the given customer IDs with Authentication State using the Adobe Experience Cloud ID Service. + +**Syntax** +```dart +static Future syncIdentifiersWithAuthState +``` + +**Example** ```dart Identity.syncIdentifiersWithAuthState({"idType1":"idValue1", "idType2":"idValue2", "idType3":"idValue3"}, MobileVisitorAuthenticationState.authenticated); - ``` Note: `MobileVisitorAuthenticationState` is defined as: @@ -183,8 +400,15 @@ Note: `MobileVisitorAuthenticationState` is defined as: enum MobileVisitorAuthenticationState {unknown, authenticated, logged_out} ``` -##### Append visitor data to a URL: +### appendToUrl +Append visitor data to a URL. +**Syntax** +```dart +static Future appendToUrl(String url) +``` + +**Example** ```dart String result = ""; @@ -195,14 +419,15 @@ try { } ``` -##### Setting the advertising identifier: +### get urlVariables +Get visitor data as URL query parameter string. +**Syntax** ```dart -MobileCore.setAdvertisingIdentifier("ad-id"); +static Future get urlVariables ``` -##### Get visitor data as URL query parameter string: - +**Example** ```dart String result = ""; @@ -213,8 +438,15 @@ try { } ``` -##### Get Identifiers: +### get identifiers +Returns all customer identifiers that were previously synced with the Adobe Experience Cloud. + +**Syntax** +```dart +static Future> get identifiers +``` +**Example** ```dart List result; @@ -225,7 +457,15 @@ try { } ``` -##### Get Experience Cloud IDs: +### getExperienceCloudId +This API retrieves the Experience Cloud ID (ECID) that was generated when the app was initially launched. This ID is preserved between app upgrades, is saved and restored during the standard application backup process, and is removed at uninstall. + +**Syntax** +```dart +static Future get experienceCloudId +``` + +**Example** ```dart String result = ""; @@ -236,32 +476,38 @@ try { } ``` -##### AEPMobileVisitorId Class: -```dart -class Identifiable { - String get idOrigin; - String get idType; - String get identifier; - MobileVisitorAuthenticationState get authenticationState; -} -``` - ### Lifecycle -For more information on the Core Lifecycle APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/mobile-core/lifecycle/) +For more information about Lifecycle for Edge Network, visit the documentation [here](https://developer.adobe.com/client-sdks/edge/lifecycle-for-edge-network/). -> Note: It is required to implement Lifecycle in native [Android and iOS code](https://developer.adobe.com/client-sdks/documentation/mobile-core/lifecycle/). +Starting from Adobe Experience Platform Flutter **5.x**, lifecycle tracking is enabled automatically with Initialize APIs by default. + +Refer to +- [MobileCore.initializeWithAppId(appId)](#initializewithappid) +- [MobileCore.initialize(initOptions)](#initialize) ### Signal For more information on the Core Signal APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/mobile-core/signal/) -##### Importing Signal: +### Importing Signal: +In your Flutter application, import the Signal package as follows: + ```dart import 'package:flutter_aepcore/flutter_aepsignal.dart'; ``` -##### Getting Signal version: +## API reference + +### extensionVersion +Returns the SDK version of the Core extension. + +**Syntax** +``` +static Future get extensionVersion +``` + +**Example** ```dart String version = await Signal.extensionVersion; ``` diff --git a/plugins/flutter_aepcore/android/build.gradle b/plugins/flutter_aepcore/android/build.gradle index f2fb68d6..183bde69 100644 --- a/plugins/flutter_aepcore/android/build.gradle +++ b/plugins/flutter_aepcore/android/build.gradle @@ -1,5 +1,4 @@ group 'com.adobe.marketing.mobile.flutter.flutter_aepcore' -//do we need to update this version? version '3.0' buildscript { @@ -39,8 +38,9 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:core:3.+' - api 'com.adobe.marketing.mobile:identity:3.+' - api 'com.adobe.marketing.mobile:lifecycle:3.+' - api 'com.adobe.marketing.mobile:signal:3.+' -} + implementation platform("com.adobe.marketing.mobile:sdk-bom:[3.8.0,4.0.0)") + api "com.adobe.marketing.mobile:core" + api "com.adobe.marketing.mobile:signal" + api "com.adobe.marketing.mobile:identity" + api "com.adobe.marketing.mobile:lifecycle" +} \ No newline at end of file diff --git a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCoreDataBridge.java b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCoreDataBridge.java index e95a5125..b7cac469 100644 --- a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCoreDataBridge.java +++ b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCoreDataBridge.java @@ -15,6 +15,7 @@ import com.adobe.marketing.mobile.Event; import com.adobe.marketing.mobile.LoggingMode; import com.adobe.marketing.mobile.MobilePrivacyStatus; +import com.adobe.marketing.mobile.InitOptions; import java.util.HashMap; import java.util.Map; @@ -38,6 +39,10 @@ public class FlutterAEPCoreDataBridge { public final static String EVENT_SOURCE_KEY = "eventSource"; public final static String EVENT_DATA_KEY = "eventData"; + private final static String APPID_KEY = "appId"; + private final static String LIFECYCLE_AUTOMATIC_TRACKING_KEY = "lifecycleAutomaticTrackingEnabled"; + private static final String LIFECYCLE_ADDITIONAL_CONTEXTDATA_KEY = "lifecycleAdditionalContextData"; + /** * Converts a {@link Map} into an {@link Event} * @@ -118,6 +123,36 @@ static String stringFromPrivacyStatus(final MobilePrivacyStatus privacyStatus) { return AEP_PRIVACY_STATUS_UNKNOWN; } + static InitOptions initOptionsFromMap(final Object arguments) { + if (!(arguments instanceof Map)) { + return null; + } + + Map argumentsAsMap = (Map) arguments; + + Map initOptionsMap = getNullableMap(argumentsAsMap, "initOptions"); + if (initOptionsMap == null) { + return null; + } + String appId = getNullableString(initOptionsMap, APPID_KEY); + Boolean lifecycleAutomaticTrackingEnabled = getNullableBoolean(initOptionsMap, LIFECYCLE_AUTOMATIC_TRACKING_KEY); + Map lifecycleAdditionalContextData = getNullableStringMap(initOptionsMap, LIFECYCLE_ADDITIONAL_CONTEXTDATA_KEY); + + InitOptions options; + if (appId != null) { + options = InitOptions.configureWithAppID(appId); + } else { + options = new InitOptions(); + } + if (lifecycleAutomaticTrackingEnabled != null) + { + options.setLifecycleAutomaticTrackingEnabled(lifecycleAutomaticTrackingEnabled); + } + options.setLifecycleAdditionalContextData(lifecycleAdditionalContextData); + + return options; + } + // Helper methods private static String getNullableString(final Map data, final String key) { @@ -127,4 +162,29 @@ private static String getNullableString(final Map data, final String key) { private static Map getNullableMap(final Map data, final String key) { return data.containsKey(key) && (data.get(key) instanceof Map) ? (Map) data.get(key) : null; } + + private static Boolean getNullableBoolean(final Map data, final String key) { + return data.containsKey(key) && (data.get(key) instanceof Boolean) ? (Boolean) data.get(key) : null; + } + + private static Map getNullableStringMap(final Map data, final String key) { + if (!data.containsKey(key) || !(data.get(key) instanceof Map)) { + return null; + } + + Map rawMap = (Map) data.get(key); + + if (rawMap == null || !(rawMap instanceof Map)) { + return null; + } + + for (Object entryObj : rawMap.entrySet()) { + Map.Entry entry = (Map.Entry) entryObj; + if (!(entry.getKey() instanceof String) || !(entry.getValue() instanceof String)) { + return null; + } + } + + return (Map) rawMap; + } } diff --git a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCorePlugin.java b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCorePlugin.java index b18610a9..3e9a2658 100644 --- a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCorePlugin.java +++ b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPCorePlugin.java @@ -17,18 +17,22 @@ import java.util.HashMap; import java.util.Map; - +import android.app.Application; import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; +import android.content.Context; public class FlutterAEPCorePlugin implements FlutterPlugin, MethodCallHandler { - - private final String TAG = "FlutterAEPCorePlugin"; + + private static final String TAG = "FlutterAEPCorePlugin"; + private static final String INVALID_ARGUMENT = "INVALID_ARGUMENT"; + private static final String INITIALIZATION_ERROR = "INITIALIZATION_ERROR"; private MethodChannel channel; + private Application application; private final FlutterAEPIdentityPlugin flutterAEPIdentityPlugin = new FlutterAEPIdentityPlugin(); private final FlutterAEPLifecyclePlugin flutterAEPLifecyclePlugin = new FlutterAEPLifecyclePlugin(); private final FlutterAEPSignalPlugin flutterAEPSignalPlugin = new FlutterAEPSignalPlugin(); @@ -36,7 +40,12 @@ public class FlutterAEPCorePlugin implements FlutterPlugin, MethodCallHandler { @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepcore"); - channel.setMethodCallHandler(new FlutterAEPCorePlugin()); + channel.setMethodCallHandler(this); + Context appContext = binding.getApplicationContext(); + if (appContext instanceof Application) { + application = (Application) appContext; + } + MobileCore.setWrapperType(WrapperType.FLUTTER); flutterAEPIdentityPlugin.onAttachedToEngine(binding); flutterAEPLifecyclePlugin.onAttachedToEngine(binding); flutterAEPSignalPlugin.onAttachedToEngine(binding); @@ -47,6 +56,7 @@ public void onDetachedFromEngine(@NonNull final FlutterPluginBinding binding) { if (channel != null) { channel.setMethodCallHandler(null); } + application = null; flutterAEPIdentityPlugin.onDetachedFromEngine(binding); flutterAEPLifecyclePlugin.onDetachedFromEngine(binding); flutterAEPSignalPlugin.onDetachedFromEngine(binding); @@ -57,6 +67,8 @@ public void onMethodCall(MethodCall call, @NonNull Result result) { final String AEPCORE_TAG = "AEPCORE"; if ("extensionVersion".equals(call.method)) { result.success(MobileCore.extensionVersion()); + } else if ("initialize".equals(call.method)) { + handleInitialize(result, call.arguments); } else if ("track".equals(call.method)) { handleTrackCall(call.arguments); result.success(null); @@ -97,6 +109,32 @@ public void onMethodCall(MethodCall call, @NonNull Result result) { } } + private void handleInitialize(Result result, Object arguments) { + if (!(arguments instanceof Map)) { + result.error(INVALID_ARGUMENT, "Initialize failed because arguments is not a Map", null); + return; + } + + if (application == null) { + result.error(INITIALIZATION_ERROR, "Initialize failed because application is null", null); + return; + } + + InitOptions initOptions = FlutterAEPCoreDataBridge.initOptionsFromMap(arguments); + + if (initOptions == null) { + result.error(INITIALIZATION_ERROR, "Initialize failed because initOptions is null", null); + return; + } + + MobileCore.initialize(application, initOptions, new AdobeCallback() { + @Override + public void call(Object o) { + result.success(null); + } + }); + } + private void handleTrackCall(Object arguments) { if (!(arguments instanceof Map) || !((Map) arguments).containsKey("type") || !((Map) arguments).containsKey("name")) { Log.e(TAG, "Track action failed because arguments were invalid"); diff --git a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPIdentityPlugin.java b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPIdentityPlugin.java index bbdf8203..895094e4 100644 --- a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPIdentityPlugin.java +++ b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPIdentityPlugin.java @@ -39,7 +39,7 @@ public class FlutterAEPIdentityPlugin implements FlutterPlugin, MethodChannel.Me @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepidentity"); - channel.setMethodCallHandler(new FlutterAEPIdentityPlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPLifecyclePlugin.java b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPLifecyclePlugin.java index d73b6d3f..867ad663 100644 --- a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPLifecyclePlugin.java +++ b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPLifecyclePlugin.java @@ -26,7 +26,7 @@ public class FlutterAEPLifecyclePlugin implements FlutterPlugin, MethodChannel.M @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aeplifecycle"); - channel.setMethodCallHandler(new FlutterAEPLifecyclePlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPSignalPlugin.java b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPSignalPlugin.java index 9b7faca4..3b9eeb8b 100644 --- a/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPSignalPlugin.java +++ b/plugins/flutter_aepcore/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepcore/FlutterAEPSignalPlugin.java @@ -26,7 +26,7 @@ public class FlutterAEPSignalPlugin implements FlutterPlugin, MethodChannel.Meth @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepsignal"); - channel.setMethodCallHandler(new FlutterAEPSignalPlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.h b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.h index bb4c7556..f59a00f8 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.h +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.h @@ -26,4 +26,5 @@ governing permissions and limitations under the License. + (NSDictionary *)sanitizeDictionaryToContainClass: (Class) type WithDictionary:(NSDictionary *)dict; ++ (AEPInitOptions *)initOptionsFromMap:(id)map; @end diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.m index 5b093735..874fa77e 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.m +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCoreDataBridge.m @@ -11,6 +11,8 @@ */ #import "FlutterAEPCoreDataBridge.h" +#import + @implementation FlutterAEPCoreDataBridge @@ -89,4 +91,44 @@ + (NSDictionary *)sanitizeDictionaryToContainClass: (Class) type WithDictionary: return sanitizedDict; } ++ (AEPInitOptions *)initOptionsFromMap:(id)map { + if (![map isKindOfClass:[FlutterMethodCall class]]) { + return nil; + } + + FlutterMethodCall *call = (FlutterMethodCall *)map; + NSDictionary *arguments = call.arguments; + if (![arguments isKindOfClass:[NSDictionary class]]) { + return nil; + } + + NSDictionary *initOptionsMap = arguments[@"initOptions"]; + if (![initOptionsMap isKindOfClass:[NSDictionary class]]) { + return nil; + } + + NSString *appId = initOptionsMap[@"appId"]; + NSNumber *lifecycleAutomaticTrackingEnabled = initOptionsMap[@"lifecycleAutomaticTrackingEnabled"]; + NSDictionary *lifecycleAdditionalContextData = initOptionsMap[@"lifecycleAdditionalContextData"]; + NSString *appGroup = initOptionsMap[@"appGroupIOS"]; + + AEPInitOptions *initOptions; + if (appId != nil && [appId isKindOfClass:[NSString class]]) { + initOptions = [[AEPInitOptions alloc] initWithAppId:appId]; + } else { + initOptions = [[AEPInitOptions alloc] init]; + } + if (lifecycleAutomaticTrackingEnabled != nil && [lifecycleAutomaticTrackingEnabled isKindOfClass:[NSNumber class]] ) { + initOptions.lifecycleAutomaticTrackingEnabled = [lifecycleAutomaticTrackingEnabled boolValue]; + } + if (lifecycleAdditionalContextData != nil && [lifecycleAdditionalContextData isKindOfClass:[NSDictionary class]]) { + initOptions.lifecycleAdditionalContextData = lifecycleAdditionalContextData; + } + if (appGroup != nil && [appGroup isKindOfClass:[NSString class]]) { + initOptions.appGroup = appGroup; + } + + return initOptions; +} + @end diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m index 9d282e9f..c48fce8e 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m @@ -15,6 +15,7 @@ #import "FlutterAEPIdentityPlugin.h" #import "FlutterAEPLifecyclePlugin.h" #import "FlutterAEPSignalPlugin.h" +#import "FlutterAEPErrorHelper.h" @implementation FlutterAEPCorePlugin + (void)registerWithRegistrar:(NSObject *)registrar { @@ -27,12 +28,15 @@ + (void)registerWithRegistrar:(NSObject *)registrar { [FlutterAEPIdentityPlugin registerWithRegistrar:registrar]; [FlutterAEPLifecyclePlugin registerWithRegistrar:registrar]; [FlutterAEPSignalPlugin registerWithRegistrar:registrar]; + [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([@"extensionVersion" isEqualToString:call.method]) { result([AEPMobileCore extensionVersion]); + } else if ([@"initialize" isEqualToString:call.method]) { + [self handleInitialize:call result:result]; } else if ([@"track" isEqualToString:call.method]) { [self handleTrackCall:call]; result(nil); @@ -80,6 +84,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call } } +- (void)handleInitialize:(id)arguments result:(FlutterResult)result { + AEPInitOptions *initOptions = [FlutterAEPCoreDataBridge initOptionsFromMap:arguments]; + if (!initOptions) { + result([FlutterError errorWithCode:@"INVALID_ARGUMENT" + message:@"Initialize failed because initOptions is not valid" + details:nil]); + return; + } + + [AEPMobileCore initializeWithOptions:initOptions completion:^{ + result(nil); + }]; +} + - (void)handleTrackCall:(FlutterMethodCall *)call { NSDictionary *dict = (NSDictionary *)call.arguments; NSString *type = (NSString *)dict[@"type"]; @@ -155,9 +173,8 @@ - (void)handleDispatchEventWithResponseCallback:(FlutterMethodCall *)call - (void)handleGetSdkIdentities:(FlutterMethodCall *)call result:(FlutterResult)result { - [AEPMobileCore getSdkIdentities:^(NSString *_Nullable content, - NSError *_Nullable error) { - result(content); + [AEPMobileCore getSdkIdentities:^(NSString *_Nullable content, NSError *_Nullable error) { + [FlutterAEPErrorHelper handleResult:result error:error success:content]; }]; } diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPErrorHelper.h b/plugins/flutter_aepcore/ios/Classes/FlutterAEPErrorHelper.h new file mode 100644 index 00000000..da7e11b4 --- /dev/null +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPErrorHelper.h @@ -0,0 +1,7 @@ +#import + +@interface FlutterAEPErrorHelper : NSObject + ++ (void)handleResult:(FlutterResult _Nullable )result error:(NSError * _Nullable)error success:(id _Nullable)success; + +@end diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPErrorHelper.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPErrorHelper.m new file mode 100644 index 00000000..32666dd8 --- /dev/null +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPErrorHelper.m @@ -0,0 +1,15 @@ +#import "FlutterAEPErrorHelper.h" + +@implementation FlutterAEPErrorHelper + ++ (void)handleResult:(FlutterResult)result error:(NSError * _Nullable)error success:(id _Nullable)success { + if (error) { + result([FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", (long)error.code] + message:error.localizedDescription + details:nil]); + } else { + result(success); + } +} + +@end diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPIdentityPlugin.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPIdentityPlugin.m index f7d7da22..5ac38fb9 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPIdentityPlugin.m +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPIdentityPlugin.m @@ -11,6 +11,7 @@ #import "FlutterAEPIdentityPlugin.h" @import AEPIdentity; #import "FlutterAEPIdentityDataBridge.h" +#import "FlutterAEPErrorHelper.h" @implementation FlutterAEPIdentityPlugin + (void)registerWithRegistrar:(NSObject*)registrar { @@ -46,25 +47,28 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result - (void)handleAppendToUrl:(FlutterMethodCall *) call result:(FlutterResult)result { NSURL *url = [NSURL URLWithString:call.arguments]; [AEPMobileIdentity appendToUrl:url completion:^(NSURL * _Nullable url, NSError * _Nullable error) { - result(url.absoluteString); + [FlutterAEPErrorHelper handleResult:result error:error success:url.absoluteString]; }]; } - (void)handleGetIdentifiers:(FlutterMethodCall *) call result:(FlutterResult)result { [AEPMobileIdentity getIdentifiers:^(NSArray> * _Nullable visitorIDs, NSError * _Nullable error) { - NSMutableArray *visitorIDList = [NSMutableArray array]; - for (id visitorID in visitorIDs) { - NSDictionary *visitorIDDict = [FlutterAEPIdentityDataBridge dictionaryFromVisitorId:visitorID]; - [visitorIDList addObject:visitorIDDict]; + if (error) { + [FlutterAEPErrorHelper handleResult:result error:error success:nil]; + } else { + NSMutableArray *visitorIDList = [NSMutableArray array]; + for (id visitorID in visitorIDs) { + NSDictionary *visitorIDDict = [FlutterAEPIdentityDataBridge dictionaryFromVisitorId:visitorID]; + [visitorIDList addObject:visitorIDDict]; + } + [FlutterAEPErrorHelper handleResult:result error:nil success:visitorIDList]; } - - result(visitorIDList); }]; } - (void)handleGetExperienceCloudId:(FlutterMethodCall *) call result:(FlutterResult)result { [AEPMobileIdentity getExperienceCloudId:^(NSString * _Nullable experienceCloudId, NSError * _Nullable error) { - result(experienceCloudId); + [FlutterAEPErrorHelper handleResult:result error:error success:experienceCloudId]; }]; } @@ -90,7 +94,7 @@ - (void)handleSyncIdentifiersWithAuthState:(FlutterMethodCall *) call result:(Fl - (void)handleUrlVariables:(FlutterMethodCall *) call result:(FlutterResult)result { [AEPMobileIdentity getUrlVariables:^(NSString * _Nullable urlVariables, NSError * _Nullable error) { - result(urlVariables); + [FlutterAEPErrorHelper handleResult:result error:error success:urlVariables]; }]; } diff --git a/plugins/flutter_aepcore/ios/flutter_aepcore.podspec b/plugins/flutter_aepcore/ios/flutter_aepcore.podspec index 30ff8086..aa5a9712 100644 --- a/plugins/flutter_aepcore/ios/flutter_aepcore.podspec +++ b/plugins/flutter_aepcore/ios/flutter_aepcore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'flutter_aepcore' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform support for Flutter apps.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } @@ -9,10 +9,10 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'AEPCore', '~> 5.0' - s.dependency 'AEPLifecycle', '~> 5.0' - s.dependency 'AEPIdentity', '~> 5.0' - s.dependency 'AEPSignal', '~> 5.0' + s.dependency 'AEPCore', '>= 5.4.0', '< 6.0.0' + s.dependency 'AEPLifecycle', '>= 5.4.0', '< 6.0.0' + s.dependency 'AEPIdentity', '>= 5.4.0', '< 6.0.0' + s.dependency 'AEPSignal', '>= 5.4.0', '< 6.0.0' s.platform = :ios, '12.0' s.static_framework = true diff --git a/plugins/flutter_aepcore/lib/flutter_aepcore.dart b/plugins/flutter_aepcore/lib/flutter_aepcore.dart index fe667c44..eca771f7 100644 --- a/plugins/flutter_aepcore/lib/flutter_aepcore.dart +++ b/plugins/flutter_aepcore/lib/flutter_aepcore.dart @@ -23,6 +23,20 @@ class MobileCore { static Future get extensionVersion => _channel.invokeMethod('extensionVersion').then((value) => value!); + /// Initializes the AEP Mobile SDK with the provided initialization options. + /// @param initOptions The [InitOptions] to configure the SDK. + static Future initialize({required InitOptions initOptions}) { + return _channel.invokeMethod('initialize', {'initOptions': initOptions.toMap()}); + } + + /// Initializes the AEP Mobile SDK with the provided App ID. + /// + /// This method creates an [InitOptions] object using the provided App ID and then calls the `initialize` API. + /// @param appId The AEP SDK App ID. + static Future initializeWithAppId({required String appId}) { + return initialize(initOptions: InitOptions(appId: appId)); + } + /// This method sends a generic Analytics action tracking hit with context data. static Future trackAction( String action, { diff --git a/plugins/flutter_aepcore/lib/flutter_aepcore_data.dart b/plugins/flutter_aepcore/lib/flutter_aepcore_data.dart index 6041fa5e..a08c7b08 100644 --- a/plugins/flutter_aepcore/lib/flutter_aepcore_data.dart +++ b/plugins/flutter_aepcore/lib/flutter_aepcore_data.dart @@ -13,3 +13,4 @@ export 'package:flutter_aepcore/src/aepextension_event.dart'; export 'package:flutter_aepcore/src/aepmobile_logging_level.dart'; export 'package:flutter_aepcore/src/aepmobile_privacy_status.dart'; export 'package:flutter_aepcore/src/aepmobile_visitor_id.dart'; +export 'package:flutter_aepcore/src/aepmobile_init_options.dart'; \ No newline at end of file diff --git a/plugins/flutter_aepcore/lib/src/aepmobile_init_options.dart b/plugins/flutter_aepcore/lib/src/aepmobile_init_options.dart new file mode 100644 index 00000000..94476c60 --- /dev/null +++ b/plugins/flutter_aepcore/lib/src/aepmobile_init_options.dart @@ -0,0 +1,41 @@ +/* +Copyright 2025 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +/// Configuration options for initializing the Adobe Mobile SDK. +class InitOptions { + /// The App ID for the Adobe SDK configuration. + String? appId; + /// Flag indicating whether automatic lifecycle tracking is enabled + bool? lifecycleAutomaticTrackingEnabled; + /// Additional context data to be included in lifecycle start event. + Map? lifecycleAdditionalContextData; + /// App group used to share user defaults and files among containing app and extension apps on iOS + String? appGroupIOS; + + /// Constructor with named optional parameters. + InitOptions({ + this.appId, + this.lifecycleAutomaticTrackingEnabled = null, + this.lifecycleAdditionalContextData = null, + this.appGroupIOS = null, + }); + + /// Converts the [InitOptions] instance to a [Map]. + Map toMap() { + Map retMap = {}; + retMap['appId'] = appId; + retMap['lifecycleAutomaticTrackingEnabled'] = lifecycleAutomaticTrackingEnabled; + retMap['lifecycleAdditionalContextData'] = lifecycleAdditionalContextData; + retMap['appGroupIOS'] = appGroupIOS; + + return retMap; + } +} diff --git a/plugins/flutter_aepcore/pubspec.yaml b/plugins/flutter_aepcore/pubspec.yaml index fae31bb9..cfe9fabf 100644 --- a/plugins/flutter_aepcore/pubspec.yaml +++ b/plugins/flutter_aepcore/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_aepcore description: Official Adobe Experience Platform support for Flutter apps. The Mobile Core represents the core Adobe Experience Platform SDK that is required for every app implementation. -version: 4.0.2 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk-flutter/tree/main/plugins/flutter_aepcore diff --git a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart index 677df8f2..1ab2da1b 100644 --- a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart @@ -9,6 +9,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ + import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -19,29 +20,113 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('extensionVersion', () { - final String testVersion = "2.5.0"; + final String testVersion = "2.5.0"; + final List log = []; + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + log.add(methodCall); + return testVersion; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { + await MobileCore.extensionVersion; + + expect(log, [ + isMethodCall( + 'extensionVersion', + arguments: null, + ), + ]); + }); + + test('returns correct result', () async { + expect(await MobileCore.extensionVersion, testVersion); + }); + }); + + group('initialize', () { + final String appId = "12345"; + final bool lifecycleAutomaticTrackingEnabled = true; + final Map lifecycleAdditionalContextData = {"key": "value"}; + final String appGroup = "group.com.example"; + + InitOptions initOptions = InitOptions( + appId: appId, + lifecycleAutomaticTrackingEnabled: lifecycleAutomaticTrackingEnabled, + lifecycleAdditionalContextData: lifecycleAdditionalContextData, + appGroupIOS: appGroup, + ); + final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); - return testVersion; + return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { - await MobileCore.extensionVersion; + await MobileCore.initialize(initOptions:initOptions); expect(log, [ isMethodCall( - 'extensionVersion', - arguments: null, + 'initialize', + arguments: { + 'initOptions': { + 'appId': appId, + 'lifecycleAutomaticTrackingEnabled': lifecycleAutomaticTrackingEnabled, + 'lifecycleAdditionalContextData': lifecycleAdditionalContextData, + 'appGroupIOS': appGroup, + }, + }, ), ]); }); + }); - test('returns correct result', () async { - expect(await MobileCore.extensionVersion, testVersion); + group('initializeWithAppid', () { + final String appId = "12345"; + + final List log = []; + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { + await MobileCore.initializeWithAppId(appId:appId); + + expect(log, [ + isMethodCall( + 'initialize', + arguments: { + 'initOptions': { + 'appId': appId, + 'lifecycleAutomaticTrackingEnabled': null, + 'lifecycleAdditionalContextData': null, + 'appGroupIOS': null, + }, + }, + ), + ]); }); }); @@ -53,12 +138,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await MobileCore.trackAction(testAction, data: testContextData); @@ -74,6 +163,7 @@ void main() { ]); }); }); + group('trackState', () { final String testState = "myTestState"; @@ -83,12 +173,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.trackState(testState, data: testContextData); @@ -109,13 +204,18 @@ void main() { final String testAdId = "test-aid"; final List log = []; - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.setAdvertisingIdentifier(testAdId); @@ -139,12 +239,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return true; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.dispatchEvent(expectedEvent); @@ -175,12 +280,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return returnedEvent.data; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + log.add(methodCall); + return returnedEvent.data; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.dispatchEventWithResponseCallback(expectedEvent, 1000); @@ -227,13 +337,18 @@ void main() { final String testSdkIdentities = "sdkIdentities"; final List log = []; - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testSdkIdentities; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.sdkIdentities; @@ -254,13 +369,18 @@ void main() { group('getPrivacyStatus', () { final List log = []; - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return PrivacyStatus.opt_in.value; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.privacyStatus; @@ -283,12 +403,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.setLogLevel(logLevel); @@ -305,13 +430,17 @@ void main() { final PrivacyStatus privacyStatus = PrivacyStatus.opt_in; final List log = []; - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await MobileCore.setPrivacyStatus(privacyStatus); @@ -329,12 +458,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.updateConfiguration(testConfig); @@ -351,12 +485,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.clearUpdatedConfiguration(); @@ -370,13 +509,18 @@ void main() { final Map testPiiData = {"testKey": "testValue"}; final List log = []; - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.collectPii(testPiiData); @@ -394,12 +538,17 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method', () async { await MobileCore.setAppGroup(testAppGroup); diff --git a/plugins/flutter_aepcore/test/flutter_aepidentity_test.dart b/plugins/flutter_aepcore/test/flutter_aepidentity_test.dart index c7385cd7..f16830f9 100644 --- a/plugins/flutter_aepcore/test/flutter_aepidentity_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aepidentity_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.extensionVersion; @@ -51,12 +55,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedUrl; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.appendToUrl(inputUrl); @@ -89,12 +97,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return serializedIds; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.identifiers; @@ -118,12 +130,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testMcId; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.experienceCloudId; @@ -149,12 +165,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.syncIdentifier(testIdType, testId, testState); @@ -181,12 +201,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.syncIdentifiers(testIds); @@ -211,12 +235,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.syncIdentifiersWithAuthState(testIds, testState); @@ -234,12 +262,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedVariables; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.urlVariables; diff --git a/plugins/flutter_aepcore/test/flutter_aeplifecycle_test.dart b/plugins/flutter_aepcore/test/flutter_aeplifecycle_test.dart index 75543085..153a1a6a 100644 --- a/plugins/flutter_aepcore/test/flutter_aeplifecycle_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aeplifecycle_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Lifecycle.extensionVersion; diff --git a/plugins/flutter_aepcore/test/flutter_aepsignal_test.dart b/plugins/flutter_aepcore/test/flutter_aepsignal_test.dart index 1570158b..9e85cebf 100644 --- a/plugins/flutter_aepcore/test/flutter_aepsignal_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aepsignal_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Signal.extensionVersion; diff --git a/plugins/flutter_aepedge/README.md b/plugins/flutter_aepedge/README.md index a0d3bb47..78d1592e 100644 --- a/plugins/flutter_aepedge/README.md +++ b/plugins/flutter_aepedge/README.md @@ -17,91 +17,27 @@ Install instructions for this package can be found [here](https://pub.dev/packag > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests - -Run: - -```bash -flutter test -``` - ## Usage For more detailed information on the Edge APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/edge-network/) -### Registering the extension with AEPCore: - - > Note: It is required to initialize the SDK via native code inside your AppDelegate (iOS) and MainApplication class (Android). +### Importing the extension: -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. - -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. - -**Initialization Example** - -iOS -```objc -// AppDelegate.h -@import AEPCore; -@import AEPEdge; -@import AEPEdgeIdentity; -... -@implementation AppDelegate - -// AppDelegate.m -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - NSString* ENVIRONMENT_FILE_ID = @"YOUR-APP-ID"; - - NSArray *extensionsToRegister = @[AEPMobileEdgeIdentity.class, - AEPMobileEdge.class - ]; - - [AEPMobileCore registerExtensions:extensionsToRegister completion:^{ - [AEPMobileCore configureWithAppId: ENVIRONMENT_FILE_ID]; - }]; - return YES; - } -``` - -Android -```java -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Edge; -import com.adobe.marketing.mobile.edge.identity.Identity; -... -import io.flutter.app.FlutterApplication; -... -public class MainApplication extends FlutterApplication { - ... - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - private final String ENVIRONMENT_FILE_ID = "YOUR-APP-ID"; - - @Override - public void on Create(){ - super.onCreate(); - ... - MobileCore.setApplication(this); - MobileCore.setWrapperType(WrapperType.FLUTTER); - MobileCore.configureWithAppID(ENVIRONMENT_FILE_ID); - - MobileCore.registerExtensions( - Arrays.asList(Edge.EXTENSION, Identity.EXTENSION), - o -> Log.d("MainApp", "Adobe Experience Platform Mobile SDK was initialized.") - ); - } -} -``` ------- -### Importing the extension In your Flutter application, import the Edge extension as follows: + ```dart import 'package:flutter_aepedge/flutter_aepedge.dart'; ``` ------- +### Initializing with SDK: + +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) + +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. + ## API reference + ### extensionVersion Returns the SDK version of the Edge Network extension. @@ -318,6 +254,14 @@ static const String _payload = 'payload'; ## Next steps - Schemas setup and validation with Assurance For examples on XDM schemas and datasets setup and tips on validating with Assurance, refer to the [Edge Network tutorial](https://github.com/adobe/aepsdk-edge-ios/blob/main/Documentation/Tutorials). +## Tests + +Run: + +```bash +flutter test +``` + ## Contributing See [CONTRIBUTING](https://github.com/adobe/aepsdk_flutter/blob/main/CONTRIBUTING.md) diff --git a/plugins/flutter_aepedge/android/build.gradle b/plugins/flutter_aepedge/android/build.gradle index a1d49a32..c9666829 100644 --- a/plugins/flutter_aepedge/android/build.gradle +++ b/plugins/flutter_aepedge/android/build.gradle @@ -38,5 +38,6 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:edge:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api "com.adobe.marketing.mobile:edge" } diff --git a/plugins/flutter_aepedge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedge/FlutterAEPEdgePlugin.java b/plugins/flutter_aepedge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedge/FlutterAEPEdgePlugin.java index 2b0d330a..0c016d75 100644 --- a/plugins/flutter_aepedge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedge/FlutterAEPEdgePlugin.java +++ b/plugins/flutter_aepedge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedge/FlutterAEPEdgePlugin.java @@ -44,7 +44,7 @@ public class FlutterAEPEdgePlugin implements FlutterPlugin, MethodCallHandler { @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepedge"); - channel.setMethodCallHandler(new FlutterAEPEdgePlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepedge/ios/flutter_aepedge.podspec b/plugins/flutter_aepedge/ios/flutter_aepedge.podspec index ab63eb1f..f70dc8ff 100644 --- a/plugins/flutter_aepedge/ios/flutter_aepedge.podspec +++ b/plugins/flutter_aepedge/ios/flutter_aepedge.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_aepedge' - s.version = '4.1.0' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform Edge Network extension for Flutter apps.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepedge/pubspec.yaml b/plugins/flutter_aepedge/pubspec.yaml index baabb0f4..50ef3643 100644 --- a/plugins/flutter_aepedge/pubspec.yaml +++ b/plugins/flutter_aepedge/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_aepedge description: Official Adobe Experience Platform support for Flutter apps. The Experience Platform Edge extension enables sending data to the Adobe Experience Edge from a mobile device. -version: 4.1.0 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedge @@ -12,8 +12,15 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" - flutter_aepedgeidentity: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + flutter_aepedgeidentity: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore + + flutter_aepedgeidentity: + path: ../flutter_aepedgeidentity dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepedge/test/flutter_aepedge_test.dart b/plugins/flutter_aepedge/test/flutter_aepedge_test.dart index 4489b2a1..0c75ddb9 100644 --- a/plugins/flutter_aepedge/test/flutter_aepedge_test.dart +++ b/plugins/flutter_aepedge/test/flutter_aepedge_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Edge.extensionVersion; @@ -77,11 +81,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return [expectedEventHandle]; }); }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Edge.sendEvent(experienceEvent); @@ -248,11 +257,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return [expectedEventHandle, expectedEventHandle2]; }); }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method happy test', () async { await Edge.sendEvent(experienceEvent); @@ -280,12 +294,16 @@ void main() { setUp(() { log.clear(); - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Edge.setLocationHint(testLocationHint); @@ -315,12 +333,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testGetLocationHint; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Edge.locationHint; @@ -338,7 +360,7 @@ void main() { }); test('returns correct result null', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.clear(); return testGetLocationHintNull; }); diff --git a/plugins/flutter_aepedgebridge/README.md b/plugins/flutter_aepedgebridge/README.md index a6a6322a..a7057e8c 100644 --- a/plugins/flutter_aepedgebridge/README.md +++ b/plugins/flutter_aepedgebridge/README.md @@ -25,110 +25,35 @@ Install instructions for this package can be found [here](https://pub.dev/packag > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests - -Run: - -```bash -flutter test -``` - ## Usage -### Installing and registering the extension with the AEP Mobile Core - Install the Adobe Experience Platform Edge Network extension in your mobile property and configure the default Datastream ID by following the steps in the [Edge Network extension documentation](https://developer.adobe.com/client-sdks/documentation/edge-network). > **Note** Experience Platform Edge Bridge does not have a corresponding extension card in the Data Collection UI; no changes to a Data Collection mobile property are required to use Edge Bridge. -### Registering the extension with AEPCore: - -> **Note** -It is required to initialize the SDK via native code inside your AppDelegate (iOS) and MainApplication class (Android). - -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. - -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. - -**Initialization Example** - -iOS -```objc -// AppDelegate.h -@import AEPCore; -@import AEPEdge; -@import AEPEdgeIdentity; -@import AEPEdgeBridge; -... -@implementation AppDelegate - -// AppDelegate.m -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - NSString* ENVIRONMENT_FILE_ID = @"YOUR-APP-ID"; - - NSArray *extensionsToRegister = @[AEPMobileEdgeIdentity.class, - AEPMobileEdge.class, - AEPMobileEdgeBridge.class - ]; - - [AEPMobileCore registerExtensions:extensionsToRegister completion:^{ - [AEPMobileCore configureWithAppId: ENVIRONMENT_FILE_ID]; - }]; - return YES; - } -``` - -Android -```java -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Edge; -import com.adobe.marketing.mobile.edge.identity.Identity; -import com.adobe.marketing.mobile.edge.bridge.EdgeBridge - -... -import io.flutter.app.FlutterApplication; -... -public class MainApplication extends FlutterApplication { - ... - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - private final String ENVIRONMENT_FILE_ID = "YOUR-APP-ID"; - - @Override - public void on Create(){ - super.onCreate(); - ... - MobileCore.setApplication(this); - MobileCore.setWrapperType(WrapperType.FLUTTER); - MobileCore.configureWithAppID(ENVIRONMENT_FILE_ID); - - MobileCore.registerExtensions( - Arrays.asList(Identity.EXTENSION, Edge.EXTENSION, EdgeBridge.EXTENSION), - o -> Log.d("MainApp", "Adobe Experience Platform Mobile SDK was initialized") - ); - } -} -``` - ### Importing the SDK: ```dart import 'package:flutter_aepedgebridge/flutter_aepedgebridge.dart'; ``` +### Initializing with SDK: + +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) + +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. ### Edge Bridge tutorials -For tutorials on implementing Edge Bridge and Data Prep mapping, refer to the [Edge Bridge tutorials](https://github.com/adobe/aepsdk-edgebridge-ios/tree/main/Documentation/tutorials). +For tutorials on implementing Edge Bridge, refer to the [Edge Bridge tutorials](https://github.com/adobe/aepsdk-edgebridge-ios/tree/main/Documentation/tutorials). ### Validation -Validating Edge Bridge events through Edge workflow using the [Event Transations](https://developer.adobe.com/client-sdks/edge/edge-network/validation/#use-the-event-transactions-view) view or [Analytics Events 2.0](https://experienceleague.adobe.com/en/docs/experience-platform/assurance/view/adobe-analytics-edge) view in Assurance. - +Validating Edge Bridge events through Edge workflow using the [Event Transations](https://developer.adobe.com/client-sdks/edge/edge-network/validation/validation/#use-the-event-transactions-view) view or [Analytics Events](https://experienceleague.adobe.com/en/docs/experience-platform/assurance/view/adobe-analytics-edge) view in Assurance. ## API reference @@ -143,4 +68,11 @@ static Future get extensionVersion **Example** ```dart String version = await EdgeBridge.extensionVersion; -``` \ No newline at end of file +``` +## Tests + +Run: + +```bash +flutter test +``` diff --git a/plugins/flutter_aepedgebridge/android/build.gradle b/plugins/flutter_aepedgebridge/android/build.gradle index 2d157917..00aecf2f 100644 --- a/plugins/flutter_aepedgebridge/android/build.gradle +++ b/plugins/flutter_aepedgebridge/android/build.gradle @@ -38,5 +38,6 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:edgebridge:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api "com.adobe.marketing.mobile:edgebridge" } diff --git a/plugins/flutter_aepedgebridge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgebridge/FlutterAEPEdgeBridgePlugin.java b/plugins/flutter_aepedgebridge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgebridge/FlutterAEPEdgeBridgePlugin.java index 21605f34..3217bf16 100644 --- a/plugins/flutter_aepedgebridge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgebridge/FlutterAEPEdgeBridgePlugin.java +++ b/plugins/flutter_aepedgebridge/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgebridge/FlutterAEPEdgeBridgePlugin.java @@ -28,7 +28,7 @@ public class FlutterAEPEdgeBridgePlugin implements FlutterPlugin, MethodCallHand @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepedgebridge"); - channel.setMethodCallHandler(new FlutterAEPEdgeBridgePlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepedgebridge/ios/flutter_aepedgeBridge.podspec b/plugins/flutter_aepedgebridge/ios/flutter_aepedgeBridge.podspec index fd368f2f..3604e0b4 100644 --- a/plugins/flutter_aepedgebridge/ios/flutter_aepedgeBridge.podspec +++ b/plugins/flutter_aepedgebridge/ios/flutter_aepedgeBridge.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'flutter_aepedgebridge' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform Edge Bridge support for Flutter apps.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepedgebridge/pubspec.yaml b/plugins/flutter_aepedgebridge/pubspec.yaml index 13abd3df..df88a4a9 100644 --- a/plugins/flutter_aepedgebridge/pubspec.yaml +++ b/plugins/flutter_aepedgebridge/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_aepedgebridge description: Official Adobe Experience Platform support for Flutter apps. The AEP Edge Bridge mobile extension provides seamless routing of data to the Adobe Experience Platform Edge Network for existing SDK implementations. -version: 4.0.2 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedgebridge @@ -11,7 +11,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepedgebridge/test/flutter_aepedgebridge_test.dart b/plugins/flutter_aepedgebridge/test/flutter_aepedgebridge_test.dart index 7d672113..b30836fa 100644 --- a/plugins/flutter_aepedgebridge/test/flutter_aepedgebridge_test.dart +++ b/plugins/flutter_aepedgebridge/test/flutter_aepedgebridge_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await EdgeBridge.extensionVersion; diff --git a/plugins/flutter_aepedgeconsent/README.md b/plugins/flutter_aepedgeconsent/README.md index e6a42350..6df2702c 100644 --- a/plugins/flutter_aepedgeconsent/README.md +++ b/plugins/flutter_aepedgeconsent/README.md @@ -16,94 +16,23 @@ Install instructions for this package can be found [here](https://pub.dev/packag > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests - -Run: - -```bash -flutter test -``` - ## Usage For more detailed information on the Consent APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/consent-for-edge-network/) -### Registering the extension with AEPCore: - - > Note: It is required to initialize the SDK via native code inside your AppDelegate (iOS) and MainApplication class (Android). - -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. - -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. - -**Initialization Example** - -iOS -```objc -// AppDelegate.h -@import AEPCore; -@import AEPEdge; -@import AEPEdgeIdentity; -@import AEPEdgeConsent; -... -@implementation AppDelegate - -// AppDelegate.m -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - NSString* ENVIRONMENT_FILE_ID = @"YOUR-APP-ID"; - - NSArray *extensionsToRegister = @[AEPMobileEdgeIdentity.class, - AEPMobileEdge.class, - AEPMobileEdgeConsent.class - ]; - - [AEPMobileCore registerExtensions:extensionsToRegister completion:^{ - [AEPMobileCore configureWithAppId: ENVIRONMENT_FILE_ID]; - }]; - return YES; - } -``` - -Android -```java -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Edge; -import com.adobe.marketing.mobile.edge.identity.Identity; -import com.adobe.marketing.mobile.edge.consent.Consent; - -... -import io.flutter.app.FlutterApplication; -... -public class MainApplication extends FlutterApplication { - ... - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - private final String ENVIRONMENT_FILE_ID = "YOUR-APP-ID"; - - @Override - public void on Create(){ - super.onCreate(); - ... - MobileCore.setApplication(this); - MobileCore.setWrapperType(WrapperType.FLUTTER); - MobileCore.configureWithAppID(ENVIRONMENT_FILE_ID); - - MobileCore.registerExtensions( - Arrays.asList(Consent.EXTENSION, Identity.EXTENSION, Edge.EXTENSION), - o -> Log.d("MainApp", "Adobe Experience Platform Mobile SDK was initialized") - ); - } -} -``` ------- ### Importing the SDK: ```dart import 'package:flutter_aepedgeconsent/flutter_aepedgeconsent.dart'; ``` ------- +### Initializing with SDK: + +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) + +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. + ## API reference ### extensionVersion Returns the SDK version of the Consent extension. @@ -158,6 +87,14 @@ Map currentConsents = {"consents": collectConsents}; Consent.update(currentConsents); ``` +## Tests + +Run: + +```bash +flutter test +``` + ## Contributing See [CONTRIBUTING](https://github.com/adobe/aepsdk_flutter/blob/main/CONTRIBUTING.md) diff --git a/plugins/flutter_aepedgeconsent/android/build.gradle b/plugins/flutter_aepedgeconsent/android/build.gradle index 75f4c2d6..91319fe5 100644 --- a/plugins/flutter_aepedgeconsent/android/build.gradle +++ b/plugins/flutter_aepedgeconsent/android/build.gradle @@ -38,5 +38,6 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:edgeconsent:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api "com.adobe.marketing.mobile:edgeconsent" } diff --git a/plugins/flutter_aepedgeconsent/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeconsent/FlutterAEPEdgeConsentPlugin.java b/plugins/flutter_aepedgeconsent/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeconsent/FlutterAEPEdgeConsentPlugin.java index 83b5c481..a1402007 100644 --- a/plugins/flutter_aepedgeconsent/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeconsent/FlutterAEPEdgeConsentPlugin.java +++ b/plugins/flutter_aepedgeconsent/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeconsent/FlutterAEPEdgeConsentPlugin.java @@ -36,7 +36,7 @@ public class FlutterAEPEdgeConsentPlugin implements FlutterPlugin, MethodCallHan @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepedgeconsent"); - channel.setMethodCallHandler(new FlutterAEPEdgeConsentPlugin()); + channel.setMethodCallHandler(this); } @Override @@ -75,12 +75,13 @@ public void fail(final AdobeError adobeError) { }); } + @SuppressWarnings("unchecked") private void handleUpdateConsents(final Object arguments) { if (!(arguments instanceof Map)) { Log.e(TAG, "Update Consent failed because arguments were invalid, expected Map."); return; } - Consent.update((Map) arguments); + Consent.update((Map) arguments); } } diff --git a/plugins/flutter_aepedgeconsent/ios/flutter_aepedgeconsent.podspec b/plugins/flutter_aepedgeconsent/ios/flutter_aepedgeconsent.podspec index 9fba41bb..6c8c182a 100644 --- a/plugins/flutter_aepedgeconsent/ios/flutter_aepedgeconsent.podspec +++ b/plugins/flutter_aepedgeconsent/ios/flutter_aepedgeconsent.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'flutter_aepedgeconsent' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform Consent Collection support for Flutter apps.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepedgeconsent/pubspec.yaml b/plugins/flutter_aepedgeconsent/pubspec.yaml index 14a82437..2103e1fd 100644 --- a/plugins/flutter_aepedgeconsent/pubspec.yaml +++ b/plugins/flutter_aepedgeconsent/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_aepedgeconsent description: Official Adobe Experience Platform support for Flutter apps. The AEP Consent Collection plugin enables consent preferences collection from your Flutter app when using the Adobe Experience Platform Mobile SDK and the Edge Network extension. -version: 4.0.2 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedgeconsent @@ -12,7 +12,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepedgeconsent/test/flutter_aepedgeconsent_test.dart b/plugins/flutter_aepedgeconsent/test/flutter_aepedgeconsent_test.dart index e5e0f782..1010e7e1 100644 --- a/plugins/flutter_aepedgeconsent/test/flutter_aepedgeconsent_test.dart +++ b/plugins/flutter_aepedgeconsent/test/flutter_aepedgeconsent_test.dart @@ -23,7 +23,7 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); @@ -55,7 +55,7 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedConsent; }); @@ -86,12 +86,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Consent.update(expectedSetConsent); diff --git a/plugins/flutter_aepedgeidentity/README.md b/plugins/flutter_aepedgeidentity/README.md index d1a22eee..2b03cc56 100644 --- a/plugins/flutter_aepedgeidentity/README.md +++ b/plugins/flutter_aepedgeidentity/README.md @@ -16,89 +16,24 @@ Install instructions for this package can be found [here](https://pub.dev/packag > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests -Run: - -```bash -flutter test -``` - ## Usage For more detailed information on the Edge Identity APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/identity-for-edge-network/) -### Registering the extension with AEPCore: - - > Note: It is required to initialize the SDK via native code inside your AppDelegate (iOS) and MainApplication class (Android). - -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. - -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. - -**Initialization Example** - -iOS -```objc -// AppDelegate.h -@import AEPCore; -@import AEPEdge; -@import AEPEdgeIdentity; -... -@implementation AppDelegate - -// AppDelegate.m -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - NSString* ENVIRONMENT_FILE_ID = @'YOUR-APP-ID'; - - NSArray *extensionsToRegister = @[AEPMobileEdgeIdentity.class, - AEPMobileEdge.class - ]; - - [AEPMobileCore registerExtensions:extensionsToRegister completion:^{ - [AEPMobileCore configureWithAppId: ENVIRONMENT_FILE_ID]; - }]; - return YES; - } -``` - -Android -```java -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Edge; -import com.adobe.marketing.mobile.edge.identity.Identity; -... -import io.flutter.app.FlutterApplication; -... -public class MainApplication extends FlutterApplication { - ... - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - private final String ENVIRONMENT_FILE_ID = 'YOUR-APP-ID'; - - @Override - public void on Create(){ - super.onCreate(); - ... - MobileCore.setApplication(this); - MobileCore.setWrapperType(WrapperType.FLUTTER); - MobileCore.configureWithAppID(ENVIRONMENT_FILE_ID); - - MobileCore.registerExtensions( - Arrays.asList(Edge.EXTENSION, Identity.EXTENSION), - o -> Log.d("MainApp", "Adobe Experience Platform Mobile SDK was initialized.") - ); - } -} -``` ------- -### Importing the extension +### Importing the extension: In your Flutter application, import the Edge Identity extension as follows: ```dart import 'package:flutter_aepedgeidentity/flutter_aepedgeidentity.dart'; ``` ------- + +### Initializing with SDK: + +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) + +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. + ## API reference ### extensionVersion Returns the SDK version of the Identity for Edge Network extension. @@ -350,6 +285,13 @@ enum AuthenticatedState { AUTHENTICATED, LOGGED_OUT, AMBIGUOUS } IdentityItem item = new IdentityItem('identifier', AuthenticatedState.AUTHENTICATED, false); ``` +## Tests + +Run: + +```bash +flutter test +``` ## Frequently Asked Questions (FAQ) For more details, refer to the [frequently asked questions page](https://developer.adobe.com/client-sdks/documentation/identity-for-edge-network/faq/) diff --git a/plugins/flutter_aepedgeidentity/android/build.gradle b/plugins/flutter_aepedgeidentity/android/build.gradle index aedd36b6..4d2c16df 100644 --- a/plugins/flutter_aepedgeidentity/android/build.gradle +++ b/plugins/flutter_aepedgeidentity/android/build.gradle @@ -38,5 +38,6 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:edgeidentity:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api "com.adobe.marketing.mobile:edgeidentity" } diff --git a/plugins/flutter_aepedgeidentity/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeidentity/FlutterAEPEdgeIdentityPlugin.java b/plugins/flutter_aepedgeidentity/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeidentity/FlutterAEPEdgeIdentityPlugin.java index f2497591..b139cf96 100644 --- a/plugins/flutter_aepedgeidentity/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeidentity/FlutterAEPEdgeIdentityPlugin.java +++ b/plugins/flutter_aepedgeidentity/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepedgeidentity/FlutterAEPEdgeIdentityPlugin.java @@ -40,7 +40,7 @@ public class FlutterAEPEdgeIdentityPlugin implements FlutterPlugin, MethodCallHa @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepedgeidentity"); - channel.setMethodCallHandler(new FlutterAEPEdgeIdentityPlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepedgeidentity/ios/flutter_aepedgeidentity.podspec b/plugins/flutter_aepedgeidentity/ios/flutter_aepedgeidentity.podspec index 6c7ad668..387545da 100644 --- a/plugins/flutter_aepedgeidentity/ios/flutter_aepedgeidentity.podspec +++ b/plugins/flutter_aepedgeidentity/ios/flutter_aepedgeidentity.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_aepedgeidentity' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform Identity for Edge Network extension for Adobe Experience Platform Mobile SDK. Written and maintained by Adobe.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepedgeidentity/pubspec.yaml b/plugins/flutter_aepedgeidentity/pubspec.yaml index 6743bf3b..378a1b82 100644 --- a/plugins/flutter_aepedgeidentity/pubspec.yaml +++ b/plugins/flutter_aepedgeidentity/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_aepedgeidentity description: Official Adobe Experience Platform support for Flutter apps. The Experience Platform Edge Identity extension enables handling of user identity data from a mobile app when using the Adobe Experience Platform SDK and the Edge Network extension. -version: 4.0.2 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepedgeidentity @@ -12,7 +12,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentity_test.dart b/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentity_test.dart index 2c0f33da..b17832b0 100644 --- a/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentity_test.dart +++ b/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentity_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.extensionVersion; @@ -50,12 +54,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testECID; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.experienceCloudId; @@ -78,12 +86,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedVariables; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.urlVariables; @@ -114,12 +126,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return {}; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('returns correct result', () async { IdentityMap currentIdentity = await Identity.identities; currentIdentity.addItem(item1, "namespace1"); @@ -146,12 +162,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return {}; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('returns correct result', () async { IdentityMap currentIdentity = await Identity.identities; currentIdentity.addItem(item1, "namespace1"); @@ -188,12 +208,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedMap; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { idmap.addItem(item1, "namespace1"); idmap.addItem(item2, "namespace2"); @@ -224,12 +248,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Identity.removeIdentity(item1, 'namespace1'); diff --git a/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart b/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart index a5ad5adc..63a05059 100644 --- a/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart +++ b/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart @@ -40,12 +40,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedItemsForNamespace1; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('is validated', () async { //test with getNamespaces() final int actualGetNameSpacesLength = idMap.getNamespaces().length; @@ -95,12 +99,15 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { return expectedItemsForNamespace1; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('test an invalid namespace', () async { final int actualItemsForNamespaceInvalidLength = idMap.getIdentityItemsForNamespace('invalid').length; @@ -136,12 +143,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return {}; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('same id and same item, should ignore the new added item', () async { final List actualSameIdSameItem = idMap.getIdentityItemsForNamespace('namespace1'); @@ -193,12 +204,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedItemsForNamespace2; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('is validated', () async { //test with getNamespaces() final int actualGetNameSpacesLength = idMap.getNamespaces().length; @@ -239,12 +254,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('is validated', () async { //test isEmpty false expect(idMap.isEmpty(), true); diff --git a/plugins/flutter_aepmessaging/README.md b/plugins/flutter_aepmessaging/README.md index 82d807f1..ea97a289 100644 --- a/plugins/flutter_aepmessaging/README.md +++ b/plugins/flutter_aepmessaging/README.md @@ -18,89 +18,11 @@ Install instructions for this package can be found [here](https://pub.dev/packag > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests - -Run: - -```bash -flutter test -``` - ## Usage For more detailed information on the Messaging APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/adobe-journey-optimizer/api-reference/) -### Registering the extension with AEPCore: - -> Note: It is required to initialize the SDK via native code inside your AppDelegate (iOS) and MainApplication class (Android). - -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. - -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. - -**Initialization Example** - -iOS - -```objc -// AppDelegate.h -@import AEPCore; -@import AEPEdge; -@import AEPEdgeIdentity; -@import AEPMessaging; - -... -@implementation AppDelegate - -// AppDelegate.m -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [AEPMobileCore setWrapperType:AEPWrapperTypeFlutter]; - - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - NSString* ENVIRONMENT_FILE_ID = @'YOUR-APP-ID'; - - NSArray *extensionsToRegister = @[AEPMessaging.class, - AEPMobileEdge.class, - AEPMobileEdgeIdentity.class - ]; - - [AEPMobileCore registerExtensions:extensionsToRegister completion:^{ - [AEPMobileCore configureWithAppId: ENVIRONMENT_FILE_ID]; - }]; - return YES; - } -``` - -Android - -```java -import com.adobe.marketing.mobile.MobileCore; -import com.adobe.marketing.mobile.Edge; -import com.adobe.marketing.mobile.messaging.Messaging; -... -import io.flutter.app.FlutterApplication; -... -public class MainApplication extends FlutterApplication { - ... - // TODO: Set up the preferred Environment File ID from your mobile property configured in Data Collection UI - private final String ENVIRONMENT_FILE_ID = "YOUR-APP-ID"; - - @Override - public void onCreate() { - super.onCreate(); - List> extensions = Arrays.asList( - Edge.EXTENSION, - EdgeIdentity.EXTENSION, - Messaging.EXTENSION - ); - MobileCore.registerExtensions(extensions, o -> MobileCore.configureWithAppID(ENVIRONMENT_FILE_ID)); - } -} -``` - ---- - -### Importing the extension +### Importing the extension: In your Flutter application, import the Messaging extension as follows: @@ -108,7 +30,13 @@ In your Flutter application, import the Messaging extension as follows: import 'package:flutter_aepmessaging/flutter_aepmessaging.dart'; ``` ---- +### Initializing with SDK: + +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) + +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. ## API reference @@ -265,6 +193,14 @@ The AEPMessaging extension's push messaging APIs must be called from the native In Android, [MessagingPushPayload](https://github.com/adobe/aepsdk-messaging-android/blob/main/Documentation/sources/messaging-push-payload.md#messagingpushpayload-usage) can be used for getting the notification attributes like title, body, and action. These are useful for push notification creation. +## Tests + +Run: + +```bash +flutter test +``` + ## Contributing See [CONTRIBUTING](https://github.com/adobe/aepsdk_flutter/blob/main/CONTRIBUTING.md) diff --git a/plugins/flutter_aepmessaging/android/build.gradle b/plugins/flutter_aepmessaging/android/build.gradle index 4efdb225..bc43e5d6 100644 --- a/plugins/flutter_aepmessaging/android/build.gradle +++ b/plugins/flutter_aepmessaging/android/build.gradle @@ -55,5 +55,6 @@ android { } dependencies { - api 'com.adobe.marketing.mobile:messaging:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api "com.adobe.marketing.mobile:messaging" } diff --git a/plugins/flutter_aepmessaging/ios/flutter_aepmessaging.podspec b/plugins/flutter_aepmessaging/ios/flutter_aepmessaging.podspec index 794d3071..74d13416 100644 --- a/plugins/flutter_aepmessaging/ios/flutter_aepmessaging.podspec +++ b/plugins/flutter_aepmessaging/ios/flutter_aepmessaging.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_aepmessaging' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform Messaging extension for Flutter apps' s.homepage = 'https://developer.adobe.com/client-sdks/documentation' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepmessaging/pubspec.yaml b/plugins/flutter_aepmessaging/pubspec.yaml index 9a4f8864..17be34eb 100644 --- a/plugins/flutter_aepmessaging/pubspec.yaml +++ b/plugins/flutter_aepmessaging/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_aepmessaging description: Official Adobe Experience Platform support for Flutter apps. The Experience Platform Messaging extension enables handling of user push an in-app messages from a mobile app when using the Adobe Experience Platform SDK. -version: 4.0.2 +version: 5.0.0 homepage: https://aep-sdks.gitbook.io/docs/ repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepmessaging @@ -12,7 +12,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepmessaging/test/flutter_aepmessaging_test.dart b/plugins/flutter_aepmessaging/test/flutter_aepmessaging_test.dart index 17f2b7aa..10819118 100644 --- a/plugins/flutter_aepmessaging/test/flutter_aepmessaging_test.dart +++ b/plugins/flutter_aepmessaging/test/flutter_aepmessaging_test.dart @@ -23,12 +23,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await Messaging.extensionVersion; @@ -49,12 +53,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { Messaging.refreshInAppMessages(); diff --git a/plugins/flutter_aepuserprofile/README.md b/plugins/flutter_aepuserprofile/README.md index c6af58e8..811b51de 100644 --- a/plugins/flutter_aepuserprofile/README.md +++ b/plugins/flutter_aepuserprofile/README.md @@ -4,66 +4,100 @@ `flutter_aepuserprofile` is a flutter plugin for the iOS and Android [Adobe Experience Platform UserProfile SDK](https://developer.adobe.com/client-sdks/documentation/profile/) to allow for integration with Flutter applications. Functionality to enable the UserProfile extension is provided entirely through Dart documented below. -## Installation +## Prerequisites + +The Userprofile plugin has the following peer dependency, which must be installed prior to installing it: + +- [flutter_aepcore](https://github.com/adobe/aepsdk_flutter/blob/main/plugins/flutter_aepcore/README.md) -First, make sure that the [flutter_aepcore](https://github.com/adobe/aepsdk_flutter/blob/main/plugins/flutter_aepcore/README.md) plugin is installed, as flutter_aepuserprofile depends on it. +## Installation Install instructions for this package can be found [here](https://pub.dev/packages/flutter_aepuserprofile/install). > Note: After you have installed the SDK, don't forget to run `pod install` in your `ios` directory to link the libraries to your Xcode project. -## Tests +## Usage -Run: +For more detailed information on the UserProfile APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/profile/api-reference/) -```bash -flutter test -``` +### Importing the extension: -## Usage -### UserProfile +In your Flutter application, import the Userprofile package as follows: -For more detailed information on the UserProfile APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/profile/api-reference/) +```dart +import 'package:flutter_aepuserprofile/flutter_aepuserprofile.dart'; +``` +### Initializing with SDK: -##### Registering the extension with AEPCore: +To initialize the SDK, use the following methods: +- [MobileCore.initializeWithAppId(appId)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initializewithappid) +- [MobileCore.initialize(initOptions)](https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepcore#initialize) - > Note: It is required to initialize the SDK via native code inside your AppDelegate and MainApplication for iOS and Android respectively. +Refer to the root [Readme](https://github.com/adobe/aepsdk_flutter/blob/main/README.md) for more information about the SDK setup. -As part of the initialization code, make sure that you set the SDK wrapper type to `Flutter` before you start the SDK. +## API reference -Refer to the [Initialization](https://github.com/adobe/aepsdk_flutter#initializing) section of the root README for more information about initializing the SDK. +### extensionVersion +Returns the SDK version of the User Proilfe extension. -##### Importing the SDK: +**Syntax** ```dart -import 'package:flutter_aepuserprofile/flutter_aepuserprofile.dart'; +static Future get extensionVersion ``` - -##### Getting UserProfile version: - ```dart +**Example** +```dart String version = await UserProfile.extensionVersion; - ``` +``` -##### Get user profile attributes which match the provided keys: +### getUserAttributes +Get user profile attributes which match the provided keys. - ```dart +**Syntax** +```dart +static Future getUserAttributes(List attributeKeys) +``` + +**Example** +```dart try { String userAttributes = await UserProfile.getUserAttributes(["attr1", "attr2"]); } on PlatformException { log("Failed to get the user attributes"); } - ``` +``` + +### removeUserAttributes +Remove provided user profile attributes if they exist. - ##### Remove provided user profile attributes if they exist: +**Syntax** +```dart +static Future removeUserAttributes(List attributeName) +``` +**Example** ```dart UserProfile.removeUserAttributes(["attr1", "attr2"]); ``` - ##### Set multiple user profile attributes: +### updateUserAttributes +Set multiple user profile attributes. +**Syntax** +```dart +static Future updateUserAttributes(Map attributeMap) +``` + +**Example** ```dart UserProfile.updateUserAttributes({"attr1": "attr1Value", "attr2": "attr2Value"}); ``` +## Tests + +Run: + +```bash +flutter test +``` ## Contributing See [CONTRIBUTING](https://github.com/adobe/aepsdk_flutter/blob/main/CONTRIBUTING.md) diff --git a/plugins/flutter_aepuserprofile/android/build.gradle b/plugins/flutter_aepuserprofile/android/build.gradle index 21037c60..783a5577 100644 --- a/plugins/flutter_aepuserprofile/android/build.gradle +++ b/plugins/flutter_aepuserprofile/android/build.gradle @@ -38,8 +38,8 @@ android { } } - dependencies { - api 'com.adobe.marketing.mobile:userprofile:3.+' + implementation platform("com.adobe.marketing.mobile:sdk-bom:3.+") + api 'com.adobe.marketing.mobile:userprofile' } diff --git a/plugins/flutter_aepuserprofile/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepuserprofile/FlutterAEPUserProfilePlugin.java b/plugins/flutter_aepuserprofile/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepuserprofile/FlutterAEPUserProfilePlugin.java index 9576a8a1..8bc768c2 100644 --- a/plugins/flutter_aepuserprofile/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepuserprofile/FlutterAEPUserProfilePlugin.java +++ b/plugins/flutter_aepuserprofile/android/src/main/java/com/adobe/marketing/mobile/flutter/flutter_aepuserprofile/FlutterAEPUserProfilePlugin.java @@ -46,7 +46,7 @@ public class FlutterAEPUserProfilePlugin implements FlutterPlugin, MethodCallHan @Override public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_aepuserprofile"); - channel.setMethodCallHandler(new FlutterAEPUserProfilePlugin()); + channel.setMethodCallHandler(this); } @Override diff --git a/plugins/flutter_aepuserprofile/ios/flutter_aepuserprofile.podspec b/plugins/flutter_aepuserprofile/ios/flutter_aepuserprofile.podspec index 910fcd2a..68f85b7e 100644 --- a/plugins/flutter_aepuserprofile/ios/flutter_aepuserprofile.podspec +++ b/plugins/flutter_aepuserprofile/ios/flutter_aepuserprofile.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_aepuserprofile' - s.version = '4.0.2' + s.version = '5.0.0' s.summary = 'Adobe Experience Platform User Profile support for Flutter apps.' s.homepage = 'https://developer.adobe.com/client-sdks' s.license = { :file => '../LICENSE' } diff --git a/plugins/flutter_aepuserprofile/pubspec.yaml b/plugins/flutter_aepuserprofile/pubspec.yaml index 7db7e6aa..7d33bdcc 100644 --- a/plugins/flutter_aepuserprofile/pubspec.yaml +++ b/plugins/flutter_aepuserprofile/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_aepuserprofile description: Official Adobe Experience Platform support for Flutter apps. The UserProfile extension represents the Adobe Experience Platform SDK's Profile extension which helps manage profile attributes in the client. -version: 4.0.2 +version: 5.0.0 homepage: https://developer.adobe.com/client-sdks repository: https://github.com/adobe/aepsdk_flutter/tree/main/plugins/flutter_aepuserprofile @@ -11,7 +11,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_aepcore: ">= 4.0.0 <5.0.0" + flutter_aepcore: ">=5.0.0 <6.0.0" + +dependency_overrides: + flutter_aepcore: + path: ../flutter_aepcore dev_dependencies: flutter_test: diff --git a/plugins/flutter_aepuserprofile/test/flutter_aepuserprofile_test.dart b/plugins/flutter_aepuserprofile/test/flutter_aepuserprofile_test.dart index 4ea70633..c4d7a7d5 100644 --- a/plugins/flutter_aepuserprofile/test/flutter_aepuserprofile_test.dart +++ b/plugins/flutter_aepuserprofile/test/flutter_aepuserprofile_test.dart @@ -12,12 +12,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testVersion; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await UserProfile.extensionVersion; @@ -34,17 +38,25 @@ void main() { }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + group('getUserAttributes', () { final String testUserAttributes = "userAttributes"; final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testUserAttributes; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await UserProfile.getUserAttributes([testUserAttributes]); @@ -68,12 +80,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await UserProfile.removeUserAttributes([testUserAttribute]); @@ -91,12 +107,16 @@ void main() { final List log = []; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + test('invokes correct method', () async { await UserProfile.updateUserAttributes(testUserAttribute);