Closed ManuelAccenture closed 4 months ago
Hi @ManuelAccenture,
Our push received listeners on Android work a little differently than on iOS, due to some platform restrictions in Flutter and Android. Background messages come through a separate callback, which can be wired up in your main.dart
file like:
Future<void> backgroundMessageHandler(PushReceivedEvent event) async {
// Handle the message
log("--- backgroundMessageHandler: $event");
}
void main() {
WidgetsFlutterBinding.ensureInitialized();
// ... Airship takeOff and other Airship initialization ...
Airship.push.android
.setBackgroundPushReceivedHandler(backgroundMessageHandler);
}
On Android, incoming background message callbacks are handled by spawning an isolate, which allows your app to receive a callback when background notifications are received when the app is not running, but this comes with a couple of requirements/limitations:
backgroundMessageHandler
function (or however you choose to name it) must be top-level, and not an anonymous function. Hello, @jyaganeh , first, let me apologize, because a mix two questions.
The first issue I had was that setBackgroundPushReceivedHandler wasn't working well. The problem was the isolates, I could solve that.
And the second problem is that in IOS onPushReceived is not invoked when the app is in terminated or background state.
I was looking for something similar to setBackgroundPushReceivedHandler for IOS, but I found nothing.
How can I handle background message for IOS?
@ManuelAccenture When you swipe to close an iOS app it is no longer able to receive background push or wake up in the background until you manually open it again. Its basically like the force kill behavior on Android.
Assuming you let it terminate by restarting the phone or having it in the background until its terminated by the OS, or just in the background past the 30 second window, you should be able to wake up the app by sending a push with content_available: 1
set in the payload. Even then content available pushes are pretty unreliable and are heavily throttled.
The system treats background notifications as low priority: you can use them to refresh your app’s content, but the system doesn’t guarantee their delivery. In addition, the system may throttle the delivery of background notifications if the total number becomes excessive. The number of background notifications allowed by the system depends on current conditions, but don’t try to send more than two or three per hour.
https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app
Thank you so much for the help.
At least, I need to find other way to save notifications, since there was a lot problems with background notifications in release mode.
@ManuelAccenture have you found any solution for the problem?
Hi @rlepinski , We are facing the same problem at the moment. I need to find a way to access the push notifications' payload when our iOS App is in background/terminated. I don't have to process it immediately when it arrives on the device, I just need to ensure it gets processed at some point. I found a branch with a workaround , but as it is 2 years old, I assume it is not compatible with Airship 7.x.x... Is there any other reliable way to solve the problem, or might there be one in the near future? We are paying customers and the functionality is unforunately crucial for our app. I would be very glad if you could help out!
We merged that into main to workaround a flutter delegate issue - https://github.com/urbanairship/airship-flutter/blob/main/ios/Classes/SwiftAirshipPlugin.swift#L61, so it should be there.
What version of flutter and the plugin are you on? Can you verify you have remote-notification background mode capabilities enabled, that you are sending a content-available: 1 in the payload, and you are not killing the app through the app switcher when you test it?
I can check the latest flutter today/tomorrow to make sure its still working and report back.
@rlepinski Flutter 3.22.3 airship_flutter: ^7.6.0
✅ Remote notifications in background modes capabilities in Xcode is enabled
As I am sending push notifications from Airship, I set Background processing: Send content-available flag to device to wake app for processing
in iOS Options.
I tested the app downloaded from TestFlight. If I don’t kill the app and just send it to the background, onPushReceived
works fine. However, when the app is killed onPushReceived
doesn’t work on iOS, and this is the problem.
However, when the app is killed onPushReceived doesn’t work on iOS, and this is the problem.
If you swipe to kill an iOS app it breaks this, its an OS behavior that we cant control. If the app is killed by the OS or if you restart your phone it should still work.
What is the use case?
If you are sending a visible push notification you could use a notification service extension and mark the push as mutable, but that will require native code since it runs in its own sandbox. To share data with the main app you would have to use shared app group
@rlepinski The use case is that we need to access and collect the payload of all push notifications and show them to the user in our frontend. It is a kind of custom message center in the app, but unfortunately, we can't use the Message Center from Airship. Is there another way to collect and/or access the push notifications for this purpose?
Could you please share an example or documentation of implementing a shared app group in the context of push notifications?
Here is a quick example on how you might do this.
group.<BUNDLE_ID>
final class SharedStore {
static let shared: SharedStore = SharedStore()
// update this line
private let defaults = UserDefaults(suiteName: "group..<BUNDLE_ID>")!
private static let notificationsKey = "notifications"
var notifications: [[AnyHashable : Any]] {
guard let notifications = defaults.object(forKey: Self.notificationsKey) as? [[AnyHashable : Any]] else {
return []
}
return notifications
}
func addNotification(_ notification: [AnyHashable : Any]) {
var notifications = self.notifications
notifications.append(notification)
defaults.set(notifications, forKey: Self.notificationsKey)
}
func clearNotifications() {
defaults.removeObject(forKey: Self.notificationsKey)
}
}
class NotificationService: UANotificationServiceExtension {
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
// process the request, store info in the shared app group
SharedStore.shared.addNotification(request.content.userInfo)
super.didReceive(request, withContentHandler: contentHandler)
}
}
Then in your app, you should now be able to access the shared store. You will have to expose this to flutter in your app though.
When you send pushes, make sure this is checked:
Thank you @rlepinski ! How exactly is it supposed to let us access the payload from push notifications that arrive when the iOS app is killed? Is it going to collect them and make the entire payload available as soon as the app starts?
The storage will collect the raw APNS payloads from the service and can be accessed on the native side with:
SharedStore.shared.notifications
. You will have to write a custom plugin to expose those to your app through flutter though. This is not something Airship will support natively for now.
❗For how-to inquiries involving Airship functionality or use cases, please contact (support)[https://support.airship.com/].
Preliminary Info
What Airship dependencies are you using?
airship_flutter: ^7.3.1
What are the versions of any relevant development tools you are using?
Ios
Report
What unexpected behavior are you seeing?
onPushReceived is not invoked when receive a notification when mobile is in background or terminated state.
What is the expected behavior?
onPushReceived should be invoked.
What are the steps to reproduce the unexpected behavior?
Setup onPushReceived and send a notification.
Do you have logging for the issue?
Log doesn't show any error or logs.