From d6237c7fe38e3670cf855232b6ef2a7c350775bc Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 23 Apr 2026 20:57:15 -0300 Subject: [PATCH 1/9] refactor: clean unused packages on sample app --- .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ example/lib/core.dart | 1 - example/lib/identity.dart | 1 - example/lib/messaging.dart | 2 -- example/pubspec.lock | 24 +++++++------- 6 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 example/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 example/ios/Flutter/ephemeral/flutter_lldbinit diff --git a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 00000000..a88caf99 --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/example/ios/Flutter/ephemeral/flutter_lldbinit b/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 00000000..e3ba6fbe --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/example/lib/core.dart b/example/lib/core.dart index 8cd1d566..158bfa60 100644 --- a/example/lib/core.dart +++ b/example/lib/core.dart @@ -13,7 +13,6 @@ import 'package:flutter/material.dart'; import 'dart:developer'; import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore.dart'; -import 'package:flutter_aepcore/flutter_aepcore_data.dart'; import 'package:flutter_aepcore/flutter_aeplifecycle.dart'; import 'package:flutter_aepcore/flutter_aepsignal.dart'; import 'util.dart'; diff --git a/example/lib/identity.dart b/example/lib/identity.dart index 397e9ed5..885d56b6 100644 --- a/example/lib/identity.dart +++ b/example/lib/identity.dart @@ -12,7 +12,6 @@ governing permissions and limitations under the License. import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_aepcore/flutter_aepcore_data.dart'; import 'package:flutter_aepcore/flutter_aepidentity.dart'; import 'util.dart'; diff --git a/example/lib/messaging.dart b/example/lib/messaging.dart index 24a28e83..67cd85cf 100644 --- a/example/lib/messaging.dart +++ b/example/lib/messaging.dart @@ -11,9 +11,7 @@ governing permissions and limitations under the License. import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore.dart'; -import 'package:flutter_aepcore/flutter_aepcore_data.dart'; import 'package:flutter_aepmessaging/flutter_aepmessaging.dart'; import 'util.dart'; diff --git a/example/pubspec.lock b/example/pubspec.lock index 953803ad..4db25d67 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" clock: dependency: transitive description: @@ -75,7 +75,7 @@ packages: path: "../plugins/flutter_aepcore" relative: true source: path - version: "5.0.0" + version: "5.0.1" flutter_aepedge: dependency: "direct main" description: @@ -151,26 +151,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" path: dependency: transitive description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.10" vector_math: dependency: transitive description: @@ -249,5 +249,5 @@ packages: source: hosted version: "14.2.5" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.9.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" From efdf87a0adb9b929da84aab5dafc9ab435bd02b2 Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 23 Apr 2026 21:16:28 -0300 Subject: [PATCH 2/9] feat(core): add MobileCore.setPushIdentifier API Expose setPushIdentifier on Android and iOS so Flutter apps can register a push notification token directly through the Dart API without dropping down to native code. - Dart: MobileCore.setPushIdentifier(String? token) - Android: handleSetPushIdentifier follows handleSetAdvertisingIdentifier pattern - iOS: converts NSString token to NSData via NSUTF8StringEncoding before calling [AEPMobileCore setPushIdentifier:] - Tests: added group covering token and null cases - Docs: CHANGELOG 5.0.2 entry and README API section --- plugins/flutter_aepcore/CHANGELOG.md | 4 + plugins/flutter_aepcore/README.md | 17 ++ plugins/flutter_aepcore/android/.project | 28 +++ .../org.eclipse.buildship.core.prefs | 13 ++ .../flutter_aepcore/FlutterAEPCorePlugin.java | 13 ++ .../ios/Classes/FlutterAEPCorePlugin.m | 14 ++ .../flutter_aepcore/lib/flutter_aepcore.dart | 18 +- .../test/flutter_aepcore_test.dart | 211 +++++++++++------- .../test/flutter_aepedgeidentitymap_test.dart | 32 ++- 9 files changed, 262 insertions(+), 88 deletions(-) create mode 100644 plugins/flutter_aepcore/android/.project create mode 100644 plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs diff --git a/plugins/flutter_aepcore/CHANGELOG.md b/plugins/flutter_aepcore/CHANGELOG.md index c1745675..bce6bdea 100644 --- a/plugins/flutter_aepcore/CHANGELOG.md +++ b/plugins/flutter_aepcore/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.0.2 + +* Add `MobileCore.setPushIdentifier` API to register a push notification token with the Adobe SDK on Android and iOS. + ## 5.0.1 * Add `MobileCore.setApplication` call in Android FlutterPlugin's `onAttachedToEngine` to accurately register lifecycle callbacks for launcher activity. diff --git a/plugins/flutter_aepcore/README.md b/plugins/flutter_aepcore/README.md index 88fec909..1fb3b84a 100644 --- a/plugins/flutter_aepcore/README.md +++ b/plugins/flutter_aepcore/README.md @@ -327,6 +327,23 @@ static Future trackState MobileCore.trackState("myState", data: {"key1": "value1"}); ``` +### setPushIdentifier +Register a device push token with the Adobe SDK. Pass `null` to clear a previously registered token. + +**Syntax** +```dart +static Future setPushIdentifier(String? token) +``` + +**Example** +```dart +// Register the token obtained from FCM/APNs +MobileCore.setPushIdentifier("your-push-token"); + +// Clear the push token +MobileCore.setPushIdentifier(null); +``` + ### Identity For more information on the Core Identity APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/mobile-core/identity/). diff --git a/plugins/flutter_aepcore/android/.project b/plugins/flutter_aepcore/android/.project new file mode 100644 index 00000000..a4eccda7 --- /dev/null +++ b/plugins/flutter_aepcore/android/.project @@ -0,0 +1,28 @@ + + + android + Project android created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1776988886455 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs b/plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..a0ff068a --- /dev/null +++ b/plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments=--init-script /home/bruno/.cache/opencode/bin/jdtls/config_linux/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/home/bruno/.local/share/mise/installs/java/temurin-25.0.2+10.0.LTS +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true 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 52c5db54..abd32cb5 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 @@ -90,6 +90,9 @@ public void onMethodCall(MethodCall call, @NonNull Result result) { } else if ("setAdvertisingIdentifier".equals(call.method)) { handleSetAdvertisingIdentifier(call.arguments); result.success(null); + } else if ("setPushIdentifier".equals(call.method)) { + handleSetPushIdentifier(call.arguments); + result.success(null); } else if ("dispatchEvent".equals(call.method)) { handleDispatchEvent(result, call.arguments); } else if ("dispatchEventWithResponseCallback".equals(call.method)) { @@ -191,6 +194,16 @@ private void handleSetAdvertisingIdentifier(final Object arguments) { } } + private void handleSetPushIdentifier(final Object arguments) { + if (arguments == null) { + MobileCore.setPushIdentifier(null); + } + + if (arguments instanceof String) { + MobileCore.setPushIdentifier((String) arguments); + } + } + private void handleDispatchEvent(final Result result, final Object arguments) { if (!(arguments instanceof Map)) { Log.e(TAG, "Dispatch event failed because arguments were invalid"); diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m index c48fce8e..1d38454c 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m @@ -44,6 +44,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call NSString *aid = call.arguments; [AEPMobileCore setAdvertisingIdentifier:aid]; result(nil); + } else if ([@"setPushIdentifier" isEqualToString:call.method]) { + [self handleSetPushIdentifier:call]; + result(nil); } else if ([@"dispatchEvent" isEqualToString:call.method]) { [self handleDispatchEvent:call result:result]; } else if ([@"dispatchEventWithResponseCallback" @@ -198,6 +201,17 @@ - (void)handleResetIdentities:(FlutterMethodCall *)call { [AEPMobileCore resetIdentities]; } +- (void)handleSetPushIdentifier:(FlutterMethodCall *)call { + if (call.arguments == nil || call.arguments == [NSNull null]) { + [AEPMobileCore setPushIdentifier:nil]; + return; + } + + NSString *tokenString = call.arguments; + NSData *token = [tokenString dataUsingEncoding:NSUTF8StringEncoding]; + [AEPMobileCore setPushIdentifier:token]; +} + - (FlutterError *)flutterErrorFromNSError:(NSError *)error { return [FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", (long)error.code] diff --git a/plugins/flutter_aepcore/lib/flutter_aepcore.dart b/plugins/flutter_aepcore/lib/flutter_aepcore.dart index eca771f7..4a231e80 100644 --- a/plugins/flutter_aepcore/lib/flutter_aepcore.dart +++ b/plugins/flutter_aepcore/lib/flutter_aepcore.dart @@ -26,7 +26,8 @@ class MobileCore { /// 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()}); + return _channel + .invokeMethod('initialize', {'initOptions': initOptions.toMap()}); } /// Initializes the AEP Mobile SDK with the provided App ID. @@ -69,6 +70,21 @@ class MobileCore { static Future setAdvertisingIdentifier(String aid) => _channel.invokeMethod('setAdvertisingIdentifier', aid); + /// Submits a generic event containing the provided push token with event type `generic.identity`. + /// + /// Pass `null` to clear the push identifier. + /// + /// On **Android**, pass the FCM registration token string obtained from `FirebaseMessaging.getInstance().getToken()`. + /// + /// On **iOS**, pass the APNs device token as a **lowercase hex string** (e.g. `"a1b2c3d4..."`). + /// The hex string must be obtained from the raw `Data` token received in + /// `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` and converted before + /// passing to Flutter (e.g. `deviceToken.map { String(format: "%02x", $0) }.joined()`). + /// + /// @param token The push notification token. Pass `null` to clear the identifier. + static Future setPushIdentifier(String? token) => + _channel.invokeMethod('setPushIdentifier', token); + /// Called by the extension public API to dispatch an event for other extensions or the internal SDK to consume. Any events dispatched by this call will not be processed until after `start` has been called. static Future dispatchEvent(Event event) => _channel.invokeMethod('dispatchEvent', event.data); diff --git a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart index 1ab2da1b..b44b0c05 100644 --- a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart @@ -9,7 +9,6 @@ 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'; @@ -20,34 +19,36 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('extensionVersion', () { - final String testVersion = "2.5.0"; - final List log = []; + final String testVersion = "2.5.0"; + final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { - log.add(methodCall); - return testVersion; + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + log.add(methodCall); + return testVersion; + }); }); - }); - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); - }); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); - test('invokes correct method', () async { - await MobileCore.extensionVersion; + test('invokes correct method', () async { + await MobileCore.extensionVersion; - expect(log, [ - isMethodCall( - 'extensionVersion', - arguments: null, - ), - ]); - }); + expect(log, [ + isMethodCall( + 'extensionVersion', + arguments: null, + ), + ]); + }); - test('returns correct result', () async { - expect(await MobileCore.extensionVersion, testVersion); - }); + test('returns correct result', () async { + expect(await MobileCore.extensionVersion, testVersion); + }); }); group('initialize', () { @@ -66,18 +67,20 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('invokes correct method', () async { - await MobileCore.initialize(initOptions:initOptions); + await MobileCore.initialize(initOptions: initOptions); expect(log, [ isMethodCall( @@ -85,7 +88,8 @@ void main() { arguments: { 'initOptions': { 'appId': appId, - 'lifecycleAutomaticTrackingEnabled': lifecycleAutomaticTrackingEnabled, + 'lifecycleAutomaticTrackingEnabled': + lifecycleAutomaticTrackingEnabled, 'lifecycleAdditionalContextData': lifecycleAdditionalContextData, 'appGroupIOS': appGroup, }, @@ -101,18 +105,20 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('invokes correct method', () async { - await MobileCore.initializeWithAppId(appId:appId); + await MobileCore.initializeWithAppId(appId: appId); expect(log, [ isMethodCall( @@ -138,14 +144,16 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('invokes correct method', () async { @@ -163,7 +171,6 @@ void main() { ]); }); }); - group('trackState', () { final String testState = "myTestState"; @@ -173,17 +180,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.trackState(testState, data: testContextData); @@ -204,18 +212,19 @@ void main() { final String testAdId = "test-aid"; final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.setAdvertisingIdentifier(testAdId); @@ -228,6 +237,47 @@ void main() { }); }); + group('setPushIdentifier', () { + final String testPushToken = "test-push-token"; + final List log = []; + + setUp(() { + log.clear(); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method with token', () async { + await MobileCore.setPushIdentifier(testPushToken); + + expect(log, [ + isMethodCall( + 'setPushIdentifier', + arguments: testPushToken, + ), + ]); + }); + + test('invokes correct method with null token', () async { + await MobileCore.setPushIdentifier(null); + + expect(log, [ + isMethodCall( + 'setPushIdentifier', + arguments: null, + ), + ]); + }); + }); + group('dispatchEvent', () { final Map eventConstructorData = { "eventName": "testresponseEvent", @@ -239,17 +289,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return true; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.dispatchEvent(expectedEvent); @@ -280,17 +331,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (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); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.dispatchEventWithResponseCallback(expectedEvent, 1000); @@ -337,18 +389,19 @@ void main() { final String testSdkIdentities = "sdkIdentities"; final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return testSdkIdentities; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.sdkIdentities; @@ -369,18 +422,19 @@ void main() { group('getPrivacyStatus', () { final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (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); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.privacyStatus; @@ -403,17 +457,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.setLogLevel(logLevel); @@ -430,15 +485,17 @@ void main() { final PrivacyStatus privacyStatus = PrivacyStatus.opt_in; final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('invokes correct method', () async { @@ -458,17 +515,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.updateConfiguration(testConfig); @@ -485,17 +543,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.clearUpdatedConfiguration(); @@ -509,18 +568,19 @@ void main() { final Map testPiiData = {"testKey": "testValue"}; final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.collectPii(testPiiData); @@ -538,17 +598,18 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('invokes correct method', () async { await MobileCore.setAppGroup(testAppGroup); diff --git a/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart b/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart index 63a05059..e02a557d 100644 --- a/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart +++ b/plugins/flutter_aepedgeidentity/test/flutter_aepedgeidentitymap_test.dart @@ -40,14 +40,16 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedItemsForNamespace1; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('is validated', () async { @@ -96,16 +98,16 @@ void main() { item2 ]; - final List log = []; - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { return expectedItemsForNamespace1; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('test an invalid namespace', () async { @@ -143,14 +145,16 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return {}; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('same id and same item, should ignore the new added item', () async { @@ -204,14 +208,16 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return expectedItemsForNamespace2; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('is validated', () async { @@ -254,14 +260,16 @@ void main() { final List log = []; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { log.add(methodCall); return null; }); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('is validated', () async { From 16a314751951dcf2c2c23430e0d1cb785e0cb6bb Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 23 Apr 2026 21:25:25 -0300 Subject: [PATCH 3/9] feat(core): add MobileCore.setPushIdentifierWithData for raw APNs token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a second method that accepts Uint8List? and passes the bytes directly to [AEPMobileCore setPushIdentifier:] as NSData on iOS, without any UTF-8 or hex encoding — which is the correct behaviour for raw APNs device tokens. On Android the method is a no-op with a debug log; developers should use setPushIdentifier(String?) with the FCM token there. - Dart: import dart:typed_data, add setPushIdentifierWithData(Uint8List?) - iOS: handleSetPushIdentifierWithData uses FlutterStandardTypedData.data - Android: no-op handler with debug log - Tests: 25/25 passing --- plugins/flutter_aepcore/CHANGELOG.md | 3 +- plugins/flutter_aepcore/README.md | 37 ++++++++++++++-- .../flutter_aepcore/FlutterAEPCorePlugin.java | 3 ++ .../ios/Classes/FlutterAEPCorePlugin.m | 14 ++++++ .../flutter_aepcore/lib/flutter_aepcore.dart | 18 ++++++++ .../test/flutter_aepcore_test.dart | 44 +++++++++++++++++++ 6 files changed, 115 insertions(+), 4 deletions(-) diff --git a/plugins/flutter_aepcore/CHANGELOG.md b/plugins/flutter_aepcore/CHANGELOG.md index bce6bdea..4b92153d 100644 --- a/plugins/flutter_aepcore/CHANGELOG.md +++ b/plugins/flutter_aepcore/CHANGELOG.md @@ -1,6 +1,7 @@ ## 5.0.2 -* Add `MobileCore.setPushIdentifier` API to register a push notification token with the Adobe SDK on Android and iOS. +* Add `MobileCore.setPushIdentifier` API to register a push notification token string (FCM) with the Adobe SDK on Android and iOS. +* Add `MobileCore.setPushIdentifierWithData` API to register a raw APNs push token (`Uint8List`) on iOS, passing the bytes directly to the native SDK without encoding conversion. ## 5.0.1 diff --git a/plugins/flutter_aepcore/README.md b/plugins/flutter_aepcore/README.md index 1fb3b84a..39ee228a 100644 --- a/plugins/flutter_aepcore/README.md +++ b/plugins/flutter_aepcore/README.md @@ -328,7 +328,10 @@ MobileCore.trackState("myState", data: {"key1": "value1"}); ``` ### setPushIdentifier -Register a device push token with the Adobe SDK. Pass `null` to clear a previously registered token. +Register a FCM push token string with the Adobe SDK. Pass `null` to clear a previously registered token. + +Use this method on **Android** with the FCM token string from `FirebaseMessaging.instance.getToken()`. +On **iOS**, prefer [setPushIdentifierWithData](#setpushidentifierwithdata) to pass the raw APNs token bytes. **Syntax** ```dart @@ -337,13 +340,41 @@ static Future setPushIdentifier(String? token) **Example** ```dart -// Register the token obtained from FCM/APNs -MobileCore.setPushIdentifier("your-push-token"); +// Android: register FCM token +final token = await FirebaseMessaging.instance.getToken(); +MobileCore.setPushIdentifier(token); // Clear the push token MobileCore.setPushIdentifier(null); ``` +### setPushIdentifierWithData +Register a raw APNs push token (`Uint8List`) with the Adobe SDK. The bytes are passed directly to the native iOS SDK without any encoding conversion. + +Use this method on **iOS** with the raw APNs device token obtained from +`FirebaseMessaging.instance.getAPNSToken()` or from the native +`didRegisterForRemoteNotificationsWithDeviceToken` delegate. + +On **Android** this method is a no-op — use [setPushIdentifier](#setpushidentifier) instead. + +**Syntax** +```dart +static Future setPushIdentifierWithData(Uint8List? tokenData) +``` + +**Example** +```dart +import 'dart:io'; + +if (Platform.isIOS) { + final apnsToken = await FirebaseMessaging.instance.getAPNSToken(); + MobileCore.setPushIdentifierWithData(apnsToken); +} else { + final fcmToken = await FirebaseMessaging.instance.getToken(); + MobileCore.setPushIdentifier(fcmToken); +} +``` + ### Identity For more information on the Core Identity APIs, visit the documentation [here](https://developer.adobe.com/client-sdks/documentation/mobile-core/identity/). 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 abd32cb5..5ce765b9 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 @@ -93,6 +93,9 @@ public void onMethodCall(MethodCall call, @NonNull Result result) { } else if ("setPushIdentifier".equals(call.method)) { handleSetPushIdentifier(call.arguments); result.success(null); + } else if ("setPushIdentifierWithData".equals(call.method)) { + com.adobe.marketing.mobile.services.Log.debug(TAG, AEPCORE_TAG, "setPushIdentifierWithData() is not supported on Android. Use setPushIdentifier() with the FCM token string."); + result.success(null); } else if ("dispatchEvent".equals(call.method)) { handleDispatchEvent(result, call.arguments); } else if ("dispatchEventWithResponseCallback".equals(call.method)) { diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m index 1d38454c..7c695127 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m @@ -47,6 +47,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call } else if ([@"setPushIdentifier" isEqualToString:call.method]) { [self handleSetPushIdentifier:call]; result(nil); + } else if ([@"setPushIdentifierWithData" isEqualToString:call.method]) { + [self handleSetPushIdentifierWithData:call]; + result(nil); } else if ([@"dispatchEvent" isEqualToString:call.method]) { [self handleDispatchEvent:call result:result]; } else if ([@"dispatchEventWithResponseCallback" @@ -212,6 +215,17 @@ - (void)handleSetPushIdentifier:(FlutterMethodCall *)call { [AEPMobileCore setPushIdentifier:token]; } +- (void)handleSetPushIdentifierWithData:(FlutterMethodCall *)call { + if (call.arguments == nil || call.arguments == [NSNull null]) { + [AEPMobileCore setPushIdentifier:nil]; + return; + } + + // Flutter sends Uint8List as FlutterStandardTypedData; extract NSData directly. + FlutterStandardTypedData *typedData = call.arguments; + [AEPMobileCore setPushIdentifier:typedData.data]; +} + - (FlutterError *)flutterErrorFromNSError:(NSError *)error { return [FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", (long)error.code] diff --git a/plugins/flutter_aepcore/lib/flutter_aepcore.dart b/plugins/flutter_aepcore/lib/flutter_aepcore.dart index 4a231e80..07297fd5 100644 --- a/plugins/flutter_aepcore/lib/flutter_aepcore.dart +++ b/plugins/flutter_aepcore/lib/flutter_aepcore.dart @@ -10,6 +10,7 @@ governing permissions and limitations under the License. */ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore_data.dart'; @@ -85,6 +86,23 @@ class MobileCore { static Future setPushIdentifier(String? token) => _channel.invokeMethod('setPushIdentifier', token); + /// Submits a generic event containing the provided push token (as raw bytes) with event type `generic.identity`. + /// + /// Use this method on **iOS** to pass the raw APNs device token (`Data`) obtained from + /// `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` or from + /// `FirebaseMessaging.instance.getAPNSToken()`. + /// + /// The bytes are passed directly to `[AEPMobileCore setPushIdentifier:]` as `NSData` without + /// any encoding conversion, which is the correct behaviour for raw APNs tokens. + /// + /// On **Android** this method is a no-op — use [setPushIdentifier] with the FCM token string instead. + /// + /// Pass `null` to clear a previously registered token. + /// + /// @param tokenData The raw push notification token bytes. Pass `null` to clear the identifier. + static Future setPushIdentifierWithData(Uint8List? tokenData) => + _channel.invokeMethod('setPushIdentifierWithData', tokenData); + /// Called by the extension public API to dispatch an event for other extensions or the internal SDK to consume. Any events dispatched by this call will not be processed until after `start` has been called. static Future dispatchEvent(Event event) => _channel.invokeMethod('dispatchEvent', event.data); diff --git a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart index b44b0c05..b9722ce3 100644 --- a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart @@ -9,6 +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:typed_data'; + import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -278,6 +280,48 @@ void main() { }); }); + group('setPushIdentifierWithData', () { + final Uint8List testTokenData = + Uint8List.fromList([0x95, 0x16, 0x25, 0x8b, 0x62, 0x30, 0xaf, 0xdd]); + final List log = []; + + setUp(() { + log.clear(); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('invokes correct method with token data', () async { + await MobileCore.setPushIdentifierWithData(testTokenData); + + expect(log, [ + isMethodCall( + 'setPushIdentifierWithData', + arguments: testTokenData, + ), + ]); + }); + + test('invokes correct method with null token data', () async { + await MobileCore.setPushIdentifierWithData(null); + + expect(log, [ + isMethodCall( + 'setPushIdentifierWithData', + arguments: null, + ), + ]); + }); + }); + group('dispatchEvent', () { final Map eventConstructorData = { "eventName": "testresponseEvent", From 0489770959ac9bbbac75b1ffc304205682dd2a31 Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 23 Apr 2026 21:36:39 -0300 Subject: [PATCH 4/9] chore: remove IDE-generated Eclipse/Buildship files from tracking --- .gitignore | 3 +- plugins/flutter_aepcore/android/.project | 28 ------------------- .../org.eclipse.buildship.core.prefs | 13 --------- 3 files changed, 2 insertions(+), 42 deletions(-) delete mode 100644 plugins/flutter_aepcore/android/.project delete mode 100644 plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs diff --git a/.gitignore b/.gitignore index 7d331618..924aa4d8 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,5 @@ example/android/GeneratedPluginRegistrant.java **/ios/flutter_export_environment.sh # podspec.lock for plugins -plugins/**/pubspec.lock \ No newline at end of file +plugins/**/pubspec.lockplugins/flutter_aepcore/android/.project +plugins/flutter_aepcore/android/.settings/ diff --git a/plugins/flutter_aepcore/android/.project b/plugins/flutter_aepcore/android/.project deleted file mode 100644 index a4eccda7..00000000 --- a/plugins/flutter_aepcore/android/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - android - Project android created by Buildship. - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.buildship.core.gradleprojectnature - - - - 1776988886455 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs b/plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index a0ff068a..00000000 --- a/plugins/flutter_aepcore/android/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments=--init-script /home/bruno/.cache/opencode/bin/jdtls/config_linux/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir= -eclipse.preferences.version=1 -gradle.user.home= -java.home=/home/bruno/.local/share/mise/installs/java/temurin-25.0.2+10.0.LTS -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true From a6fcf79cb03d0b905600ab5850922e23f355e9dd Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 23 Apr 2026 21:38:48 -0300 Subject: [PATCH 5/9] chore: remove ephemeral Flutter iOS files from tracking --- .../Flutter/ephemeral/flutter_lldb_helper.py | 32 ------------------- .../ios/Flutter/ephemeral/flutter_lldbinit | 5 --- 2 files changed, 37 deletions(-) delete mode 100644 example/ios/Flutter/ephemeral/flutter_lldb_helper.py delete mode 100644 example/ios/Flutter/ephemeral/flutter_lldbinit diff --git a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py deleted file mode 100644 index a88caf99..00000000 --- a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py +++ /dev/null @@ -1,32 +0,0 @@ -# -# Generated file, do not edit. -# - -import lldb - -def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): - """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" - base = frame.register["x0"].GetValueAsAddress() - page_len = frame.register["x1"].GetValueAsUnsigned() - - # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the - # first page to see if handled it correctly. This makes diagnosing - # misconfiguration (e.g. missing breakpoint) easier. - data = bytearray(page_len) - data[0:8] = b'IHELPED!' - - error = lldb.SBError() - frame.GetThread().GetProcess().WriteMemory(base, data, error) - if not error.Success(): - print(f'Failed to write into {base}[+{page_len}]', error) - return - -def __lldb_init_module(debugger: lldb.SBDebugger, _): - target = debugger.GetDummyTarget() - # Caveat: must use BreakpointCreateByRegEx here and not - # BreakpointCreateByName. For some reasons callback function does not - # get carried over from dummy target for the later. - bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") - bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) - bp.SetAutoContinue(True) - print("-- LLDB integration loaded --") diff --git a/example/ios/Flutter/ephemeral/flutter_lldbinit b/example/ios/Flutter/ephemeral/flutter_lldbinit deleted file mode 100644 index e3ba6fbe..00000000 --- a/example/ios/Flutter/ephemeral/flutter_lldbinit +++ /dev/null @@ -1,5 +0,0 @@ -# -# Generated file, do not edit. -# - -command script import --relative-to-command-file flutter_lldb_helper.py From 3f1c58cef358b60f9ab94afd667c626e8aa4c8bb Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Wed, 13 May 2026 14:34:57 -0300 Subject: [PATCH 6/9] fix(core): unify setPushIdentifier API and fix iOS hex-to-NSData conversion - Remove setPushIdentifierWithData; a single setPushIdentifier(String?) now handles both Android (FCM token) and iOS (APNs hex token) - Fix iOS bridge: convert hex string to NSData internally instead of UTF-8 encoding, which was registering wrong bytes with the AEP SDK - Fix corrupted .gitignore line that merged two entries into one - Update CHANGELOG, README and tests accordingly --- .gitignore | 3 +- plugins/flutter_aepcore/CHANGELOG.md | 3 +- plugins/flutter_aepcore/README.md | 45 +++++-------------- .../flutter_aepcore/FlutterAEPCorePlugin.java | 3 -- .../ios/Classes/FlutterAEPCorePlugin.m | 29 ++++++------ .../flutter_aepcore/lib/flutter_aepcore.dart | 31 +++---------- .../test/flutter_aepcore_test.dart | 45 +------------------ 7 files changed, 37 insertions(+), 122 deletions(-) diff --git a/.gitignore b/.gitignore index 924aa4d8..618472c5 100644 --- a/.gitignore +++ b/.gitignore @@ -81,5 +81,4 @@ example/android/GeneratedPluginRegistrant.java **/ios/flutter_export_environment.sh # podspec.lock for plugins -plugins/**/pubspec.lockplugins/flutter_aepcore/android/.project -plugins/flutter_aepcore/android/.settings/ +plugins/**/pubspec.lock diff --git a/plugins/flutter_aepcore/CHANGELOG.md b/plugins/flutter_aepcore/CHANGELOG.md index 4b92153d..19335aeb 100644 --- a/plugins/flutter_aepcore/CHANGELOG.md +++ b/plugins/flutter_aepcore/CHANGELOG.md @@ -1,7 +1,6 @@ ## 5.0.2 -* Add `MobileCore.setPushIdentifier` API to register a push notification token string (FCM) with the Adobe SDK on Android and iOS. -* Add `MobileCore.setPushIdentifierWithData` API to register a raw APNs push token (`Uint8List`) on iOS, passing the bytes directly to the native SDK without encoding conversion. +* Add `MobileCore.setPushIdentifier` API to register a push notification token with the Adobe SDK. Accepts an FCM token string on Android and an APNs hex token string on iOS. The iOS native bridge converts the hex string to `NSData` internally before forwarding to the AEP SDK. ## 5.0.1 diff --git a/plugins/flutter_aepcore/README.md b/plugins/flutter_aepcore/README.md index 39ee228a..8460a152 100644 --- a/plugins/flutter_aepcore/README.md +++ b/plugins/flutter_aepcore/README.md @@ -328,51 +328,30 @@ MobileCore.trackState("myState", data: {"key1": "value1"}); ``` ### setPushIdentifier -Register a FCM push token string with the Adobe SDK. Pass `null` to clear a previously registered token. +Register a push notification token with the Adobe SDK. Pass `null` to clear a previously registered token. -Use this method on **Android** with the FCM token string from `FirebaseMessaging.instance.getToken()`. -On **iOS**, prefer [setPushIdentifierWithData](#setpushidentifierwithdata) to pass the raw APNs token bytes. +On **Android**, pass the FCM token string from `FirebaseMessaging.instance.getToken()`. +On **iOS**, pass the APNs token string from `FirebaseMessaging.instance.getAPNSToken()`. The native bridge converts the hex string to the raw bytes expected by the AEP SDK internally. **Syntax** ```dart static Future setPushIdentifier(String? token) ``` -**Example** -```dart -// Android: register FCM token -final token = await FirebaseMessaging.instance.getToken(); -MobileCore.setPushIdentifier(token); - -// Clear the push token -MobileCore.setPushIdentifier(null); -``` - -### setPushIdentifierWithData -Register a raw APNs push token (`Uint8List`) with the Adobe SDK. The bytes are passed directly to the native iOS SDK without any encoding conversion. - -Use this method on **iOS** with the raw APNs device token obtained from -`FirebaseMessaging.instance.getAPNSToken()` or from the native -`didRegisterForRemoteNotificationsWithDeviceToken` delegate. - -On **Android** this method is a no-op — use [setPushIdentifier](#setpushidentifier) instead. - -**Syntax** -```dart -static Future setPushIdentifierWithData(Uint8List? tokenData) -``` - **Example** ```dart import 'dart:io'; -if (Platform.isIOS) { - final apnsToken = await FirebaseMessaging.instance.getAPNSToken(); - MobileCore.setPushIdentifierWithData(apnsToken); -} else { - final fcmToken = await FirebaseMessaging.instance.getToken(); - MobileCore.setPushIdentifier(fcmToken); +if (Platform.isAndroid) { + final token = await FirebaseMessaging.instance.getToken(); + MobileCore.setPushIdentifier(token); +} else if (Platform.isIOS) { + final token = await FirebaseMessaging.instance.getAPNSToken(); + MobileCore.setPushIdentifier(token); } + +// Clear the push token +MobileCore.setPushIdentifier(null); ``` ### Identity 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 5ce765b9..abd32cb5 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 @@ -93,9 +93,6 @@ public void onMethodCall(MethodCall call, @NonNull Result result) { } else if ("setPushIdentifier".equals(call.method)) { handleSetPushIdentifier(call.arguments); result.success(null); - } else if ("setPushIdentifierWithData".equals(call.method)) { - com.adobe.marketing.mobile.services.Log.debug(TAG, AEPCORE_TAG, "setPushIdentifierWithData() is not supported on Android. Use setPushIdentifier() with the FCM token string."); - result.success(null); } else if ("dispatchEvent".equals(call.method)) { handleDispatchEvent(result, call.arguments); } else if ("dispatchEventWithResponseCallback".equals(call.method)) { diff --git a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m index 7c695127..d4efee6b 100644 --- a/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m +++ b/plugins/flutter_aepcore/ios/Classes/FlutterAEPCorePlugin.m @@ -47,9 +47,6 @@ - (void)handleMethodCall:(FlutterMethodCall *)call } else if ([@"setPushIdentifier" isEqualToString:call.method]) { [self handleSetPushIdentifier:call]; result(nil); - } else if ([@"setPushIdentifierWithData" isEqualToString:call.method]) { - [self handleSetPushIdentifierWithData:call]; - result(nil); } else if ([@"dispatchEvent" isEqualToString:call.method]) { [self handleDispatchEvent:call result:result]; } else if ([@"dispatchEventWithResponseCallback" @@ -210,20 +207,24 @@ - (void)handleSetPushIdentifier:(FlutterMethodCall *)call { return; } - NSString *tokenString = call.arguments; - NSData *token = [tokenString dataUsingEncoding:NSUTF8StringEncoding]; - [AEPMobileCore setPushIdentifier:token]; -} - -- (void)handleSetPushIdentifierWithData:(FlutterMethodCall *)call { - if (call.arguments == nil || call.arguments == [NSNull null]) { + // The Dart layer sends the APNs token as a lowercase hex string (e.g. "a1b2c3d4..."). + // Convert it back to the original NSData bytes that AEP SDK expects. + NSString *hexString = call.arguments; + NSUInteger length = hexString.length; + if (length % 2 != 0) { + // Malformed hex string — clear the identifier rather than registering garbage. [AEPMobileCore setPushIdentifier:nil]; return; } - - // Flutter sends Uint8List as FlutterStandardTypedData; extract NSData directly. - FlutterStandardTypedData *typedData = call.arguments; - [AEPMobileCore setPushIdentifier:typedData.data]; + NSMutableData *tokenData = [NSMutableData dataWithCapacity:length / 2]; + for (NSUInteger i = 0; i < length; i += 2) { + NSString *byteString = [hexString substringWithRange:NSMakeRange(i, 2)]; + unsigned int byte = 0; + [[NSScanner scannerWithString:byteString] scanHexInt:&byte]; + uint8_t byteValue = (uint8_t)byte; + [tokenData appendBytes:&byteValue length:1]; + } + [AEPMobileCore setPushIdentifier:tokenData]; } - (FlutterError *)flutterErrorFromNSError:(NSError *)error { diff --git a/plugins/flutter_aepcore/lib/flutter_aepcore.dart b/plugins/flutter_aepcore/lib/flutter_aepcore.dart index 07297fd5..1746afe0 100644 --- a/plugins/flutter_aepcore/lib/flutter_aepcore.dart +++ b/plugins/flutter_aepcore/lib/flutter_aepcore.dart @@ -10,7 +10,6 @@ governing permissions and limitations under the License. */ import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore_data.dart'; @@ -75,34 +74,18 @@ class MobileCore { /// /// Pass `null` to clear the push identifier. /// - /// On **Android**, pass the FCM registration token string obtained from `FirebaseMessaging.getInstance().getToken()`. + /// On **Android**, pass the FCM registration token string obtained from + /// `FirebaseMessaging.instance.getToken()`. /// - /// On **iOS**, pass the APNs device token as a **lowercase hex string** (e.g. `"a1b2c3d4..."`). - /// The hex string must be obtained from the raw `Data` token received in - /// `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` and converted before - /// passing to Flutter (e.g. `deviceToken.map { String(format: "%02x", $0) }.joined()`). + /// On **iOS**, pass the raw APNs device token hex string obtained from + /// `FirebaseMessaging.instance.getAPNSToken()`. The native bridge converts + /// the hex string to `NSData` internally before forwarding to the AEP SDK, + /// so no manual conversion is needed on the Dart side. /// - /// @param token The push notification token. Pass `null` to clear the identifier. + /// @param token The push notification token string. Pass `null` to clear the identifier. static Future setPushIdentifier(String? token) => _channel.invokeMethod('setPushIdentifier', token); - /// Submits a generic event containing the provided push token (as raw bytes) with event type `generic.identity`. - /// - /// Use this method on **iOS** to pass the raw APNs device token (`Data`) obtained from - /// `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` or from - /// `FirebaseMessaging.instance.getAPNSToken()`. - /// - /// The bytes are passed directly to `[AEPMobileCore setPushIdentifier:]` as `NSData` without - /// any encoding conversion, which is the correct behaviour for raw APNs tokens. - /// - /// On **Android** this method is a no-op — use [setPushIdentifier] with the FCM token string instead. - /// - /// Pass `null` to clear a previously registered token. - /// - /// @param tokenData The raw push notification token bytes. Pass `null` to clear the identifier. - static Future setPushIdentifierWithData(Uint8List? tokenData) => - _channel.invokeMethod('setPushIdentifierWithData', tokenData); - /// Called by the extension public API to dispatch an event for other extensions or the internal SDK to consume. Any events dispatched by this call will not be processed until after `start` has been called. static Future dispatchEvent(Event event) => _channel.invokeMethod('dispatchEvent', event.data); diff --git a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart index b9722ce3..46a69beb 100644 --- a/plugins/flutter_aepcore/test/flutter_aepcore_test.dart +++ b/plugins/flutter_aepcore/test/flutter_aepcore_test.dart @@ -9,7 +9,6 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_aepcore/flutter_aepcore.dart'; @@ -240,7 +239,7 @@ void main() { }); group('setPushIdentifier', () { - final String testPushToken = "test-push-token"; + final String testPushToken = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"; final List log = []; setUp(() { @@ -280,48 +279,6 @@ void main() { }); }); - group('setPushIdentifierWithData', () { - final Uint8List testTokenData = - Uint8List.fromList([0x95, 0x16, 0x25, 0x8b, 0x62, 0x30, 0xaf, 0xdd]); - final List log = []; - - setUp(() { - log.clear(); - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, (MethodCall methodCall) async { - log.add(methodCall); - return null; - }); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, null); - }); - - test('invokes correct method with token data', () async { - await MobileCore.setPushIdentifierWithData(testTokenData); - - expect(log, [ - isMethodCall( - 'setPushIdentifierWithData', - arguments: testTokenData, - ), - ]); - }); - - test('invokes correct method with null token data', () async { - await MobileCore.setPushIdentifierWithData(null); - - expect(log, [ - isMethodCall( - 'setPushIdentifierWithData', - arguments: null, - ), - ]); - }); - }); - group('dispatchEvent', () { final Map eventConstructorData = { "eventName": "testresponseEvent", From 880c83dd644681adf136da767ad3af5ec31498ac Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 14 May 2026 11:05:13 -0300 Subject: [PATCH 7/9] chore: restore .gitignore to match main (no trailing newline) --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 618472c5..7d331618 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,4 @@ example/android/GeneratedPluginRegistrant.java **/ios/flutter_export_environment.sh # podspec.lock for plugins -plugins/**/pubspec.lock +plugins/**/pubspec.lock \ No newline at end of file From 1b5dba304ae67ff4aa71cfa9553c45e533b19a42 Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 14 May 2026 11:05:36 -0300 Subject: [PATCH 8/9] chore: restore example/pubspec.lock to match main --- example/pubspec.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 4db25d67..953803ad 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" clock: dependency: transitive description: @@ -75,7 +75,7 @@ packages: path: "../plugins/flutter_aepcore" relative: true source: path - version: "5.0.1" + version: "5.0.0" flutter_aepedge: dependency: "direct main" description: @@ -151,26 +151,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.16.0" path: dependency: transitive description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.6" vector_math: dependency: transitive description: @@ -249,5 +249,5 @@ packages: source: hosted version: "14.2.5" sdks: - dart: ">=3.9.0-0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" From bcfbe9a5a7d4206d8565450e95551ef16880ee91 Mon Sep 17 00:00:00 2001 From: Bruno Gabriel dos Santos Date: Thu, 14 May 2026 11:10:55 -0300 Subject: [PATCH 9/9] docs(core): decouple setPushIdentifier docs from Firebase references --- plugins/flutter_aepcore/CHANGELOG.md | 2 +- plugins/flutter_aepcore/README.md | 15 ++++----------- plugins/flutter_aepcore/lib/flutter_aepcore.dart | 12 ++++-------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/plugins/flutter_aepcore/CHANGELOG.md b/plugins/flutter_aepcore/CHANGELOG.md index 19335aeb..7f27c6cb 100644 --- a/plugins/flutter_aepcore/CHANGELOG.md +++ b/plugins/flutter_aepcore/CHANGELOG.md @@ -1,6 +1,6 @@ ## 5.0.2 -* Add `MobileCore.setPushIdentifier` API to register a push notification token with the Adobe SDK. Accepts an FCM token string on Android and an APNs hex token string on iOS. The iOS native bridge converts the hex string to `NSData` internally before forwarding to the AEP SDK. +* Add `MobileCore.setPushIdentifier` API to register a push notification token with the Adobe SDK. Accepts a push token string on Android and an APNs hex token string on iOS. The iOS native bridge converts the hex string to `NSData` internally before forwarding to the AEP SDK. ## 5.0.1 diff --git a/plugins/flutter_aepcore/README.md b/plugins/flutter_aepcore/README.md index 8460a152..b3afdbd0 100644 --- a/plugins/flutter_aepcore/README.md +++ b/plugins/flutter_aepcore/README.md @@ -330,8 +330,8 @@ MobileCore.trackState("myState", data: {"key1": "value1"}); ### setPushIdentifier Register a push notification token with the Adobe SDK. Pass `null` to clear a previously registered token. -On **Android**, pass the FCM token string from `FirebaseMessaging.instance.getToken()`. -On **iOS**, pass the APNs token string from `FirebaseMessaging.instance.getAPNSToken()`. The native bridge converts the hex string to the raw bytes expected by the AEP SDK internally. +On **Android**, pass the push token string provided by your push notification service. +On **iOS**, pass the APNs device token as a lowercase hex string. The native bridge converts it to the raw bytes expected by the AEP SDK internally. **Syntax** ```dart @@ -340,15 +340,8 @@ static Future setPushIdentifier(String? token) **Example** ```dart -import 'dart:io'; - -if (Platform.isAndroid) { - final token = await FirebaseMessaging.instance.getToken(); - MobileCore.setPushIdentifier(token); -} else if (Platform.isIOS) { - final token = await FirebaseMessaging.instance.getAPNSToken(); - MobileCore.setPushIdentifier(token); -} +// Register a push token +MobileCore.setPushIdentifier(token); // Clear the push token MobileCore.setPushIdentifier(null); diff --git a/plugins/flutter_aepcore/lib/flutter_aepcore.dart b/plugins/flutter_aepcore/lib/flutter_aepcore.dart index 1746afe0..66400ab5 100644 --- a/plugins/flutter_aepcore/lib/flutter_aepcore.dart +++ b/plugins/flutter_aepcore/lib/flutter_aepcore.dart @@ -72,15 +72,11 @@ class MobileCore { /// Submits a generic event containing the provided push token with event type `generic.identity`. /// - /// Pass `null` to clear the push identifier. + /// On **Android**, pass the push token string as provided by your push notification service. + /// On **iOS**, pass the APNs device token as a lowercase hex string. The native bridge + /// converts it to the raw bytes expected by the AEP SDK internally. /// - /// On **Android**, pass the FCM registration token string obtained from - /// `FirebaseMessaging.instance.getToken()`. - /// - /// On **iOS**, pass the raw APNs device token hex string obtained from - /// `FirebaseMessaging.instance.getAPNSToken()`. The native bridge converts - /// the hex string to `NSData` internally before forwarding to the AEP SDK, - /// so no manual conversion is needed on the Dart side. + /// Pass `null` to clear a previously registered token. /// /// @param token The push notification token string. Pass `null` to clear the identifier. static Future setPushIdentifier(String? token) =>