stream-chat-swift
stream-chat-swift copied to clipboard
`ChatRemoteNotificationHandler` does not consistently provide `ChatPushNotificationContent` in `handleNotification(completion:)`
What did you do?
I've been trying to customize a push notification's content using a UNNotificationServiceExtension as explained in the Customising Remote Push Notifications section of the documentation.
What did you expect to happen?
I expected that the ChatRemoteNotificationHandler's handleNotification(completion:) block would consistently return valid ChatPushNotificationContent that would include the MessageNotificationContent each time a new message is received.
What happened instead?
Very often the ChatRemoteNotificationHandler would not be able to properly handle the notification and would return .unknown for the ChatPushNotificationContent.
GetStream Environment
GetStream Chat version: 4.19.0 GetStream Chat frameworks: StreamChat, StreamChatUI iOS version: 15.5 Swift version: 5.6.1 Xcode version: 13.4.1 Device: iPhone 12 Pro Max, iPhone 6s
Additional context
I have noticed a few issues when trying to use a UNNotificationServiceExtension to customize a push notification's content prior to displaying it to the user.
First, let's start with the ChatClient's configuration. I am using the default configuration that has isLocalStorageEnabled enabled and have set up an application group identifier.
The ChatClient's configuration in the app looks like this:
...
var config = ChatClientConfig(apiKey: .init(ChatConstants.apiKey))
config.applicationGroupIdentifier = ChatConstants.appGroupIdentifier
ChatClient.shared = ChatClient(config: config)
...
While in the UNNotificationServiceExtension it looks like this:
...
var config = ChatClientConfig(apiKey: .init(ChatConstants.apiKey))
config.applicationGroupIdentifier = ChatConstants.appGroupIdentifier
let client = ChatClient(config: config)
let token = Token(stringLiteral: streamToken)
client.setToken(token: token)
...
The first problem that I stumbled upon is the following:
Whenever this configuration (with the local storage enabled) is used, very often the ChatRemoteNotificationHandler's handleNotification(completion:) block would return .unknown for the MessageNotificationContent. I've debugged the issue a bit, and it seems that the call to getMessageAndSync(cid:messageId:) within the getContent(completion:) method of the ChatRemoteNotificationHandler class would occasionally return nil for the message, thus resulting in an .unknown ChatPushNotificationContent. There's no error within the getMessageAndSync(cid:messageId:), but rather accessing the controller.message property returns nil.
The second problem that I have noticed is the following:
If we disable the local storage by setting isLocalStorageEnabled = false, the ChatRemoteNotificationHandler's handleNotification(completion:) block would constantly produce the correct result, and return the MessageNotificationContent. However, in this case, if we try to access the unread count for the channels by doing the following:
let currentUserController = client.currentUserController()
currentUserController.synchronize { _ in
let unreadChannelsCount = currentUserController.unreadCount.channels
}
we would constantly get 0 for the unread count. It does not make a difference whether a call to synchronize(_:) is made or not, or whether we access the count in the completion block or not.
Edit: I just realized that the unread count could be wrong in either case, regardless if the local storage is enabled or not.
I would appreciate if you could take a look into this as there's seems to be no other way to fetch the unread count while using v2 of the Push Notifications.
Looking forward to hearing from you!
Thank you in advance!
Cheers, Sasho
Hi @sasojadrovski,
Thank you for reporting this. We indeed have identified an issue in the ChatRemoteNotificationHandler, and we will work on it soon. Will keep you posted.
Hi @sasojadrovski, I created a PR that should improve the consistency with which you retrieve the message when a Push arrives. https://github.com/GetStream/stream-chat-swift/pull/2200. We'll be testing it soon and hopefully release it in the next release
Hey @polqf,
Thank you so much for looking into this so promptly! I appreciate it! I will try and pull your branch, do some testing as well, and will let you know the outcome.
In the meantime, were you able to see the other issue I mentioned above? The one regarding the unread count always returning zero in some cases. Do you believe that this PR might fix that as well?
Looking forward to hearing from you.
Thanks, Sasho
Hi @polqf ,
I did some testing and it seems to be working great now! At least when it comes to getting the correct message whenever a push notification is received. Great job!
However, the other issue with the unread count is still unresolved. Occasionally it returns the correct number, but most of the time it doesn't.
Please let me know if you need additional details around that. Thanks!
@polqf the PR looks great, we're experiencing this too, do you think you can do a release with the fix you just merged?
also mentioned in the SwiftUI repo: https://github.com/GetStream/stream-chat-swiftui/issues/157
Hi guys! Sure, we will create a release with this fix before the end of the week 😄
However, the other issue with the unread count is still unresolved. Occasionally it returns the correct number, but most of the time it doesn't.
@sasojadrovski yeah, the PR was not expected to fix that part, for which we have another internal ticket. We will keep this issue open until this last piece is solved as well.
Hey @polqf,
Do we have any updates on the final issue regarding the unread count? I am sorry for pinging you on that, but I was curious since it's only tracked in your internal systems and it is affecting our users' experience.
Looking forward to hearing from you!
Hi @sasojadrovski!
Getting the unread count on the client side for the push notifications it is a bit complicated. That is why the unread count will come directly from the backend. This change will be in production soon, and we will let you know once this is deployed. Once it is, you don't need to change the badgeCount yourself anymore, it will come from the push notification automatically.
Best, Nuno
Hi @nuno-vieira and @polqf, we appreciate you looking to resolve this issue, however, it seems as though this ticket was opened on July 27 and this is a very long time to not have badge counts working properly for our users. This is greatly impacting our retention rates since the badge count on the Home Screen is not being displayed thus users don't have an indicator to open the app if they received a chat message.
Could you please provide us with another update as to when this will be fixed? We are new to Stream and are excited to have a long term relationship with your company, but not having something as crucial and standard as badge counts not working for over a month greatly affects our business.
Hi @aaceti,
This should be fixed already in the backend. Is this not the case for you?
Best, Nuno
Hi @sasojadrovski!
Getting the unread count on the client side for the push notifications it is a bit complicated. That is why the unread count will come directly from the backend. This change will be in production soon, and we will let you know once this is deployed. Once it is, you don't need to change the
badgeCountyourself anymore, it will come from the push notification automatically.Best, Nuno
Hi @nuno-vieira, last we heard "the change will be in production soon and we will be notified when it is deployed" but we never received any message on this. Has it been deployed now?
Hi @aaceti !
Yes, we were waiting for the backend to confirm that this was deployed in all locations. And we were going to announce it this week on the new release.
Let us know if this is already working. We were concerned that you just tested today and it was not working.
Best, Nuno
Thanks @nuno-vieira! I will pass this message off to my team and we will test it out!
Hi!
This issue is now solved in the 4.21.1 release.
Thank you for your patience!
Best, Nuno
Hi @sasojadrovski!
Getting the unread count on the client side for the push notifications it is a bit complicated. That is why the unread count will come directly from the backend. This change will be in production soon, and we will let you know once this is deployed. Once it is, you don't need to change the
badgeCountyourself anymore, it will come from the push notification automatically.Best, Nuno
Hi @nuno-vieira ,
Thank you for your reply and for looking into this issue!
I was wondering, if there's no easy (or no way at all) for us to fetch the unread count on the client side from the UNNotificationServiceExtension, then how do we go about the use-case where a notification is received from other system (i.e. the app's backend service) that contains a badge count, and we need to append the unread channels count to it? Given we have no way to access the correct unread count, the badge returned by the other system would replace whatever badge is currently on the app icon (assuming the current badge represents the unread channels), thus rendering an incorrect number.
Looking forward to hearing from you!
Best, Sasho
Hi @sasojadrovski,
Whenever you receive a push notification now, the unread count is automatically populated in the badge of that notification. It doesn't matter where the message was sent, the badge count will be the current user unread count.
Hope I clarified your question.
Best, Nuno
Hi @nuno-vieira ,
Thank you for your reply.
I understand how it works now, but I am asking for a different scenario.
Let's say the app receives a push notification for an unread channel from Stream's backend, with the badge set to 1.
After a few moments, the app's backend (a different system, not Stream) sends a push notification for something else, with the badge also set to 1.
In this case, the app should combine both badges, and display the number 2.
As far as I know, this will not happen automatically as the new badge will replace the old one, and given they are both 1, it will stay as 1, and not be incremented to 2. In order to fix this, we need to manually combine both badges and display the number 2. Since the second push push notification did not come from Stream, how would we fetch the unread count in order to add it to the current badge?
I hope my question makes more sense now.
P.S. It might be important to mention that with the latest changes to Stream's backend, if a push notification template is used and set in Stream's dashboard, "badge": {{ unread_channels }} has to be added in order for the badge to be included. I am not sure if it's included by default if a template is not used, but I wanted to mention it.
Best regards, Sasho
Hey @nuno-vieira, @polqf
Any updates on my last question?
Looking forward to hearing from you!
Best regards, Sasho
Hi @sasojadrovski!
That one might be a bit trickier. But I think it is possible. When you receive a notification from the other backend system, you can increment the badge count you receive with the current unread counts from the channel controller of Stream SDK. So, in your Notification Service Extension, you check that the notification is from your external backend system, if yes, then you increment the badgeCount with client.channelController(for: "yourId").channel?.unreadCount.messages, something like this.
Best, Nuno
Hi @nuno-vieira,
Thank you so much for your prompt reply, much appreciated!
That's almost identical to the code we currently have, with the difference that we are using the CurrentChatUserController which has the issue mentioned in this ticket where the unread count is occasionally returned as 0, even though it contains an unreadCount property with counts for both channels and messages.
I can see that in your example, you are using the ChatChannelController for a specific channel, but the unread count we need is for all channels combined, and not for a single one, which means that unfortunately this approach will not work for us :(
We could potentially implement a workaround where we would manage the unread counts ourselves and each time there's a change to the count, we store that in UserDefaults and share it with the Notification Service Extension using a group container, however I would like to avoid that as it's prone to errors, and given that the SDK has a specific property for unreadCounts, I would prefer using that and rely on the SDK for the correct unread count instead.
Any other ideas/suggestions? Is it expected that the actual issue with the CurrentChatUserController would ever be resolved for this specific use-case?
Best regards, Sasho
Hi @sasojadrovski!
Sorry, I actually wanted to say CurrentChatUserController. It should not occasionally return 0 if that is the case, or it is a bug on our side, or some user in a different device actually read all the messages.
When you use the unreadCount from CurrentChatUserController, do you call synchronize() ?
@sasojadrovski,
This should be enough:
client.currentUserController.synchronize()
let unreads = client.currentUserController().unreadCount.messages
There's no need to wait for the sync call, we just want to make sure the data from the DB is fetched. But worth trying both (waiting for the sync call with the completion, and without) but I believe, the remote call won't be possible from the NSE.
Best, Nuno
Hi @nuno-vieira,
Yes, it does occasionally return 0 (or at least it did a few versions back, I will have to re-test it and get back to you). There's definitely no other user reading the messages, I am testing this locally in an isolated development environment.
As I mentioned when I opened the ticket:
if we try to access the unread count for the channels by doing the following:
let currentUserController = client.currentUserController() currentUserController.synchronize { _ in let unreadChannelsCount = currentUserController.unreadCount.channels }we would constantly get 0 for the unread count. It does not make a difference whether a call to synchronize(_:) is made or not, or whether we access the count in the completion block or not.
Here's the code we currently have for this:
if !chatNotification {
currentUserController.synchronize { _ in
let badge = content.badge?.intValue ?? 0
content.badge = NSNumber(value: badge + currentUserController.unreadCount.channels)
contentHandler(content)
}
}
I could re-test this again, but if you have not made changes to the SDK in regards to accessing the unreadCounts property of the CurrentChatUserController via an extension, I would expect the same result.
Looking forward to hearing from you.
Cheers, Sasho
Hi @sasojadrovski,
Could you please re-test this with the latest version 4.21.1? If you still see this behaviour of the unread counts returning 0, we will re-open the issue.
Best, Nuno
Sure @nuno-vieira,
I will try re-retesting this in the upcoming day or two, and get back to you.
I would appreciate if someone from your internal QA team could take a look as well.
Best regards, Sasho
@sasojadrovski
Actually... this won't work, unfortunately. The currentUserController.unreadCount fetches data from the Database only. This will only work if your app is in the background. If you turn off the app, the unreadCount won't be updated. So your desired logic won't work.
This is the reason why the badgeCount now comes from the backend, there's no way to get the updated unread count from the notification extension service since we get this value from the websocket only. At the moment the backend does not support fetching the unread count from an API Request.
I'll talk with the backend about this, but I don't have an ETA for this at the moment.
Hi @nuno-vieira,
Thank you for your reply and for confirming that this will not work as expected.
Please keep me posted on your discussions with the backend team since this is extremely important for our use-case and is affecting our users' experience.
Thank you!
Best regards, Sasho
Hi @nuno-vieira, thank you very much for your help in assisting us with these issues. As @sasojadrovski mentioned, this is extremely important for our app. I am a bit surprised that the badge counts would not include a count for anything other than the chat system with the Stream platform. We have many features in our app that require a badge count, otherwise our retention rates plummet. For example, we need a count for Friend Requests, if users don't have a notification for a request they will not open the app.
Based on the customers that Stream promotes on the home page, I am surprised that no other customer has brought this to your attention. Can you please let us know when you believe there will be a fix for this? This is vital for our success in the long term. We think Stream is great but this is definitely something that we will need in order to continue using the Stream platform.
Hi @aaceti,
The whole team is now analysing this, so I should have news pretty soon. We understand that this is an important limitation and want to see what is the best approach to deliver this. I'll let you know as soon as I have more news.
Feel free to create a new GH Issue, or GH Discussion, so that we can track this more easily, instead of a closed issue.
Thank you for using Stream!
Best, Nuno
Hi @aaceti, @sasojadrovski,
Although the backend team is analysing how they could provide a lightweight API for unread counts, there's actually a way you can be unblocked already.
When you receive a push notification from the Stream Server, you can save the unreads of Stream in UserDefaults. And then, whenever you get a notification from your own server, you merge it with what you saved on UserDefaults. This is actually a solution that is more efficient than calling an API on the Stream server to get the unreads.
Let me know if this works for you!
Best, Nuno