React Native使用expo-sqlite在iOS无法打开预存数据库求助
Expo SQLite 预存数据库 iOS 加载失败问题解决
问题场景
使用expo-sqlite加载预存的products.db数据库,Android端运行正常,但iOS端出现两类错误:
场景1:使用SQLiteProvider时的错误
代码:
return ( <> <SQLiteProvider databaseName="@/data/products.db" assetSource={{ assetId: require('@/data/products.db') }}> <ResultRenderer profile={profile} /> <Text>{buildSQLquery(profile)}</Text> </SQLiteProvider> </> );
iOS报错:
ERROR Error: The file “ExponentAsset-171a7c1e81bf6393675b790dec0140a7.db” doesn’t exist. ...(错误栈省略)
场景2:使用openDatabaseSync时的错误
代码:
const db = SQLite.openDatabaseSync("@/data/products.db", {}); const result = db.getAllSync("SELECT * from products") console.log(result);
iOS报错:
ERROR Error: FunctionCallException: Calling the 'initSync' function has failed (at ExpoModulesCore/SyncFunctionDefinition.swift:87) → Caused by: ArgumentCastException: The 1st argument cannot be cast to type SharedObject<NativeDatabase> (at ExpoModulesCore/JavaScriptUtils.swift:61) → Caused by: NativeSharedObjectNotFoundException: Unable to find the native shared object associated with given JavaScript object (at ExpoModulesCore/DynamicSharedObjectType.swift:58) ...(错误栈省略)
解决方法
1. 错误原因说明
ExponentAsset-xxx.db是Expo处理资源时生成的哈希命名文件,iOS沙盒机制不允许直接访问项目资产目录下的文件,必须先将数据库复制到可写的沙盒路径中。openDatabaseSync的错误是因为传入的路径无效,无法找到对应的本地数据库实例。
2. 复制数据库到可访问沙盒路径
通过expo-asset和expo-file-system将预存数据库复制到iOS可读写的沙盒目录:
import { Asset } from 'expo-asset'; import * as FileSystem from 'expo-file-system'; async function getValidDatabasePath() { // 获取数据库资产实例 const dbAsset = Asset.fromModule(require('@/data/products.db')); // 下载资产到本地临时路径 await dbAsset.downloadAsync(); // 定义沙盒中的目标存储路径 const targetDbPath = `${FileSystem.documentDirectory}products.db`; // 检查目标路径是否已存在数据库,避免重复复制 const fileInfo = await FileSystem.getInfoAsync(targetDbPath); if (!fileInfo.exists) { await FileSystem.copyAsync({ from: dbAsset.localUri!, to: targetDbPath, }); } return targetDbPath; }
3. 修复SQLiteProvider用法
结合React Suspense处理异步路径获取,传入有效沙盒路径:
import React from 'react'; import { SQLiteProvider } from 'expo-sqlite'; function AppDatabaseProvider({ children }) { // React.use 用于在组件中处理异步逻辑,需包裹在Suspense中 const dbPath = React.use(getValidDatabasePath()); return ( <SQLiteProvider databaseName={dbPath}> {children} </SQLiteProvider> ); } // 调用时用Suspense包裹加载状态 function App() { return ( <React.Suspense fallback={<Text>Loading database...</Text>}> <AppDatabaseProvider> <ResultRenderer profile={profile} /> <Text>{buildSQLquery(profile)}</Text> </AppDatabaseProvider> </React.Suspense> ); }
4. 修复openDatabaseSync用法
确保数据库复制完成后,传入有效沙盒路径调用同步方法:
import * as SQLite from 'expo-sqlite'; async function queryDatabase() { const dbPath = await getValidDatabasePath(); const db = SQLite.openDatabaseSync(dbPath); const result = db.getAllSync("SELECT * from products"); console.log(result); } // 在组件生命周期中触发查询 useEffect(() => { queryDatabase(); }, []);
5. 资产打包配置
在app.json中声明数据库资产,确保Expo正确打包:
{ "expo": { "assets": [ "./data/products.db" // 替换为你的数据库实际路径 ] } }
内容的提问来源于stack exchange,提问作者apioltk
相关产品推荐
相关产品推荐

