MobileFirst and Ionic Push notification callback - ibm-mobilefirst

I have an Ionic project and I added a push notification callback handler as explained in official documentation (my expectation is to have some alert when inside the app and a notification arrive), but it is never invoked.
WL.Client.Push.registerEventSourceCallback(
"myPush",
"PushAdapter",
"PushEventSource",
pushNotificationReceived);
Here the question is where I have to place the pushNotificationReceived function to be called ? I try in index.js and inside the controller as a function, but I do not have success.
Thank for help

Have you reviewed the sample application for push notifications?.
The sample demonstrates implementation of push notifications APIs. https://developer.ibm.com/mobilefirstplatform/documentation/getting-started-7-1/foundation/notifications/push-notifications-overview/push-notifications-in-hybrid-applications/event-source-notifications-in-hybrid-applications/
See the supplied main.js file: https://github.com/MobileFirst-Platform-Developer-Center/EventSourceNotifications/blob/release71/apps/HybridEventSource/common/js/main.js

You have to include callback function in ionic inside the app.js file after "Successfully connected with mobile first server".
WLAuthorizationManager.obtainAccessToken().then(
function (response) {
//alert("successfully obtained a token from the server");
MFPPush.initialize (
function(successResponse) {
//alert("Successfully intialized");
MFPPush.registerNotificationsCallback(notificationReceived);
},
function(failureResponse) {
console.log("Failed to initialize");
}
);
MFPPush.isPushSupported (
function(successResponse) {
//alert("Push Supported: " + successResponse);
},
function(failureResponse) {
console.log("Failed to get push support status");
}
);
MFPPush.registerDevice(
function(successResponse) {
//alert("Successfully registered");
},
function(failureResponse) {
console.log("Failed to register");
}
);
function notificationReceived(message) {
alert(JSON.stringify(message.alert));
}
},
function(response) {
//alert("res"+JSON.stringify(response));
console.log("Unable to obtain a token from the server: " + JSON.stringify(response));
if(JSON.stringify(response.status) == 403){
console.log("The Application is disabled on this Device");
ionic.Platform.exitApp();
}
}
);

Related

Share a URL as post in Facebook App from React-Native App

I have a React-native app, from which I want share my website's URL in a Facebook post.
My current code is :
import Share from "react-native-share";
const shareToFacebook = async () => {
const shareOptions = {
social: Share.Social.FACEBOOK,
message: "Test message",
url: "https://example.com/",
};
try {
const ShareResponse = await Share.shareSingle(shareOptions);
console.log(ShareResponse);
} catch (error) {
console.log("Error =>", error);
}
};
but this solution ,opens Facebook in the browser. Is there any way to open the actual Facebook app ?
Facebook offers an SDK for sharing posts, you can use that SDK with react-native-fbsdk-next library. It shows an in-app modal. I think showing an in-app modal is better than the open the Facebook app or open a browser because some people do not have Facebook on their phones.
You can test the share function before installing the library, follow below steps:
# clone the react-native-fbsdk-next
git clone https://github.com/thebergamo/react-native-fbsdk-next.git
# go to the test project
cd RNFBSDKExample
# install dependencies
npm install
# run the project
npm run android
Example code (from README page):
// ...
import { ShareDialog } from 'react-native-fbsdk-next';
// ...
// Build up a shareable link.
const shareLinkContent = {
contentType: 'link',
contentUrl: "https://facebook.com",
contentDescription: 'Wow, check out this great site!',
};
// ...
// Share the link using the share dialog.
shareLinkWithShareDialog() {
var tmp = this;
ShareDialog.canShow(this.state.shareLinkContent).then(
function(canShow) {
if (canShow) {
return ShareDialog.show(tmp.state.shareLinkContent);
}
}
).then(
function(result) {
if (result.isCancelled) {
console.log('Share cancelled');
} else {
console.log('Share success with postId: '
+ result.postId);
}
},
function(error) {
console.log('Share fail with error: ' + error);
}
);
}
Read more about Facebook Sharing Feature.

Set up listeners only after permission is verified

