hmali
hmali

Reputation: 439

how to trigger push notifications of firebase from an ios device using swift3

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

Answers (3)

SYED M ABDUL REHMAN
SYED M ABDUL REHMAN

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

IvanPavliuk
IvanPavliuk

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

hmali
hmali

Reputation: 439

Finally found the solution after struggling for almost 1 month.

These are the basic steps

  1. Firs off all you need to make sure that you have an active apple developers account

  2. just enable firebase push notifications here ie the link of youtube video for this step

  3. Now your app is set up for firebase remote notifications but we can only trigger them from the firebase console so here is the tricky part. here is the link of video to enable firebase console on your mac
  4. for the first time it will be good to see this video too because in this video they'll learn to write code in node.js and deploy it to the firebase.
  5. Now if anyone wants to make an API instead of making a trigger then here is a code of API which sends the notification to other user by getting his FCM token from the firebase... You can modify it according to your need

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

Related Questions