Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Commit aeae486

Browse files
Allow using this plugin as a standalone 'push notification client' - without loading the Firebase SDK #909
1 parent 7f36b60 commit aeae486

File tree

11 files changed

+314
-63
lines changed

11 files changed

+314
-63
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
* [Firestore](docs/FIRESTORE.md)
2020
* [Authentication](docs/AUTHENTICATION.md)
2121
* [Remote Config](docs/REMOTECONFIG.md)
22-
* [Cloud Messaging](docs/MESSAGING.md)
22+
* [Firebase Cloud Messaging](docs/MESSAGING.md)
23+
* [Non-Firebase Push Messaging](docs/NON_FIREBASE_MESSAGING.md) 🆕
2324
* [Storage](docs/STORAGE.md)
2425
* [Crash Reporting / Crashlytics](docs/CRASHREPORTING.md)
2526
* [Analytics](docs/ANALYTICS.md)
@@ -53,6 +54,8 @@ tns plugin add nativescript-plugin-firebase
5354
This will launch an install script which will guide you through installing additional components.
5455
Check the doc links above to see what's what. You can always change your choices later.
5556

57+
> Want to use this plugin with an *external push notification provider* and **not** use any Firebase feature? Just answer 'y' to the first question to skip most of them, and️ [hop on over to the Push Notification](docs/NON_FIREBASE_MESSAGING.md).
58+
5659
> Using [NativeScript SideKick](https://www.nativescript.org/nativescript-sidekick)? Then the aforementioned install script
5760
will not (be able to) run. In that case, running the app for Android will result in [this issue](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/issues/829#issuecomment-409870671).
5861
To fix that, see [this comment](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/issues/829#issuecomment-409855611).

demo-push/app/push-view-model.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Observable } from "tns-core-modules/data/observable";
2-
import * as firebase from "nativescript-plugin-firebase";
3-
import { messaging } from "nativescript-plugin-firebase/messaging";
2+
import { messaging, Message } from "nativescript-plugin-firebase/messaging";
43
import { alert, confirm } from "tns-core-modules/ui/dialogs";
54
import * as platform from "tns-core-modules/platform";
65
import * as applicationSettings from "tns-core-modules/application-settings";
@@ -38,13 +37,13 @@ export class PushViewModel extends Observable {
3837
}).then(pushAllowed => {
3938
if (pushAllowed) {
4039
applicationSettings.setBoolean(PushViewModel.APP_REGISTERED_FOR_NOTIFICATIONS, true);
41-
this.doRegisterPushHandlers();
40+
this.doRegisterForPushNotifications();
4241
}
4342
});
4443
}
4544

