Reputation: 439
I am new iOS developer.
I am using firebase and I am trying to make a chat application in which I need to notify user when he/she receives any message. For this purpose I tried firebase push notifications, but couldn't be able to trigger them when other user send the message. The only way I found is using firebase console to send push notification, but it doesn't fulfill my requirements. I've just configured local notifications. Please guide me how can we trigger push notifications without using the firebase console.
Upvotes: 6
Views: 2888
Reputation: 11
// PushNotificationSender.swift
//
//
// Created by DRT on 14/02/20.
// Copyright © 2020 mk. All rights reserved.
//
import Foundation
import UIKit
import CryptoKit
import SwiftJWT
class PushNotificationSender {
func sendPushNotification(FCMtoken: String, title: String, body: String,type: String,targetFUserId: String,firebasePushId: String) {
self.getAccessToken { result in
switch result {
case .success(let token):
print(token)
self.sendFCMMessage(token: token,
fcmToken: FCMtoken,
title: title,
body: body,
type: type,
targetFUserId: targetFUserId,
firebasePushId: firebasePushId)
case .failure(let failure):
print(failure)
}
}
}
// this func will send the notification to the fcm token device
func sendFCMMessage(token: String, fcmToken: String, title: String, body: String,type: String,targetFUserId: String,firebasePushId: String) {
let url = URL(string: "https://fcm.googleapis.com/v1/projects/gymstar-94659/messages:send")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"message": [
"token": fcmToken,
"notification": [
"title": title,
"body": body
],
"data": [
"badge" : "1",
"sound" : "default",
"notification_type": type,
"message": body,
"firebaseUserId": targetFUserId,
"firebasePushId": firebasePushId
]
]
]
let bodyData = try! JSONSerialization.data(withJSONObject: body, options: [])
request.httpBody = bodyData
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] {
NSLog("Received data:\n\(jsonDataDict))")
}
}
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
// this func will return access token and if some error occurs, it returns error
func getAccessToken(completion: @escaping (Result<String, Error>) -> Void) {
guard let jsonPath = Bundle.main.path(forResource: "serviceAccount", ofType: "json") else {
completion(.failure(NSError(domain: "FileNotFound", code: 404, userInfo: nil)))
return
}
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: jsonPath))
let credentials = try JSONDecoder().decode(ServiceAccountCredentials.self, from: jsonData)
let iat = Date()
let exp = Date(timeIntervalSinceNow: 3600) // Token valid for 1 hour
// Create the JWT claims
let claims = JWTClaims(
iss: credentials.client_email,
scope: "https://www.googleapis.com/auth/cloud-platform", // Change this to your required scope
aud: "https://oauth2.googleapis.com/token",
iat: iat,
exp: exp
)
// Create a JWT signer using the private key
var jwt = JWT(claims: claims)
// Private key in PEM format, needs to be cleaned
let privateKey = credentials.private_key
.replacingOccurrences(of: "-----BEGIN PRIVATE KEY-----", with: "")
.replacingOccurrences(of: "-----END PRIVATE KEY-----", with: "")
.replacingOccurrences(of: "\n", with: "")
let jwtSigner = JWTSigner.rs256(privateKey: Data(base64Encoded: privateKey)!)
let signedJWT = try jwt.sign(using: jwtSigner)
// Send the signed JWT to Google's token endpoint
getGoogleAccessToken(jwt: signedJWT) { result in
switch result {
case .success(let accessToken):
completion(.success(accessToken))
case .failure(let error):
completion(.failure(error))
}
}
} catch {
completion(.failure(error))
}
}
// Send the JWT to Google's OAuth 2.0 token endpoint
func getGoogleAccessToken(jwt: String, completion: @escaping (Result<String, Error>) -> Void) {
let tokenURL = URL(string: "https://oauth2.googleapis.com/token")!
var request = URLRequest(url: tokenURL)
request.httpMethod = "POST"
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let bodyParams = [
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": jwt
]
request.httpBody = bodyParams
.compactMap { "\($0)=\($1)" }
.joined(separator: "&")
.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(NSError(domain: "NoData", code: 500, userInfo: nil)))
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let accessToken = json["access_token"] as? String {
completion(.success(accessToken))
} else {
completion(.failure(NSError(domain: "InvalidResponse", code: 500, userInfo: nil)))
}
} catch {
completion(.failure(error))
}
}
task.resume()
}
}
struct ServiceAccountCredentials: Codable {
let client_email: String
let private_key: String
}
struct JWTClaims: Claims {
let iss: String
let scope: String
let aud: String
let iat: Date
let exp: Date
}
extension Data {
func base64URLEncodedString() -> String {
return self.base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}
NOTE#: Migrate from legacy FCM APIs to HTTP v1.
NOTE#:You can find the serviceAccount.json file in the Firebase Console where you registered push notifications.
Upvotes: 0
Reputation: 1810
I was able to send Push Notifications from my code using PushNotificationSender
class from this article -
https://www.iosapptemplates.com/blog/ios-development/push-notifications-firebase-swift-5
Upvotes: 1
Reputation: 439
Finally found the solution after struggling for almost 1 month.
These are the basic steps
Firs off all you need to make sure that you have an active apple developers account
just enable firebase push notifications here ie the link of youtube video for this step
i am finding it difficult to post code in its exact form but the code starts from here
const functions = require('firebase-functions');
const admin =require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const ref=admin.database().ref();
exports.sendNotification=functions.https.onRequest((req,res) =>{
const id = req.query.id
const title = req.query.title
const message = req.query.message
const key = req.query.key
// admin.database.ref()
const payload ={
notification: {
title: title,
body: message,
//badge: '1',
sound: 'default',
}
};
console.log('Param [key] is '+key);
const keys = [] ;
ref.child('keys').once('value')
.then(snap => {
snap.forEach(childSnap => {
const loopKey=childSnap.val().key;
keys.push(loopKey);
})
return keys;
})
.then(keys=>{
//console.log('Keys in database : '+keys.join());
if(keys.indexOf(key)>-1)
{
if(!key || !id || !message)
{
res.status(400).send('Bad request');
}
else{
ref.child('users').child(id).child('fcmToken').once('value')
.then(snapshot => {
if (snapshot.val()){
const token = snapshot.val()
console.log(token);
return admin.messaging().sendToDevice(token,payload).then(response =>{
res.status(200).send('Notification sent')
});
}
});
}
}
else
{
console.log("In-valid key : "+key);
res.status(400).send('Bad request');
}
})
.catch(error => {
res.send(error)
});
});
ends at this point
this is the function to store your fcm to database
func postToken(token: String, id: String){
print("FCM Token \(token)")
let ref = Database.database().reference().child("users").child(id)
ref.child("fcmToken").setValue(token)
}
here is the function which i used to trigger this API
func sendNotification(id: String, title: String, message: String){
var url = "your URL"
var urlComponents = NSURLComponents(string: url)
urlComponents?.queryItems = [
URLQueryItem(name: "key", value: self.apiKey),
URLQueryItem(name: "id", value: id),
URLQueryItem(name: "title", value: title),
URLQueryItem(name: "message", value: message)
]
Alamofire.request((urlComponents?.url)!).responseJSON { (response) in
print(response.response)
print(response.response)
print(response.result)
}
}
the above API was written according to my database structure. you can change it easily for your own structure.
after doing this all you'll be able to send your notifications after hitting the URL
Hope it will give a nice idea to you people to work with your own notifications according to your need.
Upvotes: 0