我有一个 React 本机应用程序,带有 expo 托管工作流程,以及 firebase 中的数据库。当用户登录应用程序时,系统会要求他们同意推送通知。
import { View, Text, StyleSheet, TextInput, Button } from "react-native";
import React, { useState } from "react";
import { useRouter } from "expo-router";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import "../firebaseConfig";
import { TouchableOpacity } from "react-native-gesture-handler";
import validateToken from "../api/validateToken";
import { useUser } from "../UserContext";
import * as Notifications from "expo-notifications";
import Constants from "expo-constants";
import axios from "axios";
const REACT_APP_ANDROID_EMULATOR_BASE_URL = Constants.expoConfig?.extra?.REACT_APP_ANDROID_BASE_URL;
const REACT_APP_EXPO_PROJECT_ID = Constants.expoConfig?.extra?.REACT_APP_EXPO_PROJECT_ID;
const LoginPage = () => {
const { setUser } = useUser();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigation = useRouter();
const [errorMessage, setErrorMessage] = useState<any>();
const registerForPushNotificationsAsync = async () => {
const { status } = await Notifications.requestPermissionsAsync();
if (status !== "granted") {
alert("Sorry, we need notification permissions to make this work!");
return null;
}
const token = (
await Notifications.getExpoPushTokenAsync({
projectId: REACT_APP_EXPO_PROJECT_ID,
})
).data;
return token;
};
const handleLogin = async () => {
try {
const pushToken = await registerForPushNotificationsAsync();
if (!pushToken) {
setErrorMessage("Failed to register for notifications.");
return;
}
const auth = getAuth();
const ordinalCaseEmail = email.toLocaleLowerCase();
const ordinalCasePassword = password.toLocaleLowerCase();
const userCredential = await signInWithEmailAndPassword(auth, ordinalCaseEmail, ordinalCasePassword);
const user = userCredential.user;
const token = await user.getIdToken();
const data = await validateToken(token);
const response = await axios.get(`${REACT_APP_ANDROID_BASE_URL}/api/users/${user.uid}`);
setUser({
uid: user.uid,
jwt: token,
deviceToken: pushToken,
userName: response.data.userName,
email: response.data.email,
image: response.data.image,
});
if (data.isValid) {
await axios.post(`${REACT_APP_ANDROID_BASE_URL}/api/user/is-online`, {
id: user.uid,
online: true,
});
navigation.push("/(tabs)");
} else {
throw new Error("Invalid token or other server-side error");
}
} catch (error: any) {
const errorMessage = error.response?.data?.error || error.message || "An unexpected error occurred.";
setErrorMessage(errorMessage);
console.error("Error: ", error);
}
};
这在通过 USB 连接的物理设备和 Android 模拟器上绝对可以正常工作。
但是,一旦部署并在物理设备和模拟器上运行已安装的应用程序,我会收到此错误:
“调用本机方法时遇到异常:在模块 ExpoPushTokenManager 上执行导出方法 getDevicePushTokenAsync 时发生异常:默认 FirebaseApp 在此过程中未初始化 com.teologi.newMotivationalExerciseApp。请确保首先调用 FirebaseApp.initializeApp(context)。”
我的 google-services.json 位于根级别。
{
"project_info": {
"project_number": "12345",
"firebase_url": "https://motivationalexerciseapp-default-rtdb.europe-west1.firebasedatabase.app",
"project_id": "motivationalexerciseapp",
"storage_bucket": "motivationalexerciseapp.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:132146:android:12345565",
"android_client_info": {
"package_name": "com.teologi.newMotivationalExerciseApp"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "12234665sdffgsdg"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
我的 Firebase 配置
import { initializeApp } from "firebase/app";
import { connectFirestoreEmulator, getFirestore, writeBatch } from "firebase/firestore";
import { getAuth, initializeAuth, getReactNativePersistence } from "firebase/auth";
import ReactNativeAsyncStorage from "@react-native-async-storage/async-storage";
import { collection, query, getDocs, deleteDoc, doc } from "firebase/firestore";
import "firebase/firestore";
import "firebase/functions";
import Constants from "expo-constants";
const { apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId } = Constants.expoConfig.extra;
const firebaseConfig = {
apiKey: apiKey,
authDomain: authDomain,
projectId: projectId,
storageBucket: storageBucket,
messagingSenderId: messagingSenderId,
appId: appId,
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = initializeAuth(app, {
persistence: getReactNativePersistence(ReactNativeAsyncStorage),
});
export { db, auth };
我在这里读到:https://docs.expo.dev/push-notifications/push-notifications-setup/#android
在这里:https://docs.expo.dev/push-notifications/fcm-credentials/
我需要将我的服务帐户密钥添加到博览会仪表板中,所以我已经添加了,但仍然没有运气。我不确定我错过了什么。非常感谢任何帮助。
编辑 1:我现在注意到我在本节中一直在使用我的网络应用程序 api 密钥:
"extra": {
"apiKey":"myOldWebApiKey"
}
我从 android 应用程序 google-services.json 文件中获得了一个新的。我没想到要切换它,因为在开发过程中 Web api 密钥从未失败过。这可能是问题所在吗?我将对其进行测试,但由于博览会中的低优先级构建队列,我需要一段时间才能注意到任何差异。
编辑2:仍然是同样的错误,我对我错过的东西感到茫然。
在这里发布对我有用的解决方案。可能看起来很明显,但我找不到任何地方可以证明这种情况。
在我的 google-services.json 中,我有一些看起来像这样的东西
{
"project_info": {
... },
"client": [
{
"client_info": {
...
},
"oauth_client": [],
"api_key": [
{
"current_key": "MY-AUTO-GENERATED-ANDROID-APP-API-KEY"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
在我的 app.json 中,我有这个:
"extra": {
"REACT_APP_ANDROID_EMULATOR_BASE_URL": "...",
"REACT_APP_EXPO_PROJECT_ID": "...",
"apiKey": "MY-AUTO-GENERATED-WEB-APP-API-KEY",
"authDomain": "...",
"databaseURL": "...",
"projectId": "...",
"storageBucket": "...",
"messagingSenderId": "...",
"appId": "...",
"router": {
"origin": false
},
所以,我基本上只需将 google-services.json 中的“current_key”值从 app.json 切换到我的 web-app apiKey(为了清楚起见,我在两个地方都使用了 web-app api 密钥)。
也许这是显而易见的,但在多个线程和演练中,我从未见过有人提到这一点,即使是“首先确保你已经检查了 api 密钥匹配”。
希望这对某人有帮助。干杯!