Firebase - clear cached subscriptions after .delete() - ios

When a user logs out, I do:
FIRInstanceID.instanceID().delete(handler: { (error) in })
Which should (?) invalidate the token and unsubscribe from all topics.
It works, but logging in with the same device and calling FIRInstanceID.instanceID().token() (from notification when it's ready), I get the same token (not really a problem, but unexpected). However; subscribing to topics (upon login for instance) seems to be cached in the device from the previous login, so it doesn't make a network call, meaning the token will not be associated with any topics on the FCM side. Assume it's the same user doing a relog; the topics he/she wants to subscribe to are the same as before delete().
I can verify this by querying https://iid.googleapis.com/iid/info/<token> with the token: Even after calling subscribeToTopic in my app, the list of topics remains empty. Normally this call results in the -5 error (described here https://github.com/firebase/quickstart-ios/issues/146) when subscribing to multiple topics, but nothing happens, indicating to me that the application thinks it's already subscribed to the topics and hence does nothing.
So - how can I unsubscribe from all topics upon logout, and successfully resubscribe when logging in? Looping the topics and doing unsubscribeFromTopic seems a little hacky to me.

Instead you can disconnect the user from getting push notifications by doing FiRMessaging.messaging.disconnect()
And connect the user when he logs back in

Related

How to observe conversations in iOS app powered by Firebase Realtime Database?

=====================
I thought about it for a while and proposed a potential solution at the end of the question, do you think it's feasible?
=====================
I'm using Firebase Realtime Database to support chatting in my iOS app. I have a node called "conversations" in database, which is structured like so:
-conversations
-user_id_1 // This is you
-recipient_id_1 // id of the user who's chatting with you
-message_id_1
-message: <some_message>
-date: <some_date>
-message_id_2
-message: <some_message>
-date: <some_date>
-recipient_id_2
-message_id_1
-message: <some_message>
-date: <some_date>
-message_id_2
-message: <some_message>
-date: <some_date>
Whenever a new message is sent to a user, he should get a notification.
I have thought about using Push Notification, i.e. I use Cloud Function Trigger to observe the path:
functions.database.ref('conversations/{uid}/{recipientId}/{messageId}').onCreate((snapshot) => {
// Send push notification to user with "uid"
})
However, there's a problem with this, what if the user blocks the push notification of the app? The user is not gonna receive any message while he's not using the app (which is what he wants by turning off push notification), but he's not gonna get any message even when he's in the app. Therefore, I'm thinking, maybe I should use Firebase SDK in my app to observe database like so:
Database
.database().reference()
.child("conversations").child(<my_user_id>)
.observe(.childAdded) { (snapshot) in
// do stuff with snapshot
}
But then, this only observes newly started conversations, i.e. if the user already had a conversation with, say "person1", and "person1" sends a new message to the user, this observer wouldn't be triggered. Then, I was thinking:
Firstly, Keep the above observer so that I get notified when a new conversation is started
Secondly, Create multiple other observers that listen to my existing conversations:
Database
.database().reference()
.child("conversations").child(<my_user_id>).child(<existing_recipient_id>)
.observe(.childAdded) { (snapshot) in
// do stuff with snapshot
}
But this way, how do I know all my "existing_recipient_id"? I certainly shouldn't fetch them on start up:
Database
.database().reference()
.child("conversations").child(<my_user_id>)
.observeSingleEvent(of: .value) { (snapshot) in
// Extract all "existing_recipient_id"
}
Because this fetches all my conversations and their messages, which could potentially be multi-GBs.
I have the following 2 questions:
If I have 1,000 conversations with 1,000 people, should I have 1,000 observers in-app? If this wouldn't cause any problem, say performance issue, how do I set up these observers so that I could avoid the problems above?
I'm definitely using push notification, in case user is not in the app while a new message is sent to him. But, I don't know whether the user is in app or not, if he is, I already have those observers, push notification seems redundant, and it raises the cost. Is there a way that I can avoid this?
Temp solution
Write a cloud function that fetches all "existing_recipient_id", which is just an array of strings.
In the app, I add a listener that monitors the device's connection state, whenever the device comes back online from offline, also when the app first launches, I call that cloud function to fetch id's of all existing conversations.
Add observers to each of those conversations
Add observer to listen to new conversation
But I still don't know what to do about push notification when user is in-app, I guess I'll do it even though it's redundant

Send notification to everybody but user who triggered it?

I'm using Firebase Messaging to send out notifications to users of my iPhone app. My database is structured like this:
- Users
- user1
- user2
- Groups
- group1
- members
- user1
- user2
When a user joins a group they get subscribed to a topic corresponding to that group. I have a cloud function that listens for writes in that group, and sends a notification to the groups topic when a write happens:
exports.sendNotifs = functions.database
.ref('pets/{petId}/events/{eventId}').onWrite(event => {
const pet_Id = event.params.petId;
const payload = {
'notification': {
'title': `${toTitleCase(name)} just logged an event`,
'body': `${events[eventType]} for ${toTitleCase(petName)}`,
'sound': 'default',
}
};
admin.messaging().sendToTopic(pet_Id, payload);
});
However, this results in everybody getting a notification including the person who did the write that triggered the notification. I only want other people in the group to display a notification since the triggering user doesn't need to see one. I tried appending the sending user's uid as extra data of the notification and only displaying the notification if the recieving user's uid doesn't match the notification data's uid. This works when the application is in the foreground but not if its in the background, so if the user writes then closes the application before he receives the notification it'll display for him when he receives it, something I'm trying to avoid.
How can I make sure only other members of a group get a notification? Are messaging topics not good for this?
If you use Topics it's not possible to send to everyone except one.
If you are Ok sending to everyone, and then filtering on the client, you will need to use the data messages, and not notification messages, to avoid the problem with background/foreground you described.
https://firebase.google.com/docs/cloud-messaging/concept-options
i guess that the solution is:
each iOS client from group1 will subscribe to topic /topics/group1.selfUserId
the server already have the list with all users that are part of group1
the iOS client ask server to send notification to group1.members beside selfUserId (make a http post request)
server send gets all group1.members beside selfUserId and send notification to all /topics/group1.memberX
As already mentioned, you cannot exclude someone who has been registered to a Topic.
So what you can do is sending a message to a single device. So for example you have a group of ten persons, one person posts a message, you have to send nine single messages to the other nine persons of the group.
What you need to do is to store the registration token of every single user into your database and you have to take into account that registration tokens will change after some time.

Handling IAP Fullfilment results for Windows 10 Store

When providing consumable In App Purchases on the Windows 10 Store, there are FullfillmentResults when ReportConsumableFullfillmentAsync is called.
The user of my app has had their IAP fullfilled by the time I get this result. This means they have their Coins/Gems/Potatoes.
But if I receive FulfillmentResult.PurchaseReverted, then what happened? How did the user just revert the purchase? Am I meant to withdraw their Coins/Gems/Potatoes?
What are scenarios behind the other error messages?
Note: I'm working with using Windows.ApplicationModel.Store
But if I receive FulfillmentResult.PurchaseReverted, then what
happened? How did the user just revert the purchase? Am I meant to
withdraw their Coins/Gems/Potatoes?
The value PurchaseReverted means the transaction is canceled on the backend and users get their money back. So you should disable the user's access to the cosumable content (withdraw the Coins/Gems/Potatoes) as necessary.
What are scenarios behind the other error messages?
NothingToFulfill : The transaction id has been fulfilled or is otherwise complete
PurchasePending: The purchase is not complete. At this point it is still possible for the transaction to be reversed due to provider failures and/or risk checks. It means the purchase has not yet cleared and could still be revoked.
ServerError: There was an issue receiving fulfillment status. It might be the problem from the Store.
Succeed: The fulfillment is complete and your Coins/Gems/Potatoes can be offered again.
Here is the documentation about FulfillmentResult Enum

Accessing previous MSMessages from MSConversation

If I have sent a few messages in my iMessage app and I want to access previous messages (obviously just my own app-created messages, not just any messages the users have sent in their conversation), is there a way I can do that?
I can access the most previous message with this:
[self activeConversation].selectedMessage;
Any way to loop through previous messages that might not have even ever been clicked by the user (so simply storing it in user defaults is not an option)
There is no way to do this. Apple considers this to be a security/privacy issue.

Sinch Client works but messages aren't being sent

I'm using sinch and I've gotten the client to work by registering a user, but I can't send a message. When I click my send button, nothing happens. The required methods that need to be implemented, such as messageFailed(), messageDelivered(), etc. are not being called either. Does anyone know why?
Have you registered the one more user? Can you enable logging and share the full log output.
See comment below, user tried to send message to self

Resources