feat: add amplify_push package#6985
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6985 +/- ##
==========================================
+ Coverage 43.22% 43.23% +0.01%
==========================================
Files 99 99
Lines 7769 7769
Branches 3400 3401 +1
==========================================
+ Hits 3358 3359 +1
+ Misses 4411 4410 -1 🚀 New features to boost your workflow:
|
b28b8ff to
d479ea0
Compare
d479ea0 to
9e9a51c
Compare
5dbc37a to
763af62
Compare
Standalone, backend-agnostic on-device push handling (token lifecycle, display, deeplink routing, permissions, background processing, badge) built on Amplify Foundation. Depends on flutter_plugin_android_lifecycle for the activity lifecycle.
763af62 to
fa2f10f
Compare
- Fix close() to cancel all provider stream subscriptions - Expose token stream as broadcast (supports multiple listeners) - Add create() idempotency guard (PushAlreadyConfiguredException) - Deduplicate initial token dispatch to provider - Fix APNs deeplink parser to use backend-neutral data key - Add ==, hashCode, toString to all value types - Replace Kotlin !! with null-safe guards for Firebase race safety - Use fixed JOB_ID constant for background service - Align podspec version with pubspec (0.1.0) - Restore warning log on missing callback dispatcher - Remove unused async dependency
d6e3ec7 to
48cb67a
Compare
| /// | ||
| /// Cancels all provider subscriptions, clears background callbacks, and | ||
| /// disconnects the provider. The client cannot be reused after closing. | ||
| Future<void> close() async { |
There was a problem hiding this comment.
The broadcast controller behind onTokenReceived never gets closed here. onDone: tokenController.close only fires if the source stream completes — cancelling the subscription won't trigger it. Worth holding a ref to the controller and calling await _tokenController.close() in here.
| /// | ||
| /// Returns `null` if the app was launched by other means. | ||
| /// Consumed on first read (subsequent reads return `null`). | ||
| PushNotificationMessage? get launchNotification { |
There was a problem hiding this comment.
This getter nullifies the field as a side effect of reading it. It's documented, but a getter that mutates state is easy to trip over. Something like consumeLaunchNotification() would read more honestly.
cadivus
left a comment
There was a problem hiding this comment.
Went over the native side too. Generated files (Pigeon .m/.h/.java/.g.dart) aside, a couple of Android things inline.
One minor iOS nit: in AmplifyPushNotificationsPlugin.swift the badge getter/setter use applicationIconBadgeNumber (lines ~95 and ~99), which is deprecated as of iOS 17 in favor of UNUserNotificationCenter.setBadgeCount(_:). Not urgent, just expect deprecation warnings.
|
|
||
| override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { | ||
| mainBinaryMessenger = null | ||
| _flutterEngineCache.clear() |
There was a problem hiding this comment.
FlutterEngineCache.getInstance() is a process-wide singleton, so clear() drops every cached engine in the app — including this plugin's background engine and anything other plugins or an add-to-app host put there. Safer to remove just our own key: _flutterEngineCache.remove(FLUTTER_ENGINE_ID).
|
|
||
| if (res is PermissionRequestResult.Granted) { | ||
| result.success(true) | ||
| } else if (res is PermissionRequestResult.NotGranted){ |
There was a problem hiding this comment.
This handles Granted and NotGranted but has no terminal else. If requestPermission() ever returns another result (or null), result.success(...) never fires and the Dart future hangs forever. Worth a fallback result.success(false) to be safe.
- close() now closes the buffered token stream in addition to cancelling the provider stream subscriptions - rename launchNotification getter to consumeLaunchNotification() since reading it mutates state - add onError handlers to the provider token, foreground, and opened stream listeners so native error events are logged rather than unhandled - Android: on engine detach, remove only this plugin's engine cache keys instead of clearing the process-wide cache - Android: handle permission results with an exhaustive when plus a terminal else, and surface exceptions via result.error so the Dart future always completes - iOS: migrate badge setting to UNUserNotificationCenter.setBadgeCount with an availability guard and a fallback to applicationIconBadgeNumber for iOS < 16 - trim the client doc comments
Summary
New Flutter plugin package
amplify_pushfor standalone on-device push notification handling, decoupled from Pinpoint.What is this?
A backend-agnostic push notifications client that handles:
No
Amplify.configure()required. No AWS service dependency. Works standalone or with an optionalPushServiceProviderfor backend wiring (Connect client integration coming in Phase 2).Design
AmplifyPushClient.create()static factory — no Amplify plugin lifecycleResult<T>fromamplify_foundation_dartAmplifyPushExceptionhierarchy extending foundationAmplifyExceptionPushServiceProviderinterface withonTokenReceivedandonPushEventcallbacksamplify_push_notifications(same platform channel bridge, backend-agnostic)flutter_plugin_android_lifecycle(the plugin resolves the activity lifecycle viaFlutterLifecycleAdapter)Usage
Verification
dart analyze: 0 errors, 0 warnings (runs in this PR's CI)amplify_push.h)Known follow-ups / not-yet-verified
amplify_push_notificationsinternally (cosmetic, channel names must match)By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.