Notifications.getExpoPushTokenAsync not working on Stand Alone iOS - ios

I'm trying to my expo/react-native project to send push notifications to my server. It works on standalone Android, but not stand alone iPhone.
The standalone iPhone app never sends the token.
Since the app sends nothing without error, I tried removing:
if (finalStatus !== 'granted') { return; }
This didn't work either.
export async function registerForPushNotificationsAsync(token) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
// Only ask if permissions have not already been determined, for iOS.
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
// Stop here if the user did not grant permissions
if (finalStatus !== 'granted') {
return;
}
// Get the push token that uniquely identifies this device
let expoToken = await Notifications.getExpoPushTokenAsync();
// Post new push token to backend for user
return axios({
method: 'POST',
url: `${str.ROOT_URL}/account/push/`,
headers: {
Authorization: `Token ${token}`
},
data: {
"token": expoToken,
"status": finalStatus
}
});
}
I expected the token to get sent to the backend, but nothing is sent on the standalone iOS app.
Please let me know if you know a workaround or had this issue before. Thanks!

I think it's too late to give an answer, but I spent 2 days to resolve it... I hope it helps somebody.
Instead this:
import * as Notifications from "expo-notifications";
Try this:
import { Notifications } from "expo";

Related

How to share cookies between in-app browser and React Native (iOS)?

I am trying to implement SSO in a mobile app.
I am using react-native-inappbrowser-reborn to handle the SSO auth flow in an in-app browser. I can successfully authenticate inside the in-app browser. That is to say, I receive a session cookie and I can view the web version of the app from inside of the in-app browser. However, when I redirect back to the mobile application, none of my fetch requests include the session cookie! I have fetch configured with {credentials: 'include'}. CookieManager.getAll() returns an empty object.
I am experiencing this problem on iOS (v15.2), and I have yet to test on Android.
According to the documentation I should be able to share the cookie set in the in the in-app browser with my react-native app.
I am using the following code based off of the react-native-inappbrowser-reborn documentation
async function authenticate() {
const url = 'http://localhost:3000/sso/login?redirect_uri=myapp://home';
const deepLink = 'myapp://home';
try {
if (await InAppBrowser.isAvailable()) {
InAppBrowser.openAuth(url, deepLink, {
// iOS Properties
ephemeralWebSession: false,
// Android Properties
showTitle: false,
enableUrlBarHiding: true,
enableDefaultShare: false,
}).then(async (response) => {
if (response.type === 'success' && response.url) {
console.log('should see a new cookie set!');
console.log(await CookieManager.getAll()); // Sadly, nothing in here
const user = await fetch('http://localhost:3000/user', {
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
/* This is an authenticated endpoint which returns a 401,
because for some reason the session cookie doesn't go along with the request */
}
});
} else {
throw new Error('login unsuccessful');
}
} catch (error) {
throw new Error();
}
}
Any bit of insight here would be immensely appreciated!

Firebase Cloud Function Push Notification Really Slow

I have an iOS app that sends a time sensitive push notification from an event via Cloud Firestore-triggered function.
In my function, I do a simple get operation prior to sending out the push notification, and it gets delivered from 30 sec up to 1 min. Can anyone advise on improving the speed?
I've looked at the online documentation talking about setting a minimum # of instances to reduce a cold start. I've also looked at this SO answer. As someone that's new to this, I admittedly am having difficulty following and pinpointing my advised next step. Any help and guidance would be extremely appreciated.
exports.pushNotification = functions.firestore.document('/users/{userId}').onUpdate((change, context) => {
var recipientUid = context.params.userId;
return admin.firestore().collection('users').doc(recipientUid).get().then(doc => {
const userData = doc.data();
var fcmToken = userData.fcmToken;
var message = {
notification: {
title: sendingTitle,
body: sendingMessage
},
apns : {
payload : {
aps : {
badge : 1,
sound: "default"
}
}
},
token: fcmToken
};
admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent push notification message:', response);
return;
})
.catch((error) => {
console.log('Error sending message:', error);
return;
});
})
});
One possible cause is that you don't correctly manage the life cycle of your Cloud Function: as a matter of fact you are not returning the Promises chain, as shown below (see the comments in the code):
exports.pushNotification = functions.firestore.document('/users/{userId}').onUpdate((change, context) => {
var recipientUid = context.params.userId;
return admin.firestore().collection('users').doc(recipientUid).get()
.then(doc => {
const userData = doc.data();
var fcmToken = userData.fcmToken;
var message = { ... };
return admin.messaging().send(message) // !!! See the return here
}) // And see also that we close the then block here
.then((response) => {
console.log('Successfully sent push notification message:', response);
return;
})
.catch((error) => {
console.log('Error sending message:', error);
return;
});
});
For more details on how to correctly manage the life cycle, I would suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series as well as read the following doc. Also, reading the doc on how to chain Promises will help.

expo-notifications - Error encountered while updating the device push token with the server

