问题
I'm a Swift dev and I'm not a backend dev. This is actually 3 different questions in bold below but they all are dependent upon each other. Any stack overflow answers to similar questions would be enough to get me started
@followers
|
kim_userId // kimKardashian
-userId_0: 1
-... // every user in between
-userId_188_million: 1
Right now I'm using a very inefficient way to send a mass push notification:
@IBAction func postButtonTapped(button: UIButton) {
let postsRef = Database.database().reference().child("posts").child(kim_userId).child(postId)
postsRef.updateChildValues(postDictionary, withCompletionBlock: { (err, _)
if let error = error { return }
// post was successful now send a push notification to all of these followers
self.fetchFollowers(for: kim_userId, send: postId)
})
}
func fetchFollowers(for userId: String, send newPostId: String) {
let followersRef = Database.database().reference().child("followers").child(userId)
followersRef.observe(.childAdded) { (snapshot) in
let userId = snapshot.key
self.fetchDeviceToken(forFollower: userId, send: newPostId)
}
}
func fetchDeviceToken(forFollower userId: String, send newPostId: String) {
let usersRef = Database.database().reference().child("users").child(userId)
usersRef.observeSingleEvent(of .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
guard let deviceToken = dict["deviceToken"] as? String else { return }
self.sendPushNotification(toFollower: userId, with: deviceToken, send: newPostId)
}
}
func sendPushNotification(toFollower: userId, with deviceToken: String, send newPostId: String) {
var apsDict = [String: Any]()
// newPostId and whatever other values added to the dictionary
guard let url = URL(string: "https://fcm.googleapis.com/fcm/send") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: apsDict, options: [])
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("key=\(my_serverKey...)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let jsonData = data {
if let jsonDataDict = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] {
print("Received data:\n\(jsonDataDict))")
}
}
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
For eg Kim Kardashian has 188 million followers on Instagram, when she posts something it goes out to all of her followers at once. The way I'm currently doing it is not the way to go. I'm pretty sure this is a situation for Cloud Functions but I do not know enough about Cloud Functions so I am looking at where to start.
-how do I connect with Cloud Functions from within an iOS app?
-no matter what I have to get each follower from the "followers" ref and then I have to get each follower's deviceToken from within their "users" ref, I'm not sure where to start here
-how do I actually send a push notification code once inside Cloud Functions? I found this answer but it's in javascript. I don't know javascript but I do know a tad bit of Node.js
PostVC:
@IBAction func postButtonTapped(button: UIButton) {
let postsRef = Database.database().reference().child("posts").child(kim_userId).child(postId)
postsRef.updateChildValues(postDictionary, withCompletionBlock: { (err, _)
if let error = error { return }
// post was successful now connect to Cloud Functions so that a mass push notification can be sent
self.codeToConnectWithCloudFunctions(for: kim_userId, send: postId)
})
}
func codeToConnectWithCloudFunctions(for userId: String, send newPostId: String) {
// 1. how do I get each of her followers
// 2. how do I get each of their deviceTokens
// 3. how do I send the push notification
}
Any links with similar answers are enough to get me started. I can do more digging from there and ask a more specific question based on whatever I find
回答1:
How do I connect with Cloud Functions from within an iOS app?
You can call Cloud Functions by doing the same thing that you would do calling any API, you will have to design your Cloud Functions to be Callable Functions, you can find more details on them and how to set it all up, in Swift and other languages, in this Documentation.
No matter what I have to get each follower from the "followers" ref and then I have to get each follower's deviceToken from within their "users" ref, I'm not sure where to start here and how do I actually send a push notification code once inside Cloud Functions? I found this answer but it's in javascript. I don't know javascript but I do know a tad bit of Node.js
These 2 questions can be answered together by this community answer that states that you should integrate Firebase Cloud Messaging into your iOS app, with a link to the full documentation on that subject. Also, you can find in this Documentation how you can send notifications to multiple devices, in there you will also find an example of the code that you will need to use in the Cloud Function itself using the Admin SDK.
NOTE: Cloud Functions can be written in Node.js, Go, Java and Python, and all the sample codes for the Cloud functions are in those languages.
回答2:
Here's how to send data from an iOS app to the RealTimeDatabase using Cloud Functions:
Before you start you need to have node.js/npm
installed, it's easy to do, follow this youtube
1- assuming you have node
installed, go to the Firebase console > select Functions
in the right side > Upgrade
> Pay as you go Blaze Plan
> Get Started
> Continue
> Finish
2- open terminal and enter $ npm install -g firebase-tools
(if this is already installed you can skip this step). If you get Error: EACCES: permission denied response then enter $ sudo npm install -g firebase-tools
and enter your cpu password to continue
3- after it's finished enter $ npm --version
to see which version you have installed
4- create an empty folder and name it something like Cloud_Functions or whatever. Drag that folder into step 5 below. You will do all of the below steps inside this Cloud_Functions folder.
5- Go to the main folder where you Xcode project lives, mines is on my desktop and is named fooProject
6- cd into that folder $ cd fooProject
then $ cd Cloud_Functions
7- in terminal enter $ pwd
because if you have to be inside the Cloud_Functions folder for this wot work
8- in terminal enter $ firebase login
(enter your login credentials and press enter)
9- assuming your in the correct folder, in terminal enter $ firebase init functions
10.A- You might see this option first (or not at all) Which Firebase CLI features do you want to set up for this folder? Press Space
to select features, then Enter to confirm your choices, use the up/down key to choose Functions: Configure and deploy Cloud Functions
, first press the SPACE BAR
, second press ENTER
. If you press Enter first then this won't work, you must press the Space Bar first and then Enter second. If this option doesn't appear as it didn't with one project, did with another project, then didn't again with another project, then 10.B below will definitely appear first.
10.B- You might see this option second (or first) Use an existing project
, press enter
11- next option is Select a default Firebase project for this directory
, use the up/down arrows to select your project, press enter
12- next option is What language would you like to use to write Cloud Functions?
, the two options are Javascript and typescript, I used the up/down arrow to select Javascript
then pressed enter
13- next option is Do you want to use ESLint to catch probable bugs and enforce style?
I entered n
and pressed enter. These rules are very strict and you will get a bunch of errors when you deploy in step 22 if you don't know the rules beforehand. If you aren't aware of them I suggest that you select n
for no
14- next option is Do you want to install dependencies with npm now?
I entered y
and pressed enter
15- after it finished installing the next thing wasn't an option, it was a suggestion to update to the latest version and suggested entering $ npm install -g firebase-tools
. I kept getting errors so I skipped this step
16- while still inside the Cloud_Functions
folder, run $ ls
and you will see a functions
folder. Run $ cd functions
because the next step has to happen inside the functions
folder.
17- $ pwd
to make you are in the functions
folder
18- next run $ npm i --save firebase-functions@latest
19- next run $ open index.js
to open the index.js file. Mines automatically opened in Sublime
20- this is a simple youtube video on what to do with the existing code inside the index.js
file. Or you can simply uncomment all of the code, make sure to press save (command+S), and then run $ firebase deploy --only functions
. After a few minutes you should get a Deploy complete!
statement
21- You can comment out the sample code and here is the code to receive some data from a view controller inside my iOS app (step 25). Make sure your Firebase rules via the console allow writes to whatever ref you are writing to and make sure billing is setup (step 1) because it verifies billing when you first deploy (step 22). Inside the index.js
file enter:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.updateSneakerTypeToPostsRef = functions.https.onCall((data, context) => {
const postId = data.postId; // this will be abc123
const userId = data.uid; // this will be whatever the user's id is
const sneakerName = data.sneakerName; // this will be Adidas
const receivedTimeStamp = Date.now(); // Data.now() is how you receive the timestamp in Javascript/Node
// this is just a print statement
console.log("received values =" + " | postId: " + postId + " | userId: " + userId + " | sneakerName: " + sneakerName + " | timeStamp: " + receivedTimeStamp);
// this is the database path: posts/postId/userId in step 25 and I'm going to add the *sneakerName:Adidas* and *receivedTimeStamp* to it
var postsRef = admin.database().ref('/posts/' + postId + '/' + userId);
return postsRef.set({ "sneakerName": sneakerName, "timeStamp": receivedTimeStamp })
.catch((error) => {
console.log('ERROR - updateSneakerTypeToPostsRef() Failed: ', error);
});
}
22- save the above file and now in terminal enter below, the part after the colon is the name of the exports.
function from step 21 and it has to be the same exact name:
$ firebase deploy --only functions:updateSneakerTypeToPostsRef
. This will deploy only this 1 function. If you have several functions then use $ firebase deploy --only functions
If you removed the initial sample code you will get a response: The following functions are found in your project but do not exist in your local source code: helloWorld(us-central...) If you are renaming a function or changing its region, it is recommended that you create the new function first before deleting the old one to prevent event loss. For more info, visit https://firebase.google.com/docs/functions/manage-functions#modify ? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments.* You can enter no
to delete it or yes
to keep it, it doesn't matter. Either way press Enter afterwards.
This took about 3 minutes to finish but when it did I got functions[updateSneakerTypeToPostsRef(us-central4)]: Successful create operation. Deploy complete!
I entered this below just for log info:
$ firebase functions:log
// this is log data
Now to get to the Xcode project.
23- cd to the actual Xcode project, open your Podfile and put in pod 'Firebase/Functions'
then install it $ pod install
24- after it's installed go whatever view controller and add import Firebase
to the top of the file then add this line as a class property lazy var functions = Functions.functions()
25- Here is how to send data to the function inside the index.js
file (step 21)
import Firebase
lazy var functions = Functions.functions()
@IBAction func buttonTapped(_sender : AnyObject){
sendDataToCloudFunction()
}
func sendDataToCloudFunction() {
let data: [String: Any] = ["postId": "abc123",
"uid": Auth.auth().currentUser!.uid,
"sneakerName": "Adidas"]
let exportsName = "updateSneakerTypeToPostsRef" // *** this HAS TO BE the SAME exact function name from steps 21 and 22 ***
functions.httpsCallable(exportsName).call(data) { (result, error) in
print("Function returned")
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
print(code.debugDescription)
print(message.debugDescription)
print(details.debugDescription)
}
print(error.localizedDescription)
return
}
if let res = result {
print("------->", res)
}
if let operationResult = (result?.data as? [String: Any])?["operationResult"] as? Int {
print("\(operationResult)")
}
}
}
26- The result inside firebase will look like
@posts
@abc123
@whatever_the_userId_is...
-sneakerName: "Adidas"
-timeStamp: 1595874879.9619331
来源:https://stackoverflow.com/questions/63929228/firebase-how-to-send-mass-push-notifications-at-once-using-cloud-functions