I am in the process of migrating react-native-device-info from v2 to v3 and I have a breaking change. Prior to updating the package, everything was working fine. The error appears on all emulators and real devices.
The error I get is:
NativeFirebaseError: [messaging/unregistered] You must be registered for remote notifications before calling get token, see messaging().registerForRemoteNotifications() or requestPermission().
Env Info:
System:
OS: macOS 10.15.4
CPU: (8) x64 Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz
Memory: 6.44 GB / 16.00 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 12.14.0 - ~/.nvm/versions/node/v12.14.0/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.13.4 - ~/.nvm/versions/node/v12.14.0/bin/npm
SDKs:
iOS SDK:
Platforms: iOS 13.4, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
Android SDK:
API Levels: 28, 29
Build Tools: 28.0.3, 29.0.3
System Images: android-28 | Google Play Intel x86 Atom
IDEs:
Android Studio: 3.6 AI-192.7142.36.36.6241897
Xcode: 11.4.1/11E503a - /usr/bin/xcodebuild
npmPackages:
react: 16.12.0 => 16.12.0
react-native: 0.61.5 => 0.61.5
npmGlobalPackages:
create-react-native-app: 2.0.2
react-native-cli: 2.0.1
react-native-debugger-open: 0.3.24
My code:
export interface LoginParams {
token?: string;
useProduct?: string;
useUser?: string;
ssoToken?: string;
}
// TODO: Should be switched out with tv-ui-common once the whole app starts using Product model.
interface AuthenticationResponse {
noCacheToken: string;
products: ProductData[];
currentUserId: number;
}
interface ProductsResponse {
currentProductCode: string;
products: ProductData[];
}
const getInitialIsLoggedIn = () => false;
const getInitialAuthenticating = () => false;
const getInitialProducts = () => [];
const getInitialSelectedProduct = () => null;
const getInitialCurrentUserId = () => null;
export default class AuthenticationStore {
private static async authenticate({ token, useProduct, useUser, ssoToken }: LoginParams) {
const params = new URLSearchParams();
params.append('ui', UI);
params.append('uuid', DeviceInfo.getUniqueId());
params.append('model', DeviceInfo.getSystemName());
params.append('hwVersion', DeviceInfo.getModel());
params.append('swVersion', DeviceInfo.getSystemVersion());
params.append('uiVersion', getUiVersion());
if (useProduct) {
params.append('useProduct', useProduct);
}
if (useUser) {
params.append('useUser', useUser);
}
try {
const notificationToken = await messaging().getToken();
params.append('pushToken', notificationToken);
console.log(notificationToken, 'notificationToken');
} catch (error) {
// iPhone Simulator does not give a token
console.log("error", error);
}
try {
// If we have token, meaning user authenticated via TV pairing
if (token) {
params.append('token', token);
return await Api.post<AuthenticationResponse>(
'authentication/token',
params.toString(),
undefined,
'3.0',
);
}
if (ssoToken) {
params.append('ssoToken', ssoToken);
}
return await Api.post<AuthenticationResponse>(
'authentication/sso',
params.toString(),
undefined,
'3.0',
);
} catch (error) {
console.log('err', error);
if (error.response.status === 423) {
throw new ProductNotAvailableError(error.response.data.products);
} else {
throw error;
}
}
}
@observable public isLoggedIn = getInitialIsLoggedIn();
@observable public authenticating = getInitialAuthenticating();
@observable public products: ProductData[] = getInitialProducts();
@observable public selectedProduct: ProductData | null = getInitialSelectedProduct();
@observable public currentUserId: number | null = getInitialCurrentUserId();
constructor() {
this.registerPushNotificationToken = this.registerPushNotificationToken.bind(this);
this.loadProducts = this.loadProducts.bind(this);
}
@action public reset() {
this.isLoggedIn = getInitialIsLoggedIn();
this.authenticating = getInitialAuthenticating();
this.products = getInitialProducts();
this.selectedProduct = getInitialSelectedProduct();
this.currentUserId = getInitialCurrentUserId();
}
public async registerPushNotificationToken(token: string) {
if (this.isLoggedIn) {
await Api.post('push-token-update', { token }, undefined, '3.0');
}
}
public async login(params: LoginParams = {}): Promise<boolean> {
this.setAuthenticating(true);
try {
const response = await AuthenticationStore.authenticate(params);
setNoCacheToken(response.data.noCacheToken);
const products = response.data.products || [];
this.setProducts(products);
if (response.status === 202) {
if (products.length === 1) {
const [product] = products;
return this.login({ useProduct: product.code });
}
if (products.length > 1) {
throw new ProductNotSelectedError();
}
}
this.setSelectedProduct(products[0]);
this.setAuthenticating(false, true, response.data.currentUserId);
analytics().setUserId(response.data.currentUserId.toString());
} catch (error) {
this.setAuthenticating(false, false);
if (error.response && error.response.status === 401) {
if (error.response.data.errorCode === 'E_UNAUTHORIZED') {
// Silently fail error if user was not authenticated while doing sso request.
// We do not need to handle that 'error'. It's common case for cookies
// to expire.
return false;
}
throw new NoProductsError();
}
if (
(error.response && error.response.status === 400)
|| error instanceof ProductNotSelectedError
) {
throw error;
} else if (error instanceof ProductNotAvailableError) {
// Product is suspended or in debt and thus not usable
const selectedProduct = error.products.find(
product => product.code === params.useProduct,
);
this.setProducts(error.products, selectedProduct);
throw error;
} else {
// Something went wrong with authentication on server side.
throw new Error(`Unexpected authentication error: ${error}`);
}
}
return this.isLoggedIn;
}
public async loadProducts() {
try {
// Logged in version of fetching products
const { data: { products, currentProductCode } } = await Api.get<ProductsResponse>('products');
const selectedProduct = products.find(product => product.code === currentProductCode);
this.setProducts(products, selectedProduct);
} catch (error) {
if (error.response.status === 401) {
// Logged out version of fetching products
try {
const response = await AuthenticationStore.authenticate({});
const products = response.data.products || [];
this.setProducts(products);
} catch (loginError) {
// TODO: Handle error
}
}
// TODO: Handle failed request on loading products
}
}
@action public setAuthenticating(
authenticating: boolean = false,
isLoggedIn: boolean = this.isLoggedIn,
currentUserId: number | null = this.currentUserId,
) {
this.authenticating = authenticating;
this.isLoggedIn = isLoggedIn;
this.currentUserId = this.isLoggedIn ? currentUserId : null;
}
@action private setProducts(
products: ProductData[],
selectedProduct: ProductData | null = null,
) {
this.products = products;
this.setSelectedProduct(selectedProduct);
}
@action private setSelectedProduct(product: ProductData | null = null) {
this.selectedProduct = product;
}
}
I have dug around online but can't seem to find anything at all similar, does anyone have any ideas what I can try?
The error says that you need to request the users permission for notifications before calling getToken. Have you tried this? https://rnfirebase.io/messaging/usage