README.md 9.25 KB
Newer Older
Libin Lu's avatar
Libin Lu committed
1 2
[![Join the chat at https://gitter.im/evollu/react-native-fcm](https://badges.gitter.im/evollu/react-native-fcm.svg)](https://gitter.im/evollu/react-native-fcm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Libin Lu's avatar
Libin Lu committed
3 4 5
## NOTE: 
- If you are running RN < 0.30.0, you need to use react-native-fcm@1.0.15
- If you are running RN < 0.33.0, you need to user react-native-fcm@1.1.0
Libin Lu's avatar
Libin Lu committed
6

Libin Lu's avatar
init  
Libin Lu committed
7 8 9
## Installation

- Run `npm install react-native-fcm --save`
10
- Run `react-native link react-native-fcm` (RN 0.29.1+, otherwise `rnpm link react-native-fcm`)
Libin Lu's avatar
init  
Libin Lu committed
11

12
## Android Configuration
Libin Lu's avatar
init  
Libin Lu committed
13

14 15 16 17 18
- Edit `android/build.gradle`:
```diff
  dependencies {
    classpath 'com.android.tools.build:gradle:2.0.0'
+   classpath 'com.google.gms:google-services:3.0.0'
Libin Lu's avatar
init  
Libin Lu committed
19 20
```

21 22 23 24
- Edit `android/app/build.gradle`:
```diff
  apply plugin: "com.android.application"
+ apply plugin: 'com.google.gms.google-services'
Libin Lu's avatar
init  
Libin Lu committed
25 26
```

27
- Edit `android/app/src/main/AndroidManifest.xml`:
Libin Lu's avatar
init  
Libin Lu committed
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
```diff
  <application
    ...
    android:theme="@style/AppTheme">

+   <service android:name="com.evollu.react.fcm.MessagingService">
+     <intent-filter>
+       <action android:name="com.google.firebase.MESSAGING_EVENT"/>
+     </intent-filter>
+   </service>

+   <service android:name="com.evollu.react.fcm.InstanceIdService" android:exported="false">
+     <intent-filter>
+       <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
+     </intent-filter>
+   </service>

    ...
Libin Lu's avatar
init  
Libin Lu committed
47
```
48

49
### Config for notification and `click_action` in Android
50 51 52 53 54 55 56 57 58 59 60 61 62

To allow android to respond to `click_action`, you need to define Activities and filter on specific intent. Since all javascript is running in MainActivity, you can have MainActivity to handle actions:

Edit `AndroidManifest.xml`:

```diff
  <activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:windowSoftInputMode="adjustResize"
+   android:launchMode="singleTop"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
    <intent-filter>
63 64
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
65 66 67 68 69 70
    </intent-filter>
+   <intent-filter>
+     <action android:name="fcm.ACTION.HELLO" />
+     <category android:name="android.intent.category.DEFAULT" />
+   </intent-filter>
  </activity>
71
```
72 73 74 75 76 77

Notes:
- `launchMode="singleTop"` is to reuse MainActivity
- replace `"fcm.ACTION.HELLO"` by the `click_action` you want to match


78
If you are using RN < 0.30.0 and react-native-fcm < 1.0.16, pass intent into package, edit `MainActivity.java`:
79

Libin Lu's avatar
Libin Lu committed
80
- RN 0.28:
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

```diff
  import com.facebook.react.ReactActivity;
+ import android.content.Intent;

  public class MainActivity extends ReactActivity {

+   @Override
+   public void onNewIntent (Intent intent) {
+     super.onNewIntent(intent);
+       setIntent(intent);
+   }       
```

- RN <= 0.27:

```diff
  import com.facebook.react.ReactActivity;
+ import android.content.Intent;

  public class MainActivity extends ReactActivity {

+   @Override
+   protected void onNewIntent (Intent intent) {
+     super.onNewIntent(intent);
+       setIntent(intent);
+   }       
108
```
Libin Lu's avatar
init  
Libin Lu committed
109

110 111 112
Notes:
- `@Override` is added to update intent on notification click

113
## IOS Configuration
Libin Lu's avatar
init  
Libin Lu committed
114

Libin Lu's avatar
Libin Lu committed
115 116
### Pod approach:

117 118 119
Make sure you have Cocoapods version > 1.0

Install the `Firebase/Messaging` pod:
Libin Lu's avatar
Libin Lu committed
120 121 122 123
```
cd ios && pod init
pod install Firebase/Messaging
```
Libin Lu's avatar
init  
Libin Lu committed
124

Libin Lu's avatar
Libin Lu committed
125
### Non Cocoapod approach
126 127 128

1. Download the Firebase SDK framework from [Integrate without CocoaPods](https://firebase.google.com/docs/ios/setup#frameworks)
2. Follow the `README` to link frameworks (Analytics+Messaging)
Libin Lu's avatar
Libin Lu committed
129 130

### Shared steps
Libin Lu's avatar
init  
Libin Lu committed
131

132 133 134 135 136 137 138 139 140 141 142
Edit `AppDelegate.m`:
```diff
+ #import "Firebase.h" // if you are using Non Cocoapod approach
+ #import "RNFIRMessaging.h"
  //...

  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  {
  //...
+   [FIRApp configure];
  }
Libin Lu's avatar
init  
Libin Lu committed
143

144 145 146 147 148
+ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
+   [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:notification];
+   handler(UIBackgroundFetchResultNewData);
+ }
```
Libin Lu's avatar
init  
Libin Lu committed
149 150

### FCM config file
151

152
In [firebase console](https://console.firebase.google.com/), you can get `google-services.json` file and place it in `android/app` directory and get `GoogleService-Info.plist` file and place it in `/ios/your-project-name` directory (next to your `Info.plist`)
Libin Lu's avatar
init  
Libin Lu committed
153

Libin Lu's avatar
Libin Lu committed
154
## Usage
Libin Lu's avatar
init  
Libin Lu committed
155 156

```javascript
157
import FCM from 'react-native-fcm';
Goran Gajic's avatar
Goran Gajic committed
158

159 160 161
class App extends Component {
  componentDidMount() {
    FCM.requestPermissions(); // for iOS
Goran Gajic's avatar
Goran Gajic committed
162 163 164 165 166 167 168
    FCM.getFCMToken().then(token => {
      console.log(token)
      // store fcm token in your server
    });
    this.notificationUnsubscribe = FCM.on('notification', (notif) => {
      // there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload
    });
169 170
    this.refreshUnsubscribe = FCM.on('refreshToken', (token) => {
      console.log(token)
Goran Gajic's avatar
Goran Gajic committed
171 172
      // fcm token may not be available on first load, catch it here
    });
173

Libin Lu's avatar
Libin Lu committed
174 175
    FCM.subscribeToTopic('/topics/foo-bar');
    FCM.unsubscribeFromTopic('/topics/foo-bar');
Goran Gajic's avatar
Goran Gajic committed
176 177 178
  }

  componentWillUnmount() {
179
    // prevent leaking
Goran Gajic's avatar
Goran Gajic committed
180 181 182
    this.refreshUnsubscribe();
    this.notificationUnsubscribe();
  }
183
}
Libin Lu's avatar
init  
Libin Lu committed
184 185
```

186
### Behaviour when sending `notification` and `data` payload through GCM
187
- When app is not running and user clicks notification, notification data will be passed into `FCM.initialData`
Libin Lu's avatar
Libin Lu committed
188

Libin Lu's avatar
Libin Lu committed
189 190 191 192 193
- When app is running in background (the tricky one, I strongly suggest you try it out yourself)
 - IOS will receive notificaton from `FCMNotificationReceived` event
    * if you pass `content_available` flag true, you will receive one when app is in background and another one when user resume the app. [more info](http://www.rahuljiresal.com/2015/03/retract-push-notifications-on-ios/)
    * if you just pass `notification`, you will only receive one when user resume the app.
    * you will not see banner if `notification->body` is not defined.
194 195 196 197 198 199 200
 - Android will receive notificaton from `FCMNotificationReceived` event
    * if you pass `notification` payload. it will receive data when user click on notification
    * if you pass `data` payload only, it will receive data when in background

   e.g. fcm payload looks like:

   ```json
Libin Lu's avatar
Libin Lu committed
201 202 203 204 205 206 207 208 209 210 211 212 213
   {
      "to":"some_device_token",
      "content_available": true,
      "notification": {
          "title": "hello",
          "body": "yo",
          "click_action": "fcm.ACTION.HELLO"
      },
      "data": {
          "extra":"juice"
      }
    }
    ```
214 215 216 217 218 219 220

    and event callback will receive as:
    
    - Android
      ```json
      {
        "fcm": {"action": "fcm.ACTION.HELLO"},
Libin Lu's avatar
Libin Lu committed
221
        "opened_from_tray": 1,
222 223 224 225 226 227 228 229
        "extra": "juice"
      }
      ```
    
    - iOS
      ```json
      {
        "apns": {"action_category": "fcm.ACTION.HELLO"},
Libin Lu's avatar
Libin Lu committed
230
        "opened_from_tray": 1,
231 232 233
        "extra": "juice"
      }
      ```
234

Libin Lu's avatar
Libin Lu committed
235
- When app is running in foreground
236
 - IOS will receive notification and android **won't** (better not to do anything in foreground for hybrid and send a seprate data message.)
237

238
NOTE: it is recommend not to rely on `data` payload for click_action as it can be overwritten (check [this](http://stackoverflow.com/questions/33738848/handle-multiple-notifications-with-gcm)).
239

Libin Lu's avatar
init  
Libin Lu committed
240
## Q & A
241 242

#### My Android build is failing
Libin Lu's avatar
Libin Lu committed
243
Try update your SDK and google play service
244

245
#### I can't get notification when app is killed
246 247
If you send notification with `data` only, you can only get the data message when app is in foreground or background. Killed app doesn't trigger `FCMNotificationReceived`. Use `notification` in the payload instead.

Libin Lu's avatar
Libin Lu committed
248
#### App running in background doesn't trigger `FCMNotificationReceived` when receiving hybrid notification [Android]
249
These is [an issue opened for that](https://github.com/google/gcm/issues/63). Behavior is not consistent between 2 platforms
250

Libin Lu's avatar
Libin Lu committed
251
#### Android notification is showing a white icon
252 253
Since Lollipop, the push notification icon is required to be all white, otherwise it will be a white circle.

Libin Lu's avatar
Libin Lu committed
254 255 256
#### iOS not receiving notification when the app running in the background
- Try adding Background Modes permission in Xcode->Click on project file->Capabilities tab->Background Modes->Remote Notifications

257 258
#### I am using Proguard
You need to add this to your `android/app/proguard-rules.pro`:
Libin Lu's avatar
Libin Lu committed
259 260 261 262

#### How do I tell if user clicks the notification banner?
- Check open from tray flag in notification. It will be either 0 or 1 for iOS and undefined or 1 for android. I decide for iOS base on [this](http://stackoverflow.com/questions/20569201/remote-notification-method-called-twice), and for android I set it if notification is triggered by intent change.
 
263 264 265 266 267
```
# Google Play Services
-keep class com.google.android.gms.** { *; }
-dontwarn com.google.android.gms.**
```
Libin Lu's avatar
init  
Libin Lu committed
268

Libin Lu's avatar
Libin Lu committed
269
#### Android notification doesn't vibrate/show head-up display etc
270
All available features are [here](https://firebase.google.com/docs/cloud-messaging/http-server-ref#notification-payload-support). FCM may add more support in the future but there is no timeline. If you need these features now, send notification with `data` only and creating notification locally is the only way
Libin Lu's avatar
Libin Lu committed
271

272 273
#### Some features are missing
Issues and pull requests are welcome. Let's make this thing better!