I'm using Expo's bare-workflow for my react-native project. I'm using expo's push notification service. I keep getting the following error whenever I try to get my expo push token:
[expo-notifications] Error encountered while updating the device push token with the server: {"error":"invalid_token","error_description":"The bearer token is invalid"}
I'm running the app directly on my device so should be able to get notifications.
I'm using basically the same registerForPushNotificationsAsync() that is provided in the documentation.
import Constants from 'expo-constants';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
export const registerForPushNotificationsAsync = async () => {
try {
if (Constants.isDevice) {
const experienceId = '#{username}/{slug}';
const {
status: existingStatus
} = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
const token = (
await Notifications.getExpoPushTokenAsync({ experienceId })
).data;
console.log(' 🏷 🏷 Token :', token);
return token;
} else {
alert('Must use physical device for Push Notifications');
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C'
});
}
return undefined;
} catch (error) {
console.error('Error in registerForPushNotificationsAsync()', error);
return undefined;
}
};
Expo packages in package.json
"expo": "~40.0.0",
"expo-analytics": "^1.0.16",
"expo-app-loading": "^1.0.1",
"expo-font": "~8.4.0",
"expo-image-manipulator": "~8.4.0",
"expo-image-picker": "~9.2.0",
"expo-intent-launcher": "~8.4.0",
"expo-notifications": "~0.8.2",
"expo-splash-screen": "~0.8.0",
"expo-updates": "~0.4.0",
I can't see anything about setting a bearer token, so I'm unsure what it could be after or where to even set it assuming I was able to determine what bearer token it is after.
Does anyone know what might be causing the problem?
So we figured it out.
We were using fetch-token-intercept which was adding our bearer token to calls going to expo, which meant validation was failing.
We modified the function to exclude calls to expo from including our bearer token and now the token is being retrieved successfully.

React Native Ios Push Notification Not Working

I use '#react-native-firebase/messaging' module to send notifications. On Android everything works fine, Following is the error log I get when I try const fcmToken = await firebase.messaging().getToken(); on ios device.
NativeFirebaseError: [messaging/unknown] The operation couldn’t be completed. (com.firebase.iid error 1001.)
I have already implemented "react-native-permissions" to grant notification permission.
My AppDelegate.m contains :
if ([FIRApp defaultApp] == nil) {
[FIRApp configure];
}
Should I add anything else to it?
Any help or suggestion will be very helpful.
Thanks in advance
You need to check and ask messaging permission before get the fcm token in iOS
/**
* Check is notification showing permission enabled if not ask the permission.
*/
async checkFcmPermission() {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions
this.getFcmToken(); // const fcmToken = await firebase.messaging().getToken();
} else {
// User doesn't have permission
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorized
this.getFcmToken(); // const fcmToken = await firebase.messaging().getToken();
})
.catch(error => {
// User has rejected permissions
console.log(
'PERMISSION REQUEST :: notification permission rejected',
);
});
}
});
}
There are a lot of solutions to this issue. I post my answer since some StackOverflow users requested.
Here is my code
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};
async function requestPermissionForNotification() {
try {
const permission = await requestNotifications(["alert", "badge", "sound"]);
if (permission.status === "granted") {
setupNotification();
}
} catch (e) {
console.log(e)
}
};
async function setupNotification() {
try {
await sleep(5000)
// TODO no need token for now, Will be used for future releases
const enabled = await firebase.messaging().hasPermission();
await requestNotifications(['alert', 'badge', 'sound']);
await firebase.messaging().registerForRemoteNotifications();
const token = await firebase.messaging().getToken();
firebase.messaging().onMessage(async (remoteMessage) => {
});
} catch (e) {
console.log(e)
}
}
And Also I have added following to Info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
To sum up, I use react permissions to grant permission and the sleep method is to wait for firebase to be ready. Finally uninstalled app couple time to get a result.
When It started working, I couldn't dare to touch the code since I spent days on this.

Fetch not working on react native when run on Expo from iphone

So it's actually been on and off the past few days, the fetch function for POST method (for login purposes) on my app would suddenly stop working when I'm not even making any changes to the code.
I spent hrs tracking down the bug the first time this happened but realized that the code is still running fine on Android simulator that I run from Android studio.
This problem only happens on my iphone device when I run the code via expo.
is there any sort of convention on how to tackle this issue?
I've been googling for a few days now but no one forum actually agreed on a solution, some works but says it's unstable and some just doesn't work.
I'm pretty new to expo, react native, and everything in general, so please enlighten me using "english" lol
EDIT: since I was asked about the code, here it is, I hope it helps:
export function login(user, callback){
var endpoint = "oauth/token";
const { username, password } = user;
//from https://scotch.io/tutorials/how-to-encode-and-decode-strings-with-base64-in-javascript
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}};
let header = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": ("Basic " + Base64.encode(username+":"+password))
};
let userData = {
"grant_type": "password",
"username": username,
"password": password
};
return fetchAPI(endpoint,'POST', header, userData)
.then((tokenJson) => {
alert("\nAccess Token:\n\n" + tokenJson.access_token + "");
callback(true, {exists : true, token : tokenJson.access_token}, null);
})
.catch((error) => {
alert(error);
callback(false, null, error);
});
}
And here's the fetchAPI function (a generic fetch function) called from that function:
export function fetchAPI(endpoint, method, header, data) {
let url = 'http://10.64.2.149:8081/' + endpoint;
let options = {
method: method,
headers: header,
body: stringify(data) //from qs-stringify package
};
return fetch(url, options)
.then(response => {
return response.json()
.then((json) => { ...//not relevant from this point on
The fetch 'POST' seems to always get stuck at response.json(), it just never resolves until it returns "network request failed". But as I said only happens sometimes when I run it on iphone via expo. It is fine on Android sim.
Thanks! :)

Resources