Rails

Ruby: Sử dụng FCM để push notification đến Android, iOS

Giới thiệu

Push notification là một tính năng rất phổ biến trong việc phát triển app di động hiện nay. Có rất nhiều cơ chế để gửi push notification, trong bài viết này chúng ta sẽ tìm hiểu về Firebase Cloud Messaging (FCM), 1 dịch vụ hoàn toàn miễn phí của Google.

Cách thức hoạt động

Mình sẽ giải thích đơn giản:

  • Khi thiết bị của user cài app thì sẽ gửi yêu cầu lên Firebase Cloud Server để xin token, tạm gọi là device token.
  • Firebase Server sau khi nhận request sẽ trả về 1 token, token này là duy nhất cho mỗi thiết bị.
  • Server thực hiện lưu token này xuống database sao cho không trùng nhau.
  • Service của server gửi token này + gói tin cần truyền cho Firebase.
  • Firebase kiểm tra (check token các thứ...) rồi thực hiện gửi đến device đã đăng kí với token đó.

Xong!

Các bước thực hiện

1. Đăng kí tài khoản trên https://firebase.google.com

2. Tạo 1 project mới

Project này sẽ là trung gian nhận/gửi gói tin của các chúng ta. Phần Settings, chuyển sang tab Cloud Messaging, ở đây sẽ có Server key dùng cho các thủ tục credential, cần lưu ý key này.

3. Build client

Tab General, mục Your apps, bạn cần tạo 1 client để test việc nhận thông báo, việc này các bạn sẽ tự làm. Ở trong bài viết này, mình đã build sẵn 1 Web app để test, các bạn có thể follow theo các bước hướng dẫn rất chi tiết trên Firebase để tự build client cho mình. Chú ý phần script nhúng vào app sẽ bao gồm các thông số của Project

Đây là demo web client mình đã build thử, với Android và iOS tương tự, giao diện trang web client sẽ như thế này:

Ở đây client mình đã tự generate ra Token, mình chỉ cần lưu token này vào database rồi sử dụng sau, hoặc có thể gọi trực tiếp (chi tiết bên dưới)

4. Xây dựng service cho việc gửi thông báo

Sau khi build được client, việc tiếp theo là viết service ở server thôi ✌️ Ở đây ta sẽ dùng Rails. Bắt đầu nào:

Ở server, ta sẽ dùng gem fcm có sẵn:

#gemfile

gem "fcm"

Chạy bundle install.

Đảm bảo bạn đã có 1 bảng để lưu device_token nhận được từ Firebase rồi nha. Trong bảng device_tokens này chỉ cần lưu user_iddevice_token là đủ. Bảng này đặt quan hệ belongs_to với bảng users.

#user.rb:

  has_many :device_tokens, dependent: :destroy

#device_token.rb:

  belongs_to :user
  validates :device_token, presence: true, uniqueness: true

Ta sẽ tạo 1 service, đặt tên là push_notification_service.rb.

Khai báo các thuộc tính cần có:

def initialize(message = nil, to = nil, notification_type = \'default\', options = nil)
   self.message = message
   self.to = to
   self.notification_type = notification_type
   self.options = options
end
  • message: nội dung gói tin
  • to: người nhận
  • type: loại thông báo
  • options: các tùy chọn kèm theo

Hàm gửi:

Đầu tiên cần khởi tạo một class FCM mới với server key mình đã nói ở bước 2 (nên cho vào biến môi trường)

fcm = FCM.new(ENV["SERVER_KEY"])

**Định dạng gói tin: **

Sẽ có dạng như bên dưới :

   message = {
      data: {
          alert: self.message,
          sound: \'default\'
        },
      notification_type: self.notification_type
    }

Ở đây ta đã có device_token của user, các bạn có thể lấy thông qua to chính là user ở trên.

registration_ids = self.to.device_tokens.try(:pluck, :device_token)

Gem fcm đã cung cấp sẵn 1 hàm send rất tiện, bạn có thể đọc trên đây để biết thêm.

fcm.send(registration_ids, message) if registration_ids.present?

Gom lại nào:

def deliver
  fcm = FCM.new(ENV["SERVER_KEY"])
  message = {
     data: {
         message: self.message,
         sound: \'default\'
       },
     notification_type: self.notification_type
   }
   
   registration_ids = self.to.device_tokens.try(:pluck, :device_token)
   
   fcm.send(registration_ids, message) if registration_ids.present?
end

OK, cơ bản là đã xong, giờ test thôi :v

Test thử nào

Token mình đã có sẵn ở trên và update vào User.first

user_id: 1
device_token: "fMx4LFFE0vs:APA91bFRILLVm9nZJx6wp0ooaOHXY4kUbWPgOYhorEHkk5jZdabFUTE_WKWzx-L2NNSR0WkuBad_7gd4LzXfY2fMNkJlqsllj-5ZiOHrVFQUz71mDGNGquLHTWGhqN6CHERv5bwTcU6o"
rails c
[1] pry(main)> pusher = PushNotificationService.new("hello world", User.find(1), \'default\')
[2] pry(main)> pusher.deliver
=> {:body=>
  "{\"multicast_id\":6376897082082744799,\"success\":1,\"failure\":0,\"canonical_ids\":0,\"results\":[{\"message_id\":\"0:1522720038494785%2fd9afcdf9fd7ecd\"}]}",
 :headers=>
  {"content-type"=>["application/json; charset=UTF-8"],
   "date"=>["Tue, 03 Apr 2018 01:47:18 GMT"],
   "expires"=>["Tue, 03 Apr 2018 01:47:18 GMT"],
   "cache-control"=>["private, max-age=0"],
   "x-content-type-options"=>["nosniff"],
   "x-frame-options"=>["SAMEORIGIN"],
   "x-xss-protection"=>["1; mode=block"],
   "server"=>["GSE"],
   "alt-svc"=>
    ["hq=\":443\"; ma=2592000; quic=51303432; quic=51303431; quic=51303339; quic=51303335,quic=\":443\"; ma=2592000; v=\"42,41,39,35\""],
   "accept-ranges"=>["none"],
   "vary"=>["Accept-Encoding"],
   "connection"=>["close"]},
 :status_code=>200,
 :response=>"success",
 :canonical_ids=>[],
 :not_registered_ids=>[]}

Và kết quả đây:

Thành công!

Registration Login
Sign in with social account
or
Lost your Password?
Registration Login
Sign in with social account
or
A password will be send on your post
Registration Login
Registration