4645
public doGetCurrentPushToken(): void {
47-
firebase.getCurrentPushToken()
46+
messaging.getCurrentPushToken()
4847
.then(token => {
4948
// may be null/undefined if not known yet
5049
alert({
@@ -60,6 +59,7 @@ export class PushViewModel extends Observable {
6059
if (!platform.isIOS) {
6160
console.log("##### Interactive push messaging is currently iOS-only!");
6261
console.log("##### Also, please make sure you don't include the 'click_action' notification property when pusing to Android.");
62+
return;
6363
}
6464

6565
const model = new messaging.PushNotificationModel();
@@ -104,7 +104,7 @@ export class PushViewModel extends Observable {
104104
identifier: "GENERAL"
105105
}];
106106

107-
model.onNotificationActionTakenCallback = (actionIdentifier: string, message: firebase.Message, inputText?: string) => {
107+
model.onNotificationActionTakenCallback = (actionIdentifier: string, message: Message, inputText?: string) => {
108108
console.log(`onNotificationActionTakenCallback fired! \n\r Message: ${JSON.stringify(message)}, \n\r Action taken: ${actionIdentifier}`);
109109

110110
alert({
@@ -114,7 +114,7 @@ export class PushViewModel extends Observable {
114114
});
115115
};
116116

117-
firebase.registerForInteractivePush(model);
117+
messaging.registerForInteractivePush(model);
118118

119119
console.log("Registered for interactive push");
120120
alert({
@@ -123,11 +123,11 @@ export class PushViewModel extends Observable {
123123
});
124124
}
125125

126-
// You would normally add these handlers in 'init', but if you want you can do it seperately as well.
126+
// You could add these handlers in 'init', but if you want you can do it seperately as well.
127127
// The benefit being your user will not be confronted with the "Allow notifications" consent popup when 'init' runs.
128128
public doRegisterPushHandlers(): void {
129129
// note that this will implicitly register for push notifications, so there's no need to call 'registerForPushNotifications'
130-
firebase.addOnPushTokenReceivedCallback(
130+
messaging.addOnPushTokenReceivedCallback(
131131
token => {
132132
// you can use this token to send to your own backend server,
133133
// so you can send notifications to this specific device
@@ -136,7 +136,7 @@ export class PushViewModel extends Observable {
136136
// pasteboard.setValueForPasteboardType(token, kUTTypePlainText);
137137
}
138138
);
139-
firebase.addOnMessageReceivedCallback(
139+
messaging.addOnMessageReceivedCallback(
140140
message => {
141141
console.log("Push message received in push-view-model: " + JSON.stringify(message, getCircularReplacer()));
142142

@@ -156,7 +156,7 @@ export class PushViewModel extends Observable {
156156
}
157157

158158
public doUnregisterForPushNotifications(): void {
159-
firebase.unregisterForPushNotifications().then(
159+
messaging.unregisterForPushNotifications().then(
160160
() => {
161161
alert({
162162
title: "Unregistered",
@@ -166,19 +166,34 @@ export class PushViewModel extends Observable {
166166
});
167167
}
168168

169-
public doRegisterForPushNotificationsAgain(): void {
170-
firebase.registerForPushNotifications().then(
171-
() => {
169+
public doRegisterForPushNotifications(): void {
170+
messaging.registerForPushNotifications({
171+
onPushTokenReceivedCallback: (token: string): void => {
172+
console.log("Firebase plugin received a push token: " + token);
173+
},
174+
175+
onMessageReceivedCallback: (message: Message) => {
176+
console.log("Push message received in push-view-model: " + JSON.stringify(message, getCircularReplacer()));
177+
178+
setTimeout(() => {
172179
alert({
173-
title: "Registered again",
174-
message: "You should now use the new push token which was received in 'addOnPushTokenReceivedCallback, or call 'getCurrentPushToken'.",
175-
okButtonText: "Got it."
180+
title: "Push message!",
181+
message: (message !== undefined && message.title !== undefined ? message.title : ""),
182+
okButtonText: "Sw33t"
176183
});
177-
});
184+
}, 500);
185+
},
186+
187+
// Whether you want this plugin to automatically display the notifications or just notify the callback. Currently used on iOS only. Default true.
188+
showNotifications: true,
189+
190+
// Whether you want this plugin to always handle the notifications when the app is in foreground. Currently used on iOS only. Default false.
191+
showNotificationsWhenInForeground: true
192+
}).then(() => console.log("Registered for push"));
178193
}
179194

180195
public doSubscribeToTopic(): void {
181-
firebase.subscribeToTopic("demo").then(
196+
messaging.subscribeToTopic("demo").then(
182197
() => {
183198
alert({
184199
title: "Subscribed",
@@ -197,7 +212,7 @@ export class PushViewModel extends Observable {
197212
}
198213

199214
public doUnsubscribeFromTopic(): void {
200-
firebase.unsubscribeFromTopic("demo").then(
215+
messaging.unsubscribeFromTopic("demo").then(
201216
() => {
202217
alert({
203218
title: "Unsubscribed",
@@ -218,7 +233,7 @@ export class PushViewModel extends Observable {
218233
public doGetAreNotificationsEnabled(): void {
219234
alert({
220235
title: "AreNotificationsEnabled",
221-
message: "" + firebase.areNotificationsEnabled(),
236+
message: "" + messaging.areNotificationsEnabled(),
222237
okButtonText: "Okay, very interesting"
223238
});
224239
}

docs/NON_FIREBASE_MESSAGING.md

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
If you read this, chances are you want Push Notifications, but don't want to use Firebase Cloud Messaging as a push provider.
2+
3+
You'll be happy to learn this plugin has a 'lite' mode that won't add any native Firebase dependencies (or 'Pod' libraries) on iOS, and only the bare necessities on Android (on Android Push Messaging will always use FCM, regardless the push service).
4+
5+
Go to you app's root folder and remove `firebase.nativescript.json`, then `npm i`. You will now be prompted `Are you using this plugin ONLY as a Push Notification client for an external (non-Firebase) Push service? (y/n)`. Answer:
6+
- `y` if you don't want to use any of the Firebase features (Firestore, Realtime DB, Storage, etc), or
7+
- `n` in case you do want to use some of the features (you will be asked which features later).
8+
9+
The remainder of this document applies to both situations, so please continue reading.
10+
11+
## Demo app
12+
I've tried applying best practices to a [dedicated push-only demo app](/demo-push).
13+
14+
Two important things to keep in mind are:
15+
- `require` (not `import`!) the plugin in [app/app.ts](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/e18e546ac1b96fea1d7ce71c5ae3453a8955cc17/demo-push/app/app.ts#L5).
16+
- Show [your own consent screen](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/e18e546ac1b96fea1d7ce71c5ae3453a8955cc17/demo-push/app/push-view-model.ts#L33-L43) before iOS requests permission, because a) the default popup (that'll also still be shown) isn't very friendly/configurable, and b) once the user denies permission they have to go to the settings app as you app can only request permission once.
17+
18+
## Setup
19+
20+
### Android
21+
No additional setup required.
22+
23+
There is a little quirk: you will currently not get the title and body if the notification was received while the application was in the background, but you will get the *data* payload.
24+
25+
### iOS
26+
27+
#### Enable push support in Xcode
28+
29+
Open /platforms/ios/yourproject.__xcworkspace__ (!) and go to your project's target and head over to "Capabilities" to switch this on (if it isn't already):
30+
<img src="images/push-xcode-config.png" width="600px" alt="Push Xcode config"/>
31+
32+
> Without this enabled you will receive push messages in the foreground, but **NOT in the background** / when the app is killed.
33+
34+
#### Copy the entitlements file
35+
The previous step created a the file`platforms/ios/YourAppName/(Resources/)YourAppName.entitlements`.
36+
Copy that file to `app/App_Resources/iOS/` (if it doesn't exist yet, otherwise merge its contents),
37+
so it's not removed when you remove and re-add the iOS platform. The relevant content for background push in that file is:
38+
39+
```xml
40+
<key>aps-environment</key>
41+
<string>development</string>
42+
```
43+
44+
> Note that the filename can either be `<YourAppName>.entitlements` or `app.entitlements`, where `YourAppName` is the iOS foldername, see the path above.
45+
46+
#### Allow processing when a background push is received
47+
Open `app/App_Resources/iOS/Info.plist` and add this to the bottom:
48+
49+
```xml
50+
<key>UIBackgroundModes</key>
51+
<array>
52+
<string>remote-notification</string>
53+
</array>
54+
```
55+
56+
## API
57+
58+
### `areNotificationsEnabled`
59+
On both iOS and Android the user can disable notifications for your app.
60+
If you want to check the current state of this setting, you can do:
61+
62+
```typescript
63+
import { messaging, Message } from "nativescript-plugin-firebase/messaging";
64+
65+
console.log(`Notifications enabled? ${messaging.areNotificationsEnabled()}`);
66+
```
67+
68+
### `registerForPushNotifications`
69+
The easiest way to register for (receiving) push notifications is calling `registerForPushNotifications`, and passing in a few handlers:
70+
71+
```typescript
72+
import { messaging, Message } from "nativescript-plugin-firebase/messaging";
73+
74+
messaging.registerForPushNotifications({
75+
onPushTokenReceivedCallback: (token: string): void => {
76+
console.log("Firebase plugin received a push token: " + token);
77+
},
78+
79+
onMessageReceivedCallback: (message: Message) => {
80+
console.log("Push message received: " + message.title);
81+
},
82+
83+
// Whether you want this plugin to automatically display the notifications or just notify the callback. Currently used on iOS only. Default true.
84+
showNotifications: true,
85+
86+
// Whether you want this plugin to always handle the notifications when the app is in foreground. Currently used on iOS only. Default false.
87+
showNotificationsWhenInForeground: true
88+
}).then(() => console.log("Registered for push"));
89+
```
90+
91+
> Any pending notifications (while your app was not in the foreground) will trigger the `onMessageReceivedCallback` handler.
92+
93+
> With the `token` received in `onPushTokenReceivedCallback` you can send a notification to this device.
94+
95+
96+
### `getCurrentPushToken`
97+
If - for some reason - you need to manually retrieve the current push registration token of the device, you can do:
98+
99+
```typescript
100+
import { messaging } from "nativescript-plugin-firebase/messaging";
101+
102+
messaging.getCurrentPushToken()
103+
.then(token => console.log(`Current push token: ${token}`));
104+
```
105+
106+
### Interactive notifications (iOS only for now)
107+
To register the app to receive interactive pushes you need to call `messaging.registerForInteractivePush(model)`.
108+
And you may hook to the `model.onNotificationActionTakenCallback` callback to know what action the user took interacting with the notification.
109+
110+
Each action has either type `button` or `input`, and you can set `options` to do any or all of:
111+
- Launch the app: `foreground`.
112+
- Only allow the action when the device is unlocked: `authenticationRequired`.
113+
- Make the text red to indicate something will be removed/deleted/killed: `destructive`.
114+
115+
Consider this example, where an interactive push notification is received which the user expands and picks the fourth option.
116+
He then types his reply, and (because of how the action was configured) the app launches and captures the reply.
117+
118+
<img src="https://raw.githubusercontent.com/EddyVerbruggen/nativescript-plugin-firebase/master/docs/images/messaging/interactive01.png" height="270px" alt="Interactive Notification, part 1"/> <img src="https://raw.githubusercontent.com/EddyVerbruggen/nativescript-plugin-firebase/master/docs/images/messaging/interactive02.png" height="270px" alt="Interactive Notification, part 2"/> <img src="https://raw.githubusercontent.com/EddyVerbruggen/nativescript-plugin-firebase/master/docs/images/messaging/interactive03.png" height="270px" alt="Interactive Notification, part 3"/> <img src="https://raw.githubusercontent.com/EddyVerbruggen/nativescript-plugin-firebase/master/docs/images/messaging/interactive04.png" height="270px" alt="Interactive Notification, part 4"/>
119+
120+
```typescript
121+
import { messaging, Message } from "nativescript-plugin-firebase/messaging";
122+
123+
const model = new messaging.PushNotificationModel();
124+
model.iosSettings = new messaging.IosPushSettings();
125+
model.iosSettings.badge = false;
126+
model.iosSettings.alert = true;
127+
128+
model.iosSettings.interactiveSettings = new messaging.IosInteractivePushSettings();
129+
model.iosSettings.interactiveSettings.actions = [
130+
{
131+
identifier: "OPEN_ACTION",
132+
title: "Open the app (if closed)",
133+
options: messaging.IosInteractiveNotificationActionOptions.foreground
134+
},
135+
{
136+
identifier: "AUTH",
137+
title: "Open the app, but only if device is not locked with a passcode",
138+
options: messaging.IosInteractiveNotificationActionOptions.foreground | messaging.IosInteractiveNotificationActionOptions.authenticationRequired
139+
},
140+
{
141+
identifier: "INPUT_ACTION",
142+
title: "Tap to reply without opening the app",
143+
type: "input",
144+
submitLabel: "Fire!",
145+
placeholder: "Load the gun..."
146+
},
147+
{
148+
identifier: "INPUT_ACTION",
149+
title: "Tap to reply and open the app",
150+
options: messaging.IosInteractiveNotificationActionOptions.foreground,
151+
type: "input",
152+
submitLabel: "OK, send it",
153+
placeholder: "Type here, baby!"
154+
},
155+
{
156+
identifier: "DELETE_ACTION",
157+
title: "Delete without opening the app",
158+
options: messaging.IosInteractiveNotificationActionOptions.destructive
159+
}
160+
];
161+
162+
model.iosSettings.interactiveSettings.categories = [{
163+
identifier: "GENERAL"
164+
}];
165+
166+
model.onNotificationActionTakenCallback = (actionIdentifier: string, message: Message) => {
167+
console.log(`onNotificationActionTakenCallback fired! Message: ${JSON.stringify(message)}, Action taken: ${actionIdentifier}`);
168+
};
169+
170+
messaging.registerForInteractivePush(model);
171+
```
172+
173+
To send an interactive push, add the `"click_action"` property to the notification, with a value corresponding to the `category` defined in the model you've registered in the app.
174+
The payload to trigger the notification in the screenshots above is:
175+
176+
```json
177+
{
178+
"notification": {
179+
"title": "I DEMAND YOUR ATTENTION",
180+
"subtitle": "Just kidding, but not really",
181+
"text": "Sorry to bother you I meant, please pick an option below..",
182+
"click_action": "GENERAL",
183+
"badge": "1",
184+
"sound": "default",
185+
"showWhenInForeground": true // this can go either here..
186+
},
187+
"showWhenInForeground": true, // .. or here
188+
"content_available": false,
189+
"data": {
190+
"foo": "bar"
191+
},
192+
"priority": "High",
193+
"to": "DEVICE_PUSH_KEY>"
194+
}
195+
```
196+
197+
> *IMPORTANT* Use the `click_action` only for push notifications on iOS. When such a message is tapped in the Android notification center the app WON'T be opened. This will probably be fixed in the future.
198+
199+
200+
## Testing push notifications
201+
202+
### iOS
203+
For testing notifications on iOS the easiest tool I found is [Pusher](https://github.com/noodlewerk/NWPusher):
204+
205+
<img src="images/messaging/pusher.png" width="712px" alt="Pusher"/>
206+
207+
### Android
208+
For testing on Android I prefer using [Postman](https://www.getpostman.com/). Look at which headers you need to set, and how the payload needs to be added:
209+
210+
<img src="images/messaging/postman-headers.png" width="480px" alt="Postman headers"/> <img src="images/messaging/postman-body.png" width="480px" alt="Postman body"/>
53.5 KB
Loading
195 KB
Loading

docs/images/messaging/pusher.png

364 KB
Loading

0 commit comments

Comments
 (0)