Push notification với Firebase Cloud Messaging trong React Native
Hi~! Nhân lúc làm app với React Native và Firebase nên trong bài viết này mình sẽ hướng dẫn các bạn cách đẩy thông báo (push notification) sử dụng FCM (Firebase Cloud Messaging) đồng thời cũng giải đáp một số thắc mắc mà mình mắc phải (và mình nghĩ đa số ai cũng sẽ gặp) khi code, nào bắt đầu thôi!
*Note: Đây là 1 bài khá dài do không chỉ đơn thuần show code nên các bạn có hứng thú cao độ thì cứ đọc hết nhé ;)
Push notification là gì?
Push Notification là một dạng tin nhắn đơn giản xuất hiện cùng với thông báo của hệ thống hoặc giống như tin nhắn bật lên -tuỳ thuộc vào nền tảng. Định nghĩa khá đơn giản và bạn chắc cũng đã gặp rất nhiều khi sử dụng smartphone, dưới đây là hình ảnh về nó:
Nguồn: internet
Tiếp theo chúng ta cùng tìm hiểu 1 chút về lịch sử ra đời của nó.
Lịch sử phát triển
- 6/2019: Apple ra mắt Apple Push Notification Service (APNs), dịch vụ push notification đầu tiên.
- 5/2010: Google phát hành dịch vụ riêng: Google Cloud to Device Messaging (C2DM).
- 5/2013: Google giới thiệu ‘rich notification’. Rich notification có thể chứa ảnh, các nút hành động – giúp người dùng thực hiện hành động ngay lập tức từ notification: phát nhạc, mở ứng dụng,… 9/2014: Apple thêm các nút tương tác. Các nút này cho phép người dung gửi phản hồi ngay lập tức cho nhà xuất bản ứng dụng, ngay sau đó ra mắt push notification trên Apple Watch.
Và phần quan trọng nhất: nó dùng để làm gì?
Lợi ích
Push Notification cung cấp sự tiện lợi và nhiều giá trị cho người dùng. Ví dụ người dùng có thể nhận được:
- Kết quả bóng đá và tin tức ngay trên màn hình khóa.
- Các thông báo từ ứng dụng khác (thông báo tin nhắn đến, bài viết mới,…).
Còn về phía nhà xuất bản ứng dụng:
- Cách để nói chuyện trực tiếp với người dùng.
- Không bị cho vào spam như mail, tăng tỉ lệ tương tác.
- Nhắc nhở người dùng sử dụng ứng dụng cho dù ứng dụng có được mở hoặc không.
- Quảng cáo, thông báo cho người dùng.
Nguồn: internet
Đến đây chắc các bạn cũng nắm được push notification là gì rồi đúng không? Vậy tiếp theo chúng ta sẽ dùng FCM để push notification trên Android và iOS, còn vì sao lại dùng FCM? Đương nhiên bạn có nhiều lựa chọn khác như OneSignal nhưng hiện tại mình đang làm app theo phong cách rất ‘firebase’ nên mình chơi luôn của nó :) Cho bạn này chưa biết về Firebase thì tiếp theo là vài phút quảng cáo…
Firebase là gì?
Firebase là một dịch vụ hệ thống backend được Google cung cấp sẵn cho ứng dụng Mobile của bạn, với Firebase bạn có thể rút ngắn thời gian phát triển, triển khai và thời gian mở rộng quy mô của ứng dụng mobile mình đang phát triển. Hỗ trợ cả 2 nền tảng Android và IOS, Firebase mạnh mẽ, đa năng, bảo mật và là dịch vụ cần thiết đầu tiên để xây dưng ứng dụng với hàng triệu người sử dụng. Link trang chủ: Firebase
Hiện tại dịch vụ mà Firebase cung cấp rất nhiều nên bạn có thể tha hồ vọc vạch, với Firebase việc bạn cần làm chỉ là tập trung code cho tốt phía client thôi.
Nguồn: internet
Với bài viết này chúng ta sẽ cùng vọc Cloud Messaging - nằm trong nhóm Grow, nó là…
Firebase Cloud Messaging
Nguồn: internet
Tóm gọn trong 3 dòng súc tích:
- Firebase Cloud Messaging(FCM) là phiên bản mới của Google Cloud Messaging(GCM).
- Đây là một giải pháp nhắn tin đám mây đa nền tảng.
- Bạn có thể sử dụng Firebase Cloud Messaging cho bất kỳ loại thiết bị người dùng cuối nào bao gồm iOS, Android hoặc thậm chí Web mà không mất phí.
Tính năng chính
FCM cung cấp cho chúng ta 3 tính năng chính như sau:
- Gửi tin nhắn thông báo (notification messages) hoặc tin nhắn dữ liệu (data messages).
- Mục tiêu gửi tin nhắn đa dạng: 1 thiết bị cụ thể, 1 nhóm các thiết bị hoặc thiết bị đăng kí theo chủ đề.
- Gửi tin nhắn từ app đến server.
Tổng quan về cách thức hoạt động:
Nguồn: internet
Cụ thể hơn, chúng ta có 3 cách làm sau:
- Cách 1: dùng app server của bạn
Nguồn: internet
- Cách 2: kết hợp Firebse Function
Nguồn: internet
- Cách 3: dùng Notification Composer
Trong bài này mình sẽ thực hiện cách thứ 3, cách 1 và 2 có hơi đặc thù hơn nên sẽ được tách ra thành các bài viết khác để dễ theo dõi.
Phew~! Phần lý thuyết đã xong, Giờ hãy bật Editor lên và code thôi!
Coding
Tổng quan 1 chút: mình sẽ dùng React Native bản 0.6 để code app và FCM bản 20.0 để push notification.
Chuẩn bị
- React native app: xem cách tạo app tại ĐÂY
- Cài đặt dependencies:
- react-native-firebase: xem cách thiết lập tại ĐÂY
- Tạo và config trên Firebase: xem tại ĐÂY(Updating..)
Tiến hành
Bước 1: Cài đặt Firebase Module
Trong android/app/build.gradle
thêm:
1
2
3
4
dependencies {
// ...
implementation "com.google.firebase:firebase-messaging:20.0.0"
}
Trong android/app/src/main/java/com/[app name]/MainApplication.java
thêm:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
import com.facebook.react.ReactApplication; //<- Dòng này
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;//<- Dòng này
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;//<- Dòng này
public class MainApplication extends Application implements ReactApplication {
// ...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new RNFirebaseMessagingPackage());//<- Dòng này
packages.add(new RNFirebaseNotificationsPackage());//<- Dòng này
return packages;
}
};
// ...
}
Trong android/app/src/main/AndroidManifest.xml
thêm:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
//...
<application ...>
<service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService" />
<activity
...
android:launchMode="singleTop"
>
</application>
Xong, các bạn build app xem có bị lỗi gì không, không lỗi thì tiếp tục nào (có lỗi thì tra google hoặc comment bên dưới ngay nhé). À đến đây là các bạn có thể test sơ sơ được rồi :). Để test các bạn làm như sau:
STEP 1 Mở app và sau đó để ở chế độ Background.
STEP 2 Truy cập console.firebase.google.com, vào ‘Cloud Messaging’ và ấn vào nút Send your first message
Bạn sẽ được chuyển đến Notification Composer - bộ công cụ soạn Notification của Firebase, ở đây các bạn có thể tạo nhanh 1 notification gồm: Tiêu đề (title), nội dung (text), hình ảnh (image),… Bạn có thể nhìn phần review notification của mình ở bên phải.
Để push được ngay lúc này, bạn đến phần Target
và chọn đến app của mình
Những phần khác bạn có thể tìm hiểu tại ĐÂY, còn bây giờ ấn Review
để thấy được tổng quan phần config mà bạn vừa làm
Ấn Publish
, nếu có thông báo success nhìn đến phần notification sẽ được như thế này
Giống Youtube, Zalo,… các kiểu rồi đó :) Cuối cùng chúng ta sẽ code thêm vài dòng để bắt các sự kiện khi notification được đẩy đến và xử lý nó.
Bước 2: Bắt sự kiện
Mở file App.js
là sửa thành:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, { Component } from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
Alert
} from 'react-native';
import {
Header,
Colors,
} from 'react-native/Libraries/NewAppScreen';
import { AsyncStorage } from 'react-native';
import firebase from 'react-native-firebase';
import type { Notification } from 'react-native-firebase';
export default class App extends Component {
async componentDidMount() {
this.checkPermission();
this.createNotificationListeners();
};
async componentWillUnmount() {
}
//Custom Functions
//For basic config before listenning Noti
//Step 1: check permission for Service
async checkPermission() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
this.getToken();
}
else {
this.requestPermission();
}
}
//Step 2: if not has permission -> process request
async requestPermission() {
try {
await firebase.messaging().requestPermission();
// User has authorised
this.getToken();
} catch (error) {
// User has rejected permissions
console.log('quyền bị từ chối');
}
}
//Step 3: if has permission -> process get Token
async getToken() {
let fcmToken = await AsyncStorage.getItem('fcmToken');
if (!fcmToken) {
fcmToken = await firebase.messaging().getToken();
console.log('token = ', fcmToken);
if (fcmToken) {
// user has a device token
await AsyncStorage.setItem('fcmToken', fcmToken);
}
}
}
//For Listenning Notification
async createNotificationListeners() {
//Tạo channel
const channel = new firebase.notifications.Android.Channel('test-channel', 'Test Channel', firebase.notifications.Android.Importance.Max)
.setDescription('My apps test channel');
console.log('my chanel id = ', channel);
firebase.notifications().android.createChannel(channel);
//Vietnamese explain: khi đang ở foreground => show alert khi có noti
this.notificationListener = firebase.notifications().onNotification((noti) => {
const { title, body } = noti;
Alert.alert(title, body);
});
}
// End Custom Functions
render() {
return (
<View>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<Header />
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step One</Text>
<Text style={styles.sectionDescription}>
Edit <Text style={styles.highlight}>App.js</Text> to change this
screen and then come back to see your edits.
</Text>
</View>
</View>
</ScrollView>
</SafeAreaView>
</View>
)
}
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
engine: {
position: 'absolute',
right: 0,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
highlight: {
fontWeight: '700',
},
footer: {
color: Colors.dark,
fontSize: 12,
fontWeight: '600',
padding: 4,
paddingRight: 12,
textAlign: 'right',
},
});
Việc copy đoạn trên thì đơn giản rồi nên tiếp theo mình sẽ giải thích từng bước config để bắt được sự kiện khi notification được push đến (phần này dành cho những bạn muốn hiểu nhiều hơn là việc copy code, nếu bạn đã hiểu rồi thì xuống phần dưới nhé ;)). Nào, xoắn não thôi!
Dòng config các bạn hình dung như sau:
- Setup vài thứ (trong hàm
checkPermission
)- Kiểm tra quyền: chúng ta sẽ phải yêu cầu quyền thông báo ứng dụng trong Alert Dialog.
1 2 3 4 5 6 7 8 9
async checkPermission() { const enabled = await firebase.messaging().hasPermission(); if (enabled) { this.getToken(); } else { this.requestPermission(); } }
- Nếu quyền được cấp, chúng ta sẽ lấy FCM Token về (như ID của mỗi thiết bị) và lưu vào
AsyncStorage
.1 2 3 4 5 6 7 8 9 10 11
async getToken() { let fcmToken = await AsyncStorage.getItem('fcmToken'); if (!fcmToken) { fcmToken = await firebase.messaging().getToken(); console.log('token = ', fcmToken); if (fcmToken) { // user has a device token await AsyncStorage.setItem('fcmToken', fcmToken); } } }
Để thấy được công dụng của Token, hãy log (dùng console.log) key của thiết bị bạn ra, sau đó vào Notificaion Composer, điền đầy đủ title và text thì nút Send test message sẽ được bật, bạn ấn vào
- Kiểm tra quyền: chúng ta sẽ phải yêu cầu quyền thông báo ứng dụng trong Alert Dialog.
Điền key vào Add an FCM registration token sau đó ấn nút Test, Boom! Notification bạn vừa push chỉ xuất hiện trên thiết bị có key bạn vừa điền. Là vậy đó!
- Nếu không được cấp quyền thì mình đi xin quyền :)
1 2 3 4 5 6 7 8 9 10
async requestPermission() { try { await firebase.messaging().requestPermission(); // User has authorised this.getToken(); } catch (error) { // User has rejected permissions console.log('quyền bị từ chối'); } }
- Hàm bắt sự kiện (hàm
createNotificationListeners
)- Tạo channel (kể từ bản Android 8.0 trở lên - API 26 bạn phải dùng channel thì notification mới xuất hiện)
1 2 3 4
const channel = new firebase.notifications.Android.Channel('test-channel', 'Test Channel', firebase.notifications.Android.Importance.Max) .setDescription('My apps test channel'); console.log('my chanel id = ', channel); firebase.notifications().android.createChannel(channel);
- Hàm bắt sự kiện chính của chúng ta đây:
firebase.notifications().onNotification
, hàm này bắt sự kiện khi có notification được push đến (và app đang ở trạng thái Foreground) cụ thể ở đây mình cho show alert nội dung notificaion được push đến. Bạn có thể xem tại ĐÂY để biết được các hàm bắt sự kiện khác.1 2 3 4
this.notificationListener = firebase.notifications().onNotification((noti) => { const { title, body } = noti; Alert.alert(title, body); });
- Tạo channel (kể từ bản Android 8.0 trở lên - API 26 bạn phải dùng channel thì notification mới xuất hiện)
Done~! Vậy là chúng ta đã vọc sơ qua FCM và biết cách sử dụng rồi, phát triển nó theo bất kì cách nào bạn thích và xã hội cho phép vì nó free mà ;)
Vấn đề với Head-up Notification
Mình sử dụng cả Android 6.0 và 5.1 thì đều bị lỗi không hiển thị được notification đến theo dạng popup. Mình có tham khảo qua doc của android https://developer.android.com/guide/topics/ui/notifiers/notifications#Heads-up và cũng thử nhiều solution trên Stack Overflow nhưng cũng vô vọng, nếu bạn giải quyết được thì share cách cho mọi người nhé ;)
Nguồn tham khảo: https://rnfirebase.io/docs/v5.x.x/