I am using react-native-notifications - Version 2.1.7 library to receive notifications in a react-native mobile app. I don't want to set up the notification-related listeners until the user has provided permission to receive notifications.
Q1. The documentation says that it is highly recommended to keep listeners registration at global scope rather than at screen scope. What problems should I expect if I set up listeners at a screen, at which the user is asked to provide permission?
Q2. The device token listener NotificationsAndroid.setRegistrationTokenUpdateListener() does NOT seem to work if it is inside a promise. What am I missing here? Please, see my code below.
// This function is called when the user clicks on the button "Provide permission to receive notifications."
const _requestPermissionNotification = async () => {
let hasPermission = false;
try {
hasPermission = await NotificationsAndroid.isRegisteredForRemoteNotifications();
}
catch (error) {
Alert.alert(
"Notification",
"To utilise the full functionality of this app, Permission to receive notifications is required.",
[{ text: "Ok." }]
);
} // end of: try/catch
if (hasPermission) {
// A. Register Token
// THIS LISTENER DOES NOT SEEM TO WORK UNLESS IT IS SET UP OUTSIDE THE COMPONENT!
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
console.log("PermissionsScreen - setRegistrationTokenUpdateListener - deviceToken:", deviceToken);
});
// B. Pending Notifications
PendingNotifications.getInitialNotification()
.then((notification) => {
console.log("PermissionsScreen - getInitialNotification - notification:", notification);
})
.catch((err) => console.error("getInitialNotifiation failed", err));
// C. Notification Opened
NotificationsAndroid.setNotificationOpenedListener((notification) => {
console.log("PermissionsScreen - setNotificationOpenedListener - :data", notification.getData());
});
// D.a Notification Received
NotificationsAndroid.setNotificationReceivedListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedListener - data:", notification.getData());
});
// D.b Notification Received "IN FOREGROUND"
NotificationsAndroid.setNotificationReceivedInForegroundListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedInForegroundListener (foreground)", notification.getData());
});
} // end of: if()
}; // end of: _requestPermissionNotification()
It seems that version 3.1.1 of React-Native-Notifications does not have these limitations anymore.
The following code, which uses the new commands, can be used inside a promise and inside a component.
// Step A.1: Register this app to receive notifications.
Notifications.registerRemoteNotifications();
// Step A.2: Get the device token
Notifications.events().registerRemoteNotificationsRegistered( (event) => {
console.log("deviceToken:", event.deviceToken);
});

React-Native: Can't receive expo push notification on stand alone app

This is ss from standalone app, its gettings expo token but for some reason it wont show notifications
I can't receive push notification on my standalone apps and if someone else used the app from expo (not my phone) then he won't receive it either,
For some reason only i receive it ..that too on expo client and if i install the apk on my phone, then i get the error....
In these all situations the error is same,
{
"data": {
"status": "error",
"message": "SNS failed to send the notification (reason: EndpointDisabled, status code: 400).",
"details": {
"error": "DeviceNotRegistered",
"sns": {
"statusCode": 400,
"reason": "EndpointDisabled",
"__message": "Endpoint is disabled"
}
}
}
}
my notification js
import { Permissions, Notifications } from 'expo';
import { AsyncStorage } from 'react-native';
import axios from 'axios';
import {
IUSTCONNECT_URL
} from '../actions/types';
const server = IUSTCONNECT_URL;
export default async function registerForPushNotificationsAsync() {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
// only ask if permissions have not already been determined, because
// iOS won't necessarily prompt the user a second time.
if (existingStatus !== 'granted') {
// Android remote notification permissions are granted during the app
// install, so this will only ask on iOS
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
// Stop here if the user did not grant permissions
if (finalStatus !== 'granted') {
return;
}
// Get the token that uniquely identifies this device
let token = await Notifications.getExpoPushTokenAsync();
console.log(token);
await AsyncStorage.setItem('notificationToken', token);
const adminFlag = await AsyncStorage.getItem('admin');
try {
const { data } = await axios.post(`${server}/admin/app_bridge/user.php`, {
job: 'updateExpoToken',
admin: adminFlag,
token: token
});
if(data.trim() === 'success') {
console.log('expo push notification token sent:');
} else {
console.log('error sending notification token:');
console.log(data);
}
} catch (e) {
console.log(e);
}
}
and i am calling registerForPushNotificationsAsync() on my child tab,
Everything works well for my expo app...rest it doesn't...
Any help?
The issue is with the device. you can see in error DeviceNotRegistered.
see expo docs here.
DeviceNotRegistered: the device cannot receive push notifications
anymore and you should stop sending messages to the given Expo push
token.
The problem is that in docs no information why this error is happening. Try with some other device's.
if you develop with firebase you need update your token, the steps are:
1 in your page of project firebase, select option setting.
2 navigate to option project settings.
3 select option cloud messasging.
4 on credetial of project copy the token.
5. in your terminal in root directory of project type:
expo push:android:upload --api-key <your token>
for more information visit
enter link description here
try send push notification Manually
enter link description here
i'm working with sdk 37.0.0

Ionic 3's PWA & Firebase Cloud Messaging registration

I was following this article here (which is not complete unfortunately) in attempt to learn how to friend Ionic 3 based PWA and Firebase Cloud Messaging: Push Notifications with FCM
What I did:
as advised in the article added FCM libraries into service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': '47286327412'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler((payload) => {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the service sending messages
const notificationOptions = {
icon: '/assets/img/appicon.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/vendor.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
Then created Firebase Messaging based provider here:
import { Injectable } from "#angular/core";
import * as firebase from 'firebase';
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging: firebase.messaging.Messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage
) {
this.messaging = firebase.messaging();
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):
A bad HTTP response code (404) was received when fetching the script.
:8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE
If I move service-worker.js firebase related stuff into default service worker in WWW folder - I get general error from Firebase (Error, failed to register service worker).
QUESTIONS:
- is there a fresh guide on Ionic 3's PWA & FCM?
- at high level what is the difference in registering service workers in Ionic 3 vs Angular? I did watch the tutorial about Angular but can't figure how to do the same in Ionic 3.
UPDATE: the below is valid as of today (02/12/2018) and most likely will be less relevant once AngularFire2 supports messaging module. So take the below with that assumption...
OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:
Prerequisites:
I created ionic blank app (just a home page)
installed angularfire2 and firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1") using npm install, I used specifically 5.0.0-rc.4" cause I had stability issues with latest one;(
created config (filename environment.ts in src folder):
export const firebaseConfig = {
apiKey: "Your Stuff Here from FB",
authDomain: "YOURAPPNAME.firebaseapp.com",
databaseURL: "https://YOURAPPNAME.firebaseio.com",
projectId: "YOURAPPNAME",
storageBucket: "YOURAPPNAME.appspot.com",
messagingSenderId: "FROMFIREBASECONEOLE"
};
I modified app.module.ts to add firebase and angularfire2 this way:
...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
FirebaseMessagingProvider,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
Here we also import our provider whose code is below:
in providers folder I created firebase-messaging.ts like this:
import { Injectable } from "#angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage,
private app: FirebaseApp
) {
this.messaging = app.messaging();
navigator.serviceWorker.register('service-worker.js').then((registration) => {
this.messaging.useServiceWorker(registration);
//this.disableNotifications()
this.enableNotifications();
});
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
console.log(currentToken)
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
Please note I init the firebase app and then in constructor we register ionic's default service worker (service-worker.js) that contains the following right after whatever is there by default:
service-worker.js:
// firebase messaging part:
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the Webtask
const notificationOptions = {
icon: '/assets/images/logo-128.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
At this point you also need to make sure you enabled your app as PWA, there is a good guide from Josh Morony and today there was a video stream on youtube that covers it. In TLDR you need to uncomment this in your index.html:
index.html in src uncomment:
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
OK almost the last thing - your manifest.json (in src) should have exact line:
"gcm_sender_id": "103953800507"
This concludes initial stuff on the client. Please note I didn't implement yet anything to handle notifications while user is in app itself, think for now it just handles when a message is sent from a server while your tab is not in focus (that is what I tested).
Now you want to go to your firebase console and obtain server key (click setting gear icon, then see cloud messaging section there). Copy server key. Also run the client (ionic serve and capture your local token (i just console.logged it). Now try sending yourself the message using a POST method. ( I did it with Postman)
// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
// get the key from Firebase console
headers: { Authorization: `key=${fcmServerKey}` },
json: {
"notification": {
"title": "Message title",
"body": "Message body",
"click_action": "URL to your app?"
},
// userData is where your client stored the FCM token for the given user
// it should be read from the database
"to": userData.fcmRegistrationKey
}
So by doing all this I was able to reliable send myself a message WHILE the app was in background. I am yet to handle foreground but this SO question is about how to init default service worker and marry it with FCM.
I hope this will help some learners in future.
I have successfully implemented the process and got success response on API calls. But no notification popup coming on my browser. Any idea?
api: https://fcm.googleapis.com/fcm/send
response got:
{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]}
cheth the attached url of my console:

IOS Expo Push Notifications when app is in foreground

reading from the expo docs:
For iOS, you would be wise to handle push notifications that are
received while the app is foregrounded, because otherwise the user
will never see them. Notifications that arrive while the app are
foregrounded on iOS do not show up in the system notification list. A
common solution is to just show the notification manually. For
example, if you get a message on Messenger for iOS, have the app
foregrounded, but do not have that conversation open, you will see the
notification slide down from the top of the screen with a custom
notification UI.
What I don't understand is what is the best approach for that? is there an Expo API for showing such messages? or should I create an alert component of my own? It is not really clear from the docs.
Thanks.
This answer is outdated as of February 20, 2020. Please see https://stackoverflow.com/a/60344280/2441420 for how to show iOS Notification when your application is in the Foreground
There isn't an Expo API for showing those messages. You can use any 'toast' library of your choosing and display the notification message, but that should be all your code.
For example, this is how we are doing right now:
export default class HomeScreen extends React.Component {
componentDidMount() {
this.notificationSubscription = Notifications.addListener(
(notification) => this.handlePushNotification(notification),
);
}
handlePushNotification(notification) {
const { navigation } = this.props;
PushNotificationsService.handleNotification(notification, navigation);
}
(...)
import Toast from 'react-native-root-toast';
export default class PushNotificationsService {
static handleNotification(notification, navigation) {
if (notification.data.screen && notification.origin === 'selected') {
navigation.navigate(notification.data.screen);
}
Toast.show(notification.data.message);
}
}
Toast libraries include:
react-native-root-toast
react-native-easy-toast
react-native-simple-toast
Now you can just add that in one of your app entry point. The shouldShowAlert is what you want here
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
App.json :
{
"expo": {
"notification": {
"iosDisplayInForeground": true
}
}
DEMO
I'm not sure exactly when this was added to Expo, but as of Expo version 36 is easily doable.
To show Expo Push Notifications on iOS when your app is in the foreground, please do the following:
import { Vibration } from "react-native";
import { Notifications } from "expo";
import * as Permissions from "expo-permissions";
import Constants from "expo-constants";
registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(
Permissions.NOTIFICATIONS
);
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
let token = await Notifications.getExpoPushTokenAsync();
console.log("Go to https://expo.io/notifications and copy the token below to easily send yourself a notification.");
console.warn("Notifications on iOS (and I believe Android) ONLY WORK ON A PHYSICAL DEVICE, not a simulator or emulator!!!")
console.log(token);
this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
};
componentDidMount() {
this.registerForPushNotificationsAsync();
this._notificationSubscription = Notifications.addListener(
this._handleNotification
);
}
_handleNotification = async notification => {
if (notification.remote) {
Vibration.vibrate();
const notificationId = Notifications.presentLocalNotificationAsync({
title: "Follow #technoplato",
body: "To learn yourself goodly (also follow PewDiePie)",
ios: { _displayInForeground: true } // <-- HERE'S WHERE THE MAGIC HAPPENS
});
}
};
Quick and Easy Sanity Check
1) Go here: https://expo.io/notifications
2) Copy the token that is output to the terminal when your application is run.
3) Open your application on iOS.
4) Send a notification to yourself from https://expo.io/notifications and observe that it shows up even when your app is foregrounded.
Notes
Notifications WILL NOT BE RECEIVED ON AN IOS SIMULATOR
Expo makes Notifications ridiculously easy. I honestly can't believe it.
No idea why displayInForeground is false by default and not more prominent in the documentation. I'll submit a PR for it if I can.
Code originally found at this Snack: https://snack.expo.io/#documentation/pushnotifications?platform=ios
LocalNotification.ios._displayInForeground found here: https://docs.expo.io/versions/v36.0.0/sdk/notifications/#localnotification