README.md 9.17 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
## NOTE: If you are running RN < 0.30.0, you need to use react-native-fcm@1.0.15

Libin Lu's avatar
init  
Libin Lu committed
5 6 7
## Installation

- Run `npm install react-native-fcm --save`
8
- 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
9

10
## Android Configuration
Libin Lu's avatar
init  
Libin Lu committed
11

12 13 14 15 16
- 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
17 18
```

19 20 21 22
- 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
23 24
```

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

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
```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
45
```
46

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

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>
61 62
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
63 64 65 66 67 68
    </intent-filter>
+   <intent-filter>
+     <action android:name="fcm.ACTION.HELLO" />
+     <category android:name="android.intent.category.DEFAULT" />
+   </intent-filter>
  </activity>
69
```
70 71 72 73 74 75

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


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

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

```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);
+   }       
106
```
Libin Lu's avatar
init  
Libin Lu committed
107

108 109 110
Notes:
- `@Override` is added to update intent on notification click

111
## IOS Configuration
Libin Lu's avatar
init  
Libin Lu committed
112

Libin Lu's avatar
Libin Lu committed
113 114
### Pod approach:

115 116 117
Make sure you have Cocoapods version > 1.0

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

Libin Lu's avatar
Libin Lu committed
123
### Non Cocoapod approach
124 125 126

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
127 128

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

130 131 132 133 134 135 136 137 138 139 140
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
141

142 143 144 145 146
+ - (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
147 148

### FCM config file
149

150
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
151

Libin Lu's avatar
Libin Lu committed
152
## Usage
Libin Lu's avatar
init  
Libin Lu committed
153 154

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

157 158 159
class App extends Component {
  componentDidMount() {
    FCM.requestPermissions(); // for iOS
Goran Gajic's avatar
Goran Gajic committed
160 161 162 163 164 165 166
    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
    });
167 168
    this.refreshUnsubscribe = FCM.on('refreshToken', (token) => {
      console.log(token)
Goran Gajic's avatar
Goran Gajic committed
169 170
      // fcm token may not be available on first load, catch it here
    });
171

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

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

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

Libin Lu's avatar
Libin Lu committed
187 188 189 190 191
- 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.
192 193 194 195 196 197 198
 - 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
199 200 201 202 203 204 205 206 207 208 209 210 211
   {
      "to":"some_device_token",
      "content_available": true,
      "notification": {
          "title": "hello",
          "body": "yo",
          "click_action": "fcm.ACTION.HELLO"
      },
      "data": {
          "extra":"juice"
      }
    }
    ```
212 213 214 215 216 217 218

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

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

236
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)).
237

Libin Lu's avatar
init  
Libin Lu committed
238
## Q & A
239 240

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

243
#### I can't get notification when app is killed
244 245
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
246
#### App running in background doesn't trigger `FCMNotificationReceived` when receiving hybrid notification [Android]
247
These is [an issue opened for that](https://github.com/google/gcm/issues/63). Behavior is not consistent between 2 platforms
248

Libin Lu's avatar
Libin Lu committed
249
#### Android notification is showing a white icon
250 251
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
252 253 254
#### 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

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

#### 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.
 
261 262 263 264 265
```
# Google Play Services
-keep class com.google.android.gms.** { *; }
-dontwarn com.google.android.gms.**
```
Libin Lu's avatar
init  
Libin Lu committed
266

Libin Lu's avatar
Libin Lu committed
267
#### Android notification doesn't vibrate/show head-up display etc
268
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
269

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