使用expo-contacts指定字段获取联系人时应用崩溃求助
Expo Contacts 指定字段获取联系人在iPhone12+ iOS17.5.1机型崩溃问题排查建议
问题概况
- 环境:Expo SDK 51.0.0,使用ExpoGo运行
- 核心现象:调用
Contacts.getContactsAsync时,使用rawContacts: true参数可正常获取857条联系人数据;但改为指定fields数组(包含FirstName、LastName、PhoneNumbers、Emails)时,应用无报错日志直接崩溃 - 机型差异:仅iPhone12及以上机型(iOS17.5.1)出现该问题,iPhone8同系统版本可正常运行
- 权限配置:已按文档完成
expo-contacts的权限配置,无权限相关报错
代码对比
正常运行代码
useEffect(() => { console.log("Checking current user..."); if (auth.currentUser) { const uid = auth.currentUser.uid; fetchUserData(uid) .then((data) => { console.log("User data fetched:", data); setUser(data as User); fetchContacts(); }) .catch((error) => console.log("Error fetching user data:", error)); } }, [auth.currentUser]); const fetchContacts = async () => { try { console.log("Requesting contacts permission..."); const { status } = await Contacts.requestPermissionsAsync(); console.log("Contacts permission status:", status); setPermissionStatus(status); if (status === "granted") { console.log("Fetching contacts..."); const { data } = await Contacts.getContactsAsync({ rawContacts: true, }); console.log(`Contacts fetched: ${data.length} contacts.`); setContacts(data); } else { console.log("Contacts permission denied"); setError("Contacts permission denied"); Alert.alert("Permission Denied", "Contacts permission not granted."); } } catch (error: any) { console.error("Error fetching contacts:", error); setError(error.message); Alert.alert("Error", error.message); }(error: any) => { console.error("Error fetching contacts:", error); setError(error.message); Alert.alert("Error", error.message); } };
崩溃触发代码
useEffect(() => { console.log("Checking current user..."); if (auth.currentUser) { const uid = auth.currentUser.uid; fetchUserData(uid) .then((data) => { console.log("User data fetched:", data); setUser(data as User); fetchContacts(); }) .catch((error) => console.log("Error fetching user data:", error)); } }, [auth.currentUser]); const fetchContacts = async () => { try { console.log("Requesting contacts permission..."); const { status } = await Contacts.requestPermissionsAsync(); console.log("Contacts permission status:", status); setPermissionStatus(status); if (status === "granted") { console.log("Fetching contacts..."); const { data } = await Contacts.getContactsAsync({ fields: [Contacts.Fields.FirstName, Contacts.Fields.LastName, Contacts.Fields.PhoneNumbers, Contacts.Fields.Emails], }); console.log(`Contacts fetched: ${data.length} contacts.`); setContacts(data); } else { console.log("Contacts permission denied"); setError("Contacts permission denied"); Alert.alert("Permission Denied", "Contacts permission not granted."); } } catch (error: any) { console.error("Error fetching contacts:", error); setError(error.message); Alert.alert("Error", error.message); }(error: any) => { console.error("Error fetching contacts:", error); setError(error.message); Alert.alert("Error", error.message); } };
日志对比
正常运行日志
LOG Form submission initiated with values: {"email": "xxx.xxx@xxx.com", "password": "xxx"} LOG Set loading action dispatched with isLoading: true LOG Handling setLoading in reducer LOG Handling login request in reducer LOG Dispatching loginSuccess action... LOG Handling login success in reducer LOG Handling hideLoading in reducer LOG Checking current user... LOG User data fetched: {"consentReceiveEmailSMS": true, "email": "xxx.xxx@xxx.com", "emailVerified": true, "firstName": "aaa", "lastLoggedIn": {"nanoseconds": 960000000, "seconds": 1716267809}, "lastName": "bbb", "phoneNumber": "(ccc)ccc-cccc", "phoneNumberVerified": true, "registeredOn": "2023-09-24T18:30:41.071Z", "username": "xxx"} LOG Requesting contacts permission... LOG Contacts permission status: granted LOG Fetching contacts... LOG Contacts fetched: 857 contacts.
崩溃触发日志
LOG Checking current user... LOG User data fetched: {"consentReceiveEmailSMS": true, "email": "xxx.xxx@xxx.com", "emailVerified": true, "firstName": "aaa", "lastLoggedIn": {"nanoseconds": 960000000, "seconds": 1716267809}, "lastName": "bbb", "phoneNumber": "(ccc)ccc-cccc", "phoneNumberVerified": true, "registeredOn": "2023-09-24T18:30:41.071Z", "username": "xxx"} LOG Requesting contacts permission... LOG Contacts permission status: granted LOG Fetching contacts..
app.json配置
{ "expo": { "plugins": [ [ "expo-contacts", { "contactsPermission": "Allow xxx to access your contacts." } ] ], "name": "xxx", "slug": "xxx", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/color_logo_with_background.png", "userInterfaceStyle": "light", "splash": { "image": "./assets/splash.png", "resizeMode": "contain", "backgroundColor": "#ffffff" }, "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true, "bundleIdentifier": "com.xxx.zzz", "infoPlist": { "NSContactsUsageDescription": "xxx" } }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" }, "permissions": ["READ_CONTACTS"] }, "web": { "favicon": "./assets/favicon.png" }, "extra": { "eas": { "projectId": "aaa" } }, "sdkVersion": "51.0.0" } }
排查与解决建议
- 定位触发崩溃的字段:逐个移除
fields数组中的字段,每次只保留一个字段测试,确认是否是某一个特定字段导致崩溃(比如PhoneNumbers或Emails在新机型上的处理逻辑有问题) - 增强错误捕获:在
getContactsAsync调用后添加额外的Promise捕获,尝试捕获原生层的错误:const { data } = await Contacts.getContactsAsync({ fields: [Contacts.Fields.FirstName, Contacts.Fields.LastName, Contacts.Fields.PhoneNumbers, Contacts.Fields.Emails], }).catch(err => { console.error("原生层捕获错误:", err); throw err; }); - 测试小批量数据:添加
limit参数,先获取10条联系人数据,看是否崩溃,排除是某条异常联系人数据(比如空值、特殊格式的手机号/邮箱)导致的问题 - 升级依赖版本:检查
expo-contacts是否有SDK51兼容的更新版本,部分机型特定bug可能已在新版本修复 - 脱离ExpoGo测试:使用EAS构建自定义开发客户端,排除ExpoGo本身的兼容性问题
- 手动处理字段提取:先用
rawContacts: true获取全部数据,再在前端手动提取需要的字段,模拟指定fields的场景,验证是否是前端处理逻辑问题 - 检查联系人数据异常:查看是否存在特殊联系人数据(比如无姓名、手机号格式异常),可以尝试在获取后先过滤异常数据再处理
内容的提问来源于stack exchange,提问作者Yanhamu
相关产品推荐
相关产品推荐

