Send notification to everybody but user who triggered it? - ios

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.

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

How to invoke our app and get the details from incoming Call?

If any number is saved in my application and That user call me on my iPhone then I want to invoke my application with screen where user can fill the information about that calls.(Like Call duration,Caller Name and Some application specific details)
Please guide me how to achieve log a call in iOS. I am not sure that Apple allow us to get the information about the call which is done by normal dialer not by my application.
I have find this link : Call history, SMS history, Email history in iOS
But I want to invoke my application on incoming call if that user contact number is saved in my application.
Please help me or suggest me the solution or whether it is feasible or not.
iOS 10 +:
Use Callkit, and check it out call directory extension
Below method is called : Only when the system launches the app
extension and not for each individual call, you must specify call
identification information all at once; you cannot, for example, make
a request to a web service to find information about an incoming call.
Use the addIdentificationEntry(withNextSequentialPhoneNumber:label:) method.
class CustomCallDirectoryProvider: CXCallDirectoryProvider {
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
let labelsKeyedByPhoneNumber: [CXCallDirectoryPhoneNumber: String] = [ … ]
for (phoneNumber, label) in labelsKeyedByPhoneNumber.sorted(by: <) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
context.completeRequest()
}
}
According to Apple:
Identifying Incoming Callers When a phone receives an incoming call,
the system first consults the user’s contacts to find a matching phone
number. If no match is found, the system then consults your app’s Call
Directory extension to find a matching entry to identify the phone
number. This is useful for applications that maintain a contact list
for a user that’s separate from the system contacts, such as a social
network, or for identifying incoming calls that may be initiated from
within the app, such as for customer service support or a delivery
notification. For example, consider a user who is friends with Jane in
a social networking app, but who doesn’t have her phone number in
their contacts. The social networking app has a Call Directory app
extension, which downloads and add the phone numbers of all of the
user’s friends. Because of this, when the user gets an incoming call
from Jane, the system displays something like “(App Name) Caller ID:
Jane Appleseed” rather than “Unknown Caller”.

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.

Firebase - clear cached subscriptions after .delete()

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

Migrating Parse Push to AWS

I am having an issue with Parse Push on AWS. We have the adapter configured per these specifications and can seem to broadcast to one specific user (don't know how or why that user) using the curl method, but I am confused how (or if) I can use this to send user to user push notifications. For example, "X liked Y's Z" Where X is the liker, Y is the liked and Z is the object being liked.
in parse you have multiple options to send push notifications.
Push notifications can be sent to one or more users by providing a query with all the installations that you like to send the push to. You can also send push notifications for specific channel that the user subscribed to, this is very good for marketing or maybe if your app have different type of users (for example: sellers, buyers etc.)
The reason that a push is being sent to installations and not to users is because that one users can have multiple installations (e.g. iphone,ipad,other device etc.)
like i said there are multiple options to send push but i recommend to send a push using one of the following approaches:
From cloud code - you can create cloud code function that will be triggered by the client and this cloud code function will first create a query of all the installations that you need to send the push to and will execute the function that will actually send the push. This cloud code function can receive multiple parameters that can contain any data that needs to be processed before you sending the push, such data can be array of users, channel name and more.
the following cloud code snippets show how to send a push for all users who successfully subscribed and have device token:
Parse.Cloud.afterSave("SendPush", function(request) {
var query = new Parse.Query(Parse.Installation);
query.exists("deviceToken");
var payload = {
alert: "after save push"
};
Parse.Push.send({
data: payload,
where: query
}, {
useMasterKey: true
})
.then(function() {
response.success("Push Sent!");
}, function(error) {
response.error("Error while trying to send push " + error.message);
});
});
and then from your iOS SDK you call this cloud code function in the following way:
NSDictionary * parameters = # {}; // put parameters if required
[PFCloud callFunctionInBackground: #"SendPush"
withParameters: parameters block: ^ (id _Nullable object, NSError * _Nullable error) {
// callback result
}
];
From iOS SDK - if you don't want to send your push from cloud code you can do it directly from your iOS. I think it's better to do it in cloud code because in cloud code you write ones and then you can trigger this function from any SDK and also from the REST API.
In Parse docs you can find a lot of examples on how to send push from iOS SDK all the examples can be found here:
http://parseplatform.github.io/docs/ios/guide/#push-notifications
but like i said the best is to do it with cloud code.
Answer is similar if you're using AWS services without Parse. A better architecture is where the mobile device invokes business logic in the cloud (i.e., in an AWS Lambda function) and that code sends the push notifications. This allows you to build a more secure solution because you can control content and control who can send to whom (i.e., you must be on someone's friend list to send) within your business logic. If you open permissions to publish directly from the device, then you make system vulnerable to attacks where someone uses the app's credentials and publishes harmful content (potentially to all your app's users).
Example of sending push notification from an AWS Lambda function...
Can you send SNS push notification from lambda function in Amazon AWS?

Resources