parent
6e97479586
commit
a676f00288
@ -0,0 +1,9 @@
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
|
||||
export const deleteFavoriteCity = async () => {
|
||||
try {
|
||||
await AsyncStorage.removeItem('favorite_city')
|
||||
} catch(e) {
|
||||
console.log("An error occurred", e);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { City } from '../data/stub';
|
||||
import {FETCH_FAVORITE_CITY} from '../redux/constants';
|
||||
|
||||
export const getFavoriteCityStorage = async () => {
|
||||
try {
|
||||
const cityJson = await AsyncStorage.getItem('favorite_city')
|
||||
return cityJson != null ? JSON.parse(cityJson) : null;
|
||||
} catch(e) {
|
||||
console.log("An error occurred", e);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { City } from "../data/stub";
|
||||
|
||||
export const storeFavoriteCity = async (city: City | null) => {
|
||||
try {
|
||||
const cityJson = JSON.stringify(city)
|
||||
await AsyncStorage.setItem('favorite_city', cityJson);
|
||||
} catch (e) {
|
||||
console.log("An error occurred", e);
|
||||
}
|
||||
}
|
21
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/LICENSE
generated
vendored
21
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/LICENSE
generated
vendored
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-present, Facebook, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
27
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/README.md
generated
vendored
27
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/README.md
generated
vendored
@ -0,0 +1,27 @@
|
||||
# React Native Async Storage
|
||||
|
||||
An asynchronous, unencrypted, persistent, key-value storage system for React Native.
|
||||
|
||||
|
||||
## Supported platforms
|
||||
|
||||
- iOS
|
||||
- Android
|
||||
- [Web](https://github.com/react-native-async-storage/async-storage/releases/tag/v1.9.0)
|
||||
- [MacOS](https://github.com/react-native-async-storage/async-storage/releases/tag/v1.8.1)
|
||||
- [Windows](https://github.com/react-native-async-storage/async-storage/releases/tag/v1.10.0)
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
Head over to [documentation](https://react-native-async-storage.github.io/async-storage/docs/install) to learn more.
|
||||
|
||||
|
||||
## Contribution
|
||||
Pull requests are welcome. Please open an issue first to discuss what you would like to change.
|
||||
|
||||
See the [CONTRIBUTING](CONTRIBUTING.md) file for more information.
|
||||
|
||||
## License
|
||||
|
||||
MIT.
|
@ -0,0 +1,19 @@
|
||||
require 'json'
|
||||
|
||||
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "RNCAsyncStorage"
|
||||
s.version = package['version']
|
||||
s.summary = package['description']
|
||||
s.license = package['license']
|
||||
|
||||
s.authors = package['author']
|
||||
s.homepage = package['homepage']
|
||||
s.platforms = { :ios => "9.0", :tvos => "9.2", :osx => "10.14" }
|
||||
|
||||
s.source = { :git => "https://github.com/react-native-async-storage/async-storage.git", :tag => "v#{s.version}" }
|
||||
s.source_files = "ios/**/*.{h,m}"
|
||||
|
||||
s.dependency 'React-Core'
|
||||
end
|
146
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/android/build.gradle
generated
vendored
146
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/android/build.gradle
generated
vendored
@ -0,0 +1,146 @@
|
||||
import java.nio.file.Paths
|
||||
|
||||
def resolveModulePath(packageName) {
|
||||
def basePath = rootDir.toPath().normalize()
|
||||
|
||||
// Node's module resolution algorithm searches up to the root directory,
|
||||
// after which the base path will be null
|
||||
while (basePath) {
|
||||
def candidatePath = Paths.get(basePath.toString(), 'node_modules', packageName)
|
||||
if (candidatePath.toFile().exists()) {
|
||||
return candidatePath.toString()
|
||||
}
|
||||
|
||||
basePath = basePath.getParent()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
def safeExtGet(prop, fallback) {
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
def getFlagOrDefault(flagName, defaultValue) {
|
||||
rootProject.hasProperty(flagName) ? rootProject.properties[flagName] == "true" : defaultValue
|
||||
}
|
||||
|
||||
def getVersionOrDefault(String flagName, String defaultVersion) {
|
||||
rootProject.hasProperty(flagName) ? rootProject.properties[flagName] : defaultVersion
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileClasspath
|
||||
}
|
||||
|
||||
buildscript {
|
||||
// kotlin version is dictated by rootProject extension or property in gradle.properties
|
||||
ext.asyncStorageKtVersion = rootProject.ext.has('kotlinVersion')
|
||||
? rootProject.ext['kotlinVersion']
|
||||
: rootProject.hasProperty('AsyncStorage_kotlinVersion')
|
||||
? rootProject.properties['AsyncStorage_kotlinVersion']
|
||||
: '1.6.10'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
def projectExampleDir = Paths.get(project.projectDir.getParent(), "example", "android").toString()
|
||||
def rootProjectDir = rootProject.projectDir.getPath()
|
||||
if (projectExampleDir == rootProjectDir) {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$asyncStorageKtVersion"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AsyncStorage has default size of 6MB.
|
||||
// This is a sane limit to protect the user from the app storing too much data in the database.
|
||||
// This also protects the database from filling up the disk cache and becoming malformed.
|
||||
// If you really need bigger size, please keep in mind the potential consequences.
|
||||
long dbSizeInMB = 6L
|
||||
def newDbSize = rootProject.properties['AsyncStorage_db_size_in_MB']
|
||||
if (newDbSize != null && newDbSize.isLong()) {
|
||||
dbSizeInMB = newDbSize.toLong()
|
||||
}
|
||||
|
||||
// Instead of reusing AsyncTask thread pool, AsyncStorage can use its own executor
|
||||
def useDedicatedExecutor = getFlagOrDefault('AsyncStorage_dedicatedExecutor', false)
|
||||
|
||||
// Use next storage implementation
|
||||
def useNextStorage = getFlagOrDefault("AsyncStorage_useNextStorage", false)
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
if (useNextStorage) {
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply from: './testresults.gradle'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet('compileSdkVersion', 31)
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet('minSdkVersion', 21)
|
||||
targetSdkVersion safeExtGet('targetSdkVersion', 31)
|
||||
buildConfigField "Long", "AsyncStorage_db_size", "${dbSizeInMB}L"
|
||||
buildConfigField "boolean", "AsyncStorage_useDedicatedExecutor", "${useDedicatedExecutor}"
|
||||
buildConfigField "boolean", "AsyncStorage_useNextStorage", "${useNextStorage}"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
if (useNextStorage) {
|
||||
testOptions {
|
||||
unitTests {
|
||||
returnDefaultValues = true
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "${resolveModulePath("react-native")}/android"
|
||||
}
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
if (useNextStorage) {
|
||||
def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.2')
|
||||
def coroutines_version = "1.6.0"
|
||||
def coroutinesTest_version = "1.6.0"
|
||||
// if we don't provide explicit dependency on reflection, kotlin plugin
|
||||
// would add one automatically, probably a version that is not compatible with
|
||||
// used kotlin
|
||||
def kotlinReflect_version = project.ext.asyncStorageKtVersion
|
||||
def junit_version = "4.13.2"
|
||||
def robolectric_version = "4.7.3"
|
||||
def truth_version = "1.1.3"
|
||||
def androidxtest_version = "1.4.0"
|
||||
def androidtest_junit_version = "1.1.3"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinReflect_version"
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "androidx.test:runner:$androidxtest_version"
|
||||
testImplementation "androidx.test:rules:$androidxtest_version"
|
||||
testImplementation "androidx.test.ext:junit:$androidtest_junit_version"
|
||||
testImplementation "org.robolectric:robolectric:$robolectric_version"
|
||||
testImplementation "com.google.truth:truth:$truth_version"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesTest_version"
|
||||
}
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'com.facebook.react:react-native:+' // From node_modules
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.reactnativecommunity.asyncstorage">
|
||||
|
||||
</manifest>
|
||||
|
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.KEY_COLUMN;
|
||||
import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.TABLE_CATALYST;
|
||||
import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.VALUE_COLUMN;
|
||||
|
||||
/**
|
||||
* Helper for database operations.
|
||||
*/
|
||||
public class AsyncLocalStorageUtil {
|
||||
|
||||
/**
|
||||
* Build the String required for an SQL select statement:
|
||||
* WHERE key IN (?, ?, ..., ?)
|
||||
* without 'WHERE' and with selectionCount '?'
|
||||
*/
|
||||
/* package */ static String buildKeySelection(int selectionCount) {
|
||||
String[] list = new String[selectionCount];
|
||||
Arrays.fill(list, "?");
|
||||
return KEY_COLUMN + " IN (" + TextUtils.join(", ", list) + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the String[] arguments needed for an SQL selection, i.e.:
|
||||
* {a, b, c}
|
||||
* to be used in the SQL select statement: WHERE key in (?, ?, ?)
|
||||
*/
|
||||
/* package */ static String[] buildKeySelectionArgs(ReadableArray keys, int start, int count) {
|
||||
String[] selectionArgs = new String[count];
|
||||
for (int keyIndex = 0; keyIndex < count; keyIndex++) {
|
||||
selectionArgs[keyIndex] = keys.getString(start + keyIndex);
|
||||
}
|
||||
return selectionArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the given key, or null if not found.
|
||||
*/
|
||||
public static @Nullable String getItemImpl(SQLiteDatabase db, String key) {
|
||||
String[] columns = {VALUE_COLUMN};
|
||||
String[] selectionArgs = {key};
|
||||
|
||||
Cursor cursor = db.query(
|
||||
TABLE_CATALYST,
|
||||
columns,
|
||||
KEY_COLUMN + "=?",
|
||||
selectionArgs,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
try {
|
||||
if (!cursor.moveToFirst()) {
|
||||
return null;
|
||||
} else {
|
||||
return cursor.getString(0);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the key given, returns true if successful, false otherwise.
|
||||
*/
|
||||
/* package */ static boolean setItemImpl(SQLiteDatabase db, String key, String value) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(KEY_COLUMN, key);
|
||||
contentValues.put(VALUE_COLUMN, value);
|
||||
|
||||
long inserted = db.insertWithOnConflict(
|
||||
TABLE_CATALYST,
|
||||
null,
|
||||
contentValues,
|
||||
SQLiteDatabase.CONFLICT_REPLACE);
|
||||
|
||||
return (-1 != inserted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual merge of the (key, value) pair with the value stored in the database.
|
||||
* NB: This assumes that a database lock is already in effect!
|
||||
* @return the errorCode of the operation
|
||||
*/
|
||||
/* package */ static boolean mergeImpl(SQLiteDatabase db, String key, String value)
|
||||
throws JSONException {
|
||||
String oldValue = getItemImpl(db, key);
|
||||
String newValue;
|
||||
|
||||
if (oldValue == null) {
|
||||
newValue = value;
|
||||
} else {
|
||||
JSONObject oldJSON = new JSONObject(oldValue);
|
||||
JSONObject newJSON = new JSONObject(value);
|
||||
deepMergeInto(oldJSON, newJSON);
|
||||
newValue = oldJSON.toString();
|
||||
}
|
||||
|
||||
return setItemImpl(db, key, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two {@link JSONObject}s. The newJSON object will be merged with the oldJSON object by
|
||||
* either overriding its values, or merging them (if the values of the same key in both objects
|
||||
* are of type {@link JSONObject}). oldJSON will contain the result of this merge.
|
||||
*/
|
||||
private static void deepMergeInto(JSONObject oldJSON, JSONObject newJSON)
|
||||
throws JSONException {
|
||||
Iterator<?> keys = newJSON.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = (String) keys.next();
|
||||
|
||||
JSONObject newJSONObject = newJSON.optJSONObject(key);
|
||||
JSONObject oldJSONObject = oldJSON.optJSONObject(key);
|
||||
if (newJSONObject != null && oldJSONObject != null) {
|
||||
deepMergeInto(oldJSONObject, newJSONObject);
|
||||
oldJSON.put(key, oldJSONObject);
|
||||
} else {
|
||||
oldJSON.put(key, newJSON.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* From Pie and up, Android started to use Write-ahead logging (WAL), instead of journal rollback
|
||||
* for atomic commits and rollbacks.
|
||||
* Basically, WAL does not write directly to the database file, rather to the supporting WAL file.
|
||||
* Because of that, migration to the next storage might not be successful, because the content of
|
||||
* RKStorage might be still in WAL file instead. Committing all data from WAL to db file is called
|
||||
* a "checkpoint" and is done automatically (by default) when the WAL file reaches a threshold
|
||||
* size of 1000 pages.
|
||||
* More here: https://sqlite.org/wal.html
|
||||
*
|
||||
* This helper will force checkpoint on RKStorage, if Next storage file does not exists yet.
|
||||
*/
|
||||
public static void verifyAndForceSqliteCheckpoint(Context ctx) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
Log.i("AsyncStorage_Next", "SQLite checkpoint not required on this API version.");
|
||||
}
|
||||
|
||||
File nextStorageFile = ctx.getDatabasePath("AsyncStorage");
|
||||
File currentStorageFile = ctx.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME);
|
||||
boolean isCheckpointRequired = !nextStorageFile.exists() && currentStorageFile.exists();
|
||||
if (!isCheckpointRequired) {
|
||||
Log.i("AsyncStorage_Next", "SQLite checkpoint not required.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ReactDatabaseSupplier supplier = ReactDatabaseSupplier.getInstance(ctx);
|
||||
supplier.get().rawQuery("PRAGMA wal_checkpoint", null).close();
|
||||
supplier.closeDatabase();
|
||||
Log.i("AsyncStorage_Next", "Forcing SQLite checkpoint successful.");
|
||||
} catch (Exception e) {
|
||||
Log.w("AsyncStorage_Next", "Could not force checkpoint on RKStorage, the Next storage might not migrate the data properly: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
/**
|
||||
* Helper class for database errors.
|
||||
*/
|
||||
public class AsyncStorageErrorUtil {
|
||||
|
||||
/**
|
||||
* Create Error object to be passed back to the JS callback.
|
||||
*/
|
||||
/* package */ static WritableMap getError(@Nullable String key, String errorMessage) {
|
||||
WritableMap errorMap = Arguments.createMap();
|
||||
errorMap.putString("message", errorMessage);
|
||||
if (key != null) {
|
||||
errorMap.putString("key", key);
|
||||
}
|
||||
return errorMap;
|
||||
}
|
||||
|
||||
/* package */ static WritableMap getInvalidKeyError(@Nullable String key) {
|
||||
return getError(key, "Invalid key");
|
||||
}
|
||||
|
||||
/* package */ static WritableMap getInvalidValueError(@Nullable String key) {
|
||||
return getError(key, "Invalid Value");
|
||||
}
|
||||
|
||||
/* package */ static WritableMap getDBError(@Nullable String key) {
|
||||
return getError(key, "Database Error");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// A utility class that migrates a scoped AsyncStorage database to RKStorage.
|
||||
// This utility only runs if the RKStorage file has not been created yet.
|
||||
public class AsyncStorageExpoMigration {
|
||||
static final String LOG_TAG = "AsyncStorageExpoMigration";
|
||||
|
||||
public static void migrate(Context context) {
|
||||
// Only migrate if the default async storage file does not exist.
|
||||
if (isAsyncStorageDatabaseCreated(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<File> expoDatabases = getExpoDatabases(context);
|
||||
|
||||
File expoDatabase = getLastModifiedFile(expoDatabases);
|
||||
|
||||
if (expoDatabase == null) {
|
||||
Log.v(LOG_TAG, "No scoped database found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create the storage file
|
||||
ReactDatabaseSupplier.getInstance(context).get();
|
||||
copyFile(new FileInputStream(expoDatabase), new FileOutputStream(context.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME)));
|
||||
Log.v(LOG_TAG, "Migrated most recently modified database " + expoDatabase.getName() + " to RKStorage");
|
||||
} catch (Exception e) {
|
||||
Log.v(LOG_TAG, "Failed to migrate scoped database " + expoDatabase.getName());
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
for (File file : expoDatabases) {
|
||||
if (file.delete()) {
|
||||
Log.v(LOG_TAG, "Deleted scoped database " + file.getName());
|
||||
} else {
|
||||
Log.v(LOG_TAG, "Failed to delete scoped database " + file.getName());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.v(LOG_TAG, "Completed the scoped AsyncStorage migration");
|
||||
}
|
||||
|
||||
private static boolean isAsyncStorageDatabaseCreated(Context context) {
|
||||
return context.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME).exists();
|
||||
}
|
||||
|
||||
// Find all database files that the user may have created while using Expo.
|
||||
private static ArrayList<File> getExpoDatabases(Context context) {
|
||||
ArrayList<File> scopedDatabases = new ArrayList<>();
|
||||
try {
|
||||
File databaseDirectory = context.getDatabasePath("noop").getParentFile();
|
||||
File[] directoryListing = databaseDirectory.listFiles();
|
||||
if (directoryListing != null) {
|
||||
for (File child : directoryListing) {
|
||||
// Find all databases matching the Expo scoped key, and skip any database journals.
|
||||
if (child.getName().startsWith("RKStorage-scoped-experience-") && !child.getName().endsWith("-journal")) {
|
||||
scopedDatabases.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Just in case anything happens catch and print, file system rules can tend to be different across vendors.
|
||||
e.printStackTrace();
|
||||
}
|
||||
return scopedDatabases;
|
||||
}
|
||||
|
||||
// Returns the most recently modified file.
|
||||
// If a user publishes an app with Expo, then changes the slug
|
||||
// and publishes again, a new database will be created.
|
||||
// We want to select the most recent database and migrate it to RKStorage.
|
||||
private static File getLastModifiedFile(ArrayList<File> files) {
|
||||
if (files.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
long lastMod = -1;
|
||||
File lastModFile = null;
|
||||
for (File child : files) {
|
||||
long modTime = getLastModifiedTimeInMillis(child);
|
||||
if (modTime > lastMod) {
|
||||
lastMod = modTime;
|
||||
lastModFile = child;
|
||||
}
|
||||
}
|
||||
if (lastModFile != null) {
|
||||
return lastModFile;
|
||||
}
|
||||
|
||||
return files.get(0);
|
||||
}
|
||||
|
||||
private static long getLastModifiedTimeInMillis(File file) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return getLastModifiedTimeFromBasicFileAttrs(file);
|
||||
} else {
|
||||
return file.lastModified();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private static long getLastModifiedTimeFromBasicFileAttrs(File file) {
|
||||
try {
|
||||
return Files.readAttributes(file.toPath(), BasicFileAttributes.class).creationTime().toMillis();
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyFile(FileInputStream fromFile, FileOutputStream toFile) throws IOException {
|
||||
FileChannel fromChannel = null;
|
||||
FileChannel toChannel = null;
|
||||
try {
|
||||
fromChannel = fromFile.getChannel();
|
||||
toChannel = toFile.getChannel();
|
||||
fromChannel.transferTo(0, fromChannel.size(), toChannel);
|
||||
} finally {
|
||||
try {
|
||||
if (fromChannel != null) {
|
||||
fromChannel.close();
|
||||
}
|
||||
} finally {
|
||||
if (toChannel != null) {
|
||||
toChannel.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,424 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.GuardedAsyncTask;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.modules.common.ModuleDataCleaner;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@ReactModule(name = AsyncStorageModule.NAME)
|
||||
public final class AsyncStorageModule
|
||||
extends ReactContextBaseJavaModule implements ModuleDataCleaner.Cleanable, LifecycleEventListener {
|
||||
|
||||
// changed name to not conflict with AsyncStorage from RN repo
|
||||
public static final String NAME = "RNC_AsyncSQLiteDBStorage";
|
||||
|
||||
// SQL variable number limit, defined by SQLITE_LIMIT_VARIABLE_NUMBER:
|
||||
// https://raw.githubusercontent.com/android/platform_external_sqlite/master/dist/sqlite3.c
|
||||
private static final int MAX_SQL_KEYS = 999;
|
||||
|
||||
private ReactDatabaseSupplier mReactDatabaseSupplier;
|
||||
private boolean mShuttingDown = false;
|
||||
|
||||
private final SerialExecutor executor;
|
||||
|
||||
public AsyncStorageModule(ReactApplicationContext reactContext) {
|
||||
this(
|
||||
reactContext,
|
||||
BuildConfig.AsyncStorage_useDedicatedExecutor
|
||||
? Executors.newSingleThreadExecutor()
|
||||
: AsyncTask.THREAD_POOL_EXECUTOR
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AsyncStorageModule(ReactApplicationContext reactContext, Executor executor) {
|
||||
super(reactContext);
|
||||
// The migration MUST run before the AsyncStorage database is created for the first time.
|
||||
AsyncStorageExpoMigration.migrate(reactContext);
|
||||
|
||||
this.executor = new SerialExecutor(executor);
|
||||
reactContext.addLifecycleEventListener(this);
|
||||
// Creating the database MUST happen after the migration.
|
||||
mReactDatabaseSupplier = ReactDatabaseSupplier.getInstance(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
mShuttingDown = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCatalystInstanceDestroy() {
|
||||
mShuttingDown = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSensitiveData() {
|
||||
// Clear local storage. If fails, crash, since the app is potentially in a bad state and could
|
||||
// cause a privacy violation. We're still not recovering from this well, but at least the error
|
||||
// will be reported to the server.
|
||||
mReactDatabaseSupplier.clearAndCloseDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostResume() {}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
// ensure we close database when activity is destroyed
|
||||
mReactDatabaseSupplier.closeDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of keys, this returns a map of (key, value) pairs for the keys found, and
|
||||
* (key, null) for the keys that haven't been found.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void multiGet(final ReadableArray keys, final Callback callback) {
|
||||
if (keys == null) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getInvalidKeyError(null), null);
|
||||
return;
|
||||
}
|
||||
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null), null);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] columns = {ReactDatabaseSupplier.KEY_COLUMN, ReactDatabaseSupplier.VALUE_COLUMN};
|
||||
HashSet<String> keysRemaining = new HashSet<>();
|
||||
WritableArray data = Arguments.createArray();
|
||||
for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
|
||||
int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
|
||||
Cursor cursor = mReactDatabaseSupplier.get().query(
|
||||
ReactDatabaseSupplier.TABLE_CATALYST,
|
||||
columns,
|
||||
AsyncLocalStorageUtil.buildKeySelection(keyCount),
|
||||
AsyncLocalStorageUtil.buildKeySelectionArgs(keys, keyStart, keyCount),
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
keysRemaining.clear();
|
||||
try {
|
||||
if (cursor.getCount() != keys.size()) {
|
||||
// some keys have not been found - insert them with null into the final array
|
||||
for (int keyIndex = keyStart; keyIndex < keyStart + keyCount; keyIndex++) {
|
||||
keysRemaining.add(keys.getString(keyIndex));
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
WritableArray row = Arguments.createArray();
|
||||
row.pushString(cursor.getString(0));
|
||||
row.pushString(cursor.getString(1));
|
||||
data.pushArray(row);
|
||||
keysRemaining.remove(cursor.getString(0));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
||||
return;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
for (String key : keysRemaining) {
|
||||
WritableArray row = Arguments.createArray();
|
||||
row.pushString(key);
|
||||
row.pushNull();
|
||||
data.pushArray(row);
|
||||
}
|
||||
keysRemaining.clear();
|
||||
}
|
||||
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts multiple (key, value) pairs. If one or more of the pairs cannot be inserted, this will
|
||||
* return AsyncLocalStorageFailure, but all other pairs will have been inserted.
|
||||
* The insertion will replace conflicting (key, value) pairs.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void multiSet(final ReadableArray keyValueArray, final Callback callback) {
|
||||
if (keyValueArray.size() == 0) {
|
||||
callback.invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = "INSERT OR REPLACE INTO " + ReactDatabaseSupplier.TABLE_CATALYST + " VALUES (?, ?);";
|
||||
SQLiteStatement statement = mReactDatabaseSupplier.get().compileStatement(sql);
|
||||
WritableMap error = null;
|
||||
try {
|
||||
mReactDatabaseSupplier.get().beginTransaction();
|
||||
for (int idx=0; idx < keyValueArray.size(); idx++) {
|
||||
if (keyValueArray.getArray(idx).size() != 2) {
|
||||
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||
return;
|
||||
}
|
||||
if (keyValueArray.getArray(idx).getString(0) == null) {
|
||||
error = AsyncStorageErrorUtil.getInvalidKeyError(null);
|
||||
return;
|
||||
}
|
||||
if (keyValueArray.getArray(idx).getString(1) == null) {
|
||||
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
statement.clearBindings();
|
||||
statement.bindString(1, keyValueArray.getArray(idx).getString(0));
|
||||
statement.bindString(2, keyValueArray.getArray(idx).getString(1));
|
||||
statement.execute();
|
||||
}
|
||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
mReactDatabaseSupplier.get().endTransaction();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
if (error == null) {
|
||||
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error != null) {
|
||||
callback.invoke(error);
|
||||
} else {
|
||||
callback.invoke();
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all rows of the keys given.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void multiRemove(final ReadableArray keys, final Callback callback) {
|
||||
if (keys.size() == 0) {
|
||||
callback.invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
|
||||
WritableMap error = null;
|
||||
try {
|
||||
mReactDatabaseSupplier.get().beginTransaction();
|
||||
for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
|
||||
int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
|
||||
mReactDatabaseSupplier.get().delete(
|
||||
ReactDatabaseSupplier.TABLE_CATALYST,
|
||||
AsyncLocalStorageUtil.buildKeySelection(keyCount),
|
||||
AsyncLocalStorageUtil.buildKeySelectionArgs(keys, keyStart, keyCount));
|
||||
}
|
||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
mReactDatabaseSupplier.get().endTransaction();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
if (error == null) {
|
||||
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error != null) {
|
||||
callback.invoke(error);
|
||||
} else {
|
||||
callback.invoke();
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of (key, value) pairs, this will merge the given values with the stored values
|
||||
* of the given keys, if they exist.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void multiMerge(final ReadableArray keyValueArray, final Callback callback) {
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
WritableMap error = null;
|
||||
try {
|
||||
mReactDatabaseSupplier.get().beginTransaction();
|
||||
for (int idx = 0; idx < keyValueArray.size(); idx++) {
|
||||
if (keyValueArray.getArray(idx).size() != 2) {
|
||||
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyValueArray.getArray(idx).getString(0) == null) {
|
||||
error = AsyncStorageErrorUtil.getInvalidKeyError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyValueArray.getArray(idx).getString(1) == null) {
|
||||
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AsyncLocalStorageUtil.mergeImpl(
|
||||
mReactDatabaseSupplier.get(),
|
||||
keyValueArray.getArray(idx).getString(0),
|
||||
keyValueArray.getArray(idx).getString(1))) {
|
||||
error = AsyncStorageErrorUtil.getDBError(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
mReactDatabaseSupplier.get().endTransaction();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
if (error == null) {
|
||||
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error != null) {
|
||||
callback.invoke(error);
|
||||
} else {
|
||||
callback.invoke();
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the database.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void clear(final Callback callback) {
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!mReactDatabaseSupplier.ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mReactDatabaseSupplier.clear();
|
||||
callback.invoke();
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all keys from the database.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void getAllKeys(final Callback callback) {
|
||||
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
if (!ensureDatabase()) {
|
||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null), null);
|
||||
return;
|
||||
}
|
||||
WritableArray data = Arguments.createArray();
|
||||
String[] columns = {ReactDatabaseSupplier.KEY_COLUMN};
|
||||
Cursor cursor = mReactDatabaseSupplier.get()
|
||||
.query(ReactDatabaseSupplier.TABLE_CATALYST, columns, null, null, null, null, null);
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
data.pushString(cursor.getString(0));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
||||
return;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}.executeOnExecutor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the database is open for reads and writes.
|
||||
*/
|
||||
private boolean ensureDatabase() {
|
||||
return !mShuttingDown && mReactDatabaseSupplier.ensureDatabase();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import android.util.Log;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AsyncStoragePackage implements ReactPackage {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
|
||||
List<NativeModule> moduleList = new ArrayList<>(1);
|
||||
|
||||
if (BuildConfig.AsyncStorage_useNextStorage) {
|
||||
try {
|
||||
Class storageClass = Class.forName("com.reactnativecommunity.asyncstorage.next.StorageModule");
|
||||
NativeModule inst = (NativeModule) storageClass.getDeclaredConstructor(new Class[]{ReactContext.class}).newInstance(reactContext);
|
||||
moduleList.add(inst);
|
||||
AsyncLocalStorageUtil.verifyAndForceSqliteCheckpoint(reactContext);
|
||||
} catch (Exception e) {
|
||||
String message = "Something went wrong when initializing module:"
|
||||
+ "\n"
|
||||
+ e.getCause().getClass()
|
||||
+ "\n"
|
||||
+ "Cause:" + e.getCause().getLocalizedMessage();
|
||||
Log.e("AsyncStorage_Next", message);
|
||||
}
|
||||
} else {
|
||||
moduleList.add(new AsyncStorageModule(reactContext));
|
||||
}
|
||||
|
||||
return moduleList;
|
||||
}
|
||||
|
||||
// Deprecated in RN 0.47
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Database supplier of the database used by react native. This creates, opens and deletes the
|
||||
* database as necessary.
|
||||
*/
|
||||
public class ReactDatabaseSupplier extends SQLiteOpenHelper {
|
||||
|
||||
// VisibleForTesting
|
||||
public static final String DATABASE_NAME = "RKStorage";
|
||||
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final int SLEEP_TIME_MS = 30;
|
||||
|
||||
static final String TABLE_CATALYST = "catalystLocalStorage";
|
||||
static final String KEY_COLUMN = "key";
|
||||
static final String VALUE_COLUMN = "value";
|
||||
|
||||
static final String VERSION_TABLE_CREATE =
|
||||
"CREATE TABLE " + TABLE_CATALYST + " (" +
|
||||
KEY_COLUMN + " TEXT PRIMARY KEY, " +
|
||||
VALUE_COLUMN + " TEXT NOT NULL" +
|
||||
")";
|
||||
|
||||
private static @Nullable ReactDatabaseSupplier sReactDatabaseSupplierInstance;
|
||||
|
||||
private Context mContext;
|
||||
private @Nullable SQLiteDatabase mDb;
|
||||
private long mMaximumDatabaseSize = BuildConfig.AsyncStorage_db_size * 1024L * 1024L;
|
||||
|
||||
private ReactDatabaseSupplier(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static ReactDatabaseSupplier getInstance(Context context) {
|
||||
if (sReactDatabaseSupplierInstance == null) {
|
||||
sReactDatabaseSupplierInstance = new ReactDatabaseSupplier(context.getApplicationContext());
|
||||
}
|
||||
return sReactDatabaseSupplierInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(VERSION_TABLE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion != newVersion) {
|
||||
deleteDatabase();
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the database exists and is open.
|
||||
*/
|
||||
/* package */ synchronized boolean ensureDatabase() {
|
||||
if (mDb != null && mDb.isOpen()) {
|
||||
return true;
|
||||
}
|
||||
// Sometimes retrieving the database fails. We do 2 retries: first without database deletion
|
||||
// and then with deletion.
|
||||
SQLiteException lastSQLiteException = null;
|
||||
for (int tries = 0; tries < 2; tries++) {
|
||||
try {
|
||||
if (tries > 0) {
|
||||
deleteDatabase();
|
||||
}
|
||||
mDb = getWritableDatabase();
|
||||
break;
|
||||
} catch (SQLiteException e) {
|
||||
lastSQLiteException = e;
|
||||
}
|
||||
// Wait before retrying.
|
||||
try {
|
||||
Thread.sleep(SLEEP_TIME_MS);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (mDb == null) {
|
||||
throw lastSQLiteException;
|
||||
}
|
||||
// This is a sane limit to protect the user from the app storing too much data in the database.
|
||||
// This also protects the database from filling up the disk cache and becoming malformed
|
||||
// (endTransaction() calls will throw an exception, not rollback, and leave the db malformed).
|
||||
mDb.setMaximumSize(mMaximumDatabaseSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open the database.
|
||||
*/
|
||||
public synchronized SQLiteDatabase get() {
|
||||
ensureDatabase();
|
||||
return mDb;
|
||||
}
|
||||
|
||||
public synchronized void clearAndCloseDatabase() throws RuntimeException {
|
||||
try {
|
||||
clear();
|
||||
closeDatabase();
|
||||
FLog.d(ReactConstants.TAG, "Cleaned " + DATABASE_NAME);
|
||||
} catch (Exception e) {
|
||||
// Clearing the database has failed, delete it instead.
|
||||
if (deleteDatabase()) {
|
||||
FLog.d(ReactConstants.TAG, "Deleted Local Database " + DATABASE_NAME);
|
||||
return;
|
||||
}
|
||||
// Everything failed, throw
|
||||
throw new RuntimeException("Clearing and deleting database " + DATABASE_NAME + " failed");
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ synchronized void clear() {
|
||||
get().delete(TABLE_CATALYST, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum size the database will grow to. The maximum size cannot
|
||||
* be set below the current size.
|
||||
*/
|
||||
public synchronized void setMaximumSize(long size) {
|
||||
mMaximumDatabaseSize = size;
|
||||
if (mDb != null) {
|
||||
mDb.setMaximumSize(mMaximumDatabaseSize);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean deleteDatabase() {
|
||||
closeDatabase();
|
||||
return mContext.deleteDatabase(DATABASE_NAME);
|
||||
}
|
||||
|
||||
public synchronized void closeDatabase() {
|
||||
if (mDb != null && mDb.isOpen()) {
|
||||
mDb.close();
|
||||
mDb = null;
|
||||
}
|
||||
}
|
||||
|
||||
// For testing purposes only!
|
||||
public static void deleteInstance() {
|
||||
sReactDatabaseSupplierInstance = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.reactnativecommunity.asyncstorage;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Detox is using this implementation detail in its environment setup,
|
||||
* so in order for Next storage to work, this class has been made public
|
||||
*
|
||||
* Adapted from https://android.googlesource.com/platform/frameworks/base.git/+/1488a3a19d4681a41fb45570c15e14d99db1cb66/core/java/android/os/AsyncTask.java#237
|
||||
*/
|
||||
public class SerialExecutor implements Executor {
|
||||
private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
|
||||
private Runnable mActive;
|
||||
private final Executor executor;
|
||||
|
||||
public SerialExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public synchronized void execute(final Runnable r) {
|
||||
mTasks.offer(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (mActive == null) {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
synchronized void scheduleNext() {
|
||||
if ((mActive = mTasks.poll()) != null) {
|
||||
executor.execute(mActive);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.reactnativecommunity.asyncstorage.next
|
||||
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
fun ReadableArray.toEntryList(): List<Entry> {
|
||||
val list = mutableListOf<Entry>()
|
||||
for (keyValue in this.toArrayList()) {
|
||||
if (keyValue !is ArrayList<*> || keyValue.size != 2) {
|
||||
throw AsyncStorageError.invalidKeyValueFormat()
|
||||
}
|
||||
val key = keyValue[0]
|
||||
val value = keyValue[1]
|
||||
|
||||
if (key !is String) {
|
||||
when (key) {
|
||||
null -> throw AsyncStorageError.keyIsNull()
|
||||
else -> throw AsyncStorageError.keyNotString()
|
||||
}
|
||||
}
|
||||
|
||||
if (value !is String) {
|
||||
throw AsyncStorageError.valueNotString(key)
|
||||
}
|
||||
|
||||
list.add(Entry(key, value))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun ReadableArray.toKeyList(): List<String> {
|
||||
val list = this.toArrayList()
|
||||
|
||||
for (item in list) {
|
||||
if (item !is String) {
|
||||
throw AsyncStorageError.keyNotString()
|
||||
}
|
||||
}
|
||||
return list as List<String>
|
||||
}
|
||||
|
||||
fun List<Entry>.toKeyValueArgument(): ReadableArray {
|
||||
val args = Arguments.createArray()
|
||||
|
||||
for (entry in this) {
|
||||
val keyValue = Arguments.createArray()
|
||||
keyValue.pushString(entry.key)
|
||||
keyValue.pushString(entry.value)
|
||||
args.pushArray(keyValue)
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
fun String?.isValidJson(): Boolean {
|
||||
if (this == null) return false
|
||||
|
||||
return try {
|
||||
JSONObject(this)
|
||||
true
|
||||
} catch (e: JSONException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun JSONObject.mergeWith(newObject: JSONObject): JSONObject {
|
||||
|
||||
val keys = newObject.keys()
|
||||
val mergedObject = JSONObject(this.toString())
|
||||
|
||||
while (keys.hasNext()) {
|
||||
val key = keys.next()
|
||||
val curValue = this.optJSONObject(key)
|
||||
val newValue = newObject.optJSONObject(key)
|
||||
|
||||
if (curValue != null && newValue != null) {
|
||||
val merged = curValue.mergeWith(newValue)
|
||||
mergedObject.put(key, merged)
|
||||
} else {
|
||||
mergedObject.put(key, newObject.get(key))
|
||||
}
|
||||
}
|
||||
return mergedObject
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.reactnativecommunity.asyncstorage.next
|
||||
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.Callback
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
|
||||
internal fun createExceptionHandler(cb: Callback): CoroutineExceptionHandler {
|
||||
return CoroutineExceptionHandler { _, throwable ->
|
||||
val error = Arguments.createMap()
|
||||
if (throwable !is AsyncStorageError) {
|
||||
error.putString(
|
||||
"message", "Unexpected AsyncStorage error: ${throwable.localizedMessage}"
|
||||
)
|
||||
} else {
|
||||
error.putString("message", throwable.errorMessage)
|
||||
}
|
||||
|
||||
cb(error)
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsyncStorageError private constructor(val errorMessage: String) :
|
||||
Throwable(errorMessage) {
|
||||
|
||||
companion object {
|
||||
fun keyIsNull() = AsyncStorageError("Key cannot be null.")
|
||||
|
||||
fun keyNotString() = AsyncStorageError("Provided key is not string. Only strings are supported as storage key.")
|
||||
|
||||
fun valueNotString(key: String?): AsyncStorageError {
|
||||
val detail = if (key == null) "Provided value" else "Value for key \"$key\""
|
||||
return AsyncStorageError("$detail is not a string. Only strings are supported as a value.")
|
||||
}
|
||||
|
||||
fun invalidKeyValueFormat() =
|
||||
AsyncStorageError("Invalid key-value format. Expected a list of [key, value] list.")
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.reactnativecommunity.asyncstorage.next
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.Callback
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.reactnativecommunity.asyncstorage.SerialExecutor
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), CoroutineScope {
|
||||
override fun getName() = "RNC_AsyncSQLiteDBStorage"
|
||||
|
||||
// this executor is not used by the module, but it must exists here due to
|
||||
// Detox relying on this implementation detail to run
|
||||
@VisibleForTesting
|
||||
private val executor = SerialExecutor(Dispatchers.Main.asExecutor())
|
||||
|
||||
override val coroutineContext =
|
||||
Dispatchers.IO + CoroutineName("AsyncStorageScope") + SupervisorJob()
|
||||
|
||||
private val storage = StorageSupplier.getInstance(reactContext)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getStorageInstance(ctx: Context): AsyncStorageAccess {
|
||||
return StorageSupplier.getInstance(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun multiGet(keys: ReadableArray, cb: Callback) {
|
||||
launch(createExceptionHandler(cb)) {
|
||||
val entries = storage.getValues(keys.toKeyList())
|
||||
cb(null, entries.toKeyValueArgument())
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun multiSet(keyValueArray: ReadableArray, cb: Callback) {
|
||||
launch(createExceptionHandler(cb)) {
|
||||
val entries = keyValueArray.toEntryList()
|
||||
storage.setValues(entries)
|
||||
cb(null)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun multiRemove(keys: ReadableArray, cb: Callback) {
|
||||
launch(createExceptionHandler(cb)) {
|
||||
storage.removeValues(keys.toKeyList())
|
||||
cb(null)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun multiMerge(keyValueArray: ReadableArray, cb: Callback) {
|
||||
launch(createExceptionHandler(cb)) {
|
||||
val entries = keyValueArray.toEntryList()
|
||||
storage.mergeValues(entries)
|
||||
cb(null)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun getAllKeys(cb: Callback) {
|
||||
launch(createExceptionHandler(cb)) {
|
||||
val keys = storage.getKeys()
|
||||
val result = Arguments.createArray()
|
||||
keys.forEach { result.pushString(it) }
|
||||
cb.invoke(null, result)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun clear(cb: Callback) {
|
||||
launch(createExceptionHandler(cb)) {
|
||||
storage.clear()
|
||||
cb(null)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package com.reactnativecommunity.asyncstorage.next
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Database
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.Query
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import org.json.JSONObject
|
||||
|
||||
private const val DATABASE_VERSION = 2
|
||||
private const val DATABASE_NAME = "AsyncStorage"
|
||||
private const val TABLE_NAME = "Storage"
|
||||
private const val COLUMN_KEY = "key"
|
||||
private const val COLUMN_VALUE = "value"
|
||||
|
||||
|
||||
@Entity(tableName = TABLE_NAME)
|
||||
data class Entry(
|
||||
@PrimaryKey @ColumnInfo(name = COLUMN_KEY) val key: String,
|
||||
@ColumnInfo(name = COLUMN_VALUE) val value: String?
|
||||
)
|
||||
|
||||
@Dao
|
||||
internal interface StorageDao {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM $TABLE_NAME WHERE `$COLUMN_KEY` IN (:keys)")
|
||||
suspend fun getValues(keys: List<String>): List<Entry>
|
||||
|
||||
@Transaction
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun setValues(entries: List<Entry>)
|
||||
|
||||
@Transaction
|
||||
@Query("DELETE FROM $TABLE_NAME WHERE `$COLUMN_KEY` in (:keys)")
|
||||
suspend fun removeValues(keys: List<String>)
|
||||
|
||||
@Transaction
|
||||
suspend fun mergeValues(entries: List<Entry>) {
|
||||
val currentDbEntries = getValues(entries.map { it.key })
|
||||
val newEntries = mutableListOf<Entry>()
|
||||
|
||||
entries.forEach { newEntry ->
|
||||
val oldEntry = currentDbEntries.find { it.key == newEntry.key }
|
||||
if (oldEntry?.value == null) {
|
||||
newEntries.add(newEntry)
|
||||
} else if (!oldEntry.value.isValidJson() || !newEntry.value.isValidJson()) {
|
||||
newEntries.add(newEntry)
|
||||
} else {
|
||||
val newValue =
|
||||
JSONObject(oldEntry.value).mergeWith(JSONObject(newEntry.value)).toString()
|
||||
newEntries.add(newEntry.copy(value = newValue))
|
||||
}
|
||||
}
|
||||
setValues(newEntries)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT `$COLUMN_KEY` FROM $TABLE_NAME")
|
||||
suspend fun getKeys(): List<String>
|
||||
|
||||
@Transaction
|
||||
@Query("DELETE FROM $TABLE_NAME")
|
||||
suspend fun clear()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Previous version of AsyncStorage is violating the SQL standard (based on bug in SQLite),
|
||||
* where PrimaryKey ('key' column) should never be null (https://www.sqlite.org/lang_createtable.html#the_primary_key).
|
||||
* Because of that, we cannot reuse the old DB, because ROOM is guarded against that case (won't compile).
|
||||
*
|
||||
* In order to work around this, two steps are necessary:
|
||||
* - Room DB pre-population from the old database file (https://developer.android.com/training/data-storage/room/prepopulate#from-asset)
|
||||
* - Version migration, so that we can mark 'key' column as NOT-NULL
|
||||
*
|
||||
* This migration will happens only once, when developer enable this feature (when DB is still not created).
|
||||
*/
|
||||
@Suppress("ClassName")
|
||||
private object MIGRATION_TO_NEXT : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
val oldTableName = "catalystLocalStorage" // from ReactDatabaseSupplier
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`${COLUMN_KEY}` TEXT NOT NULL, `${COLUMN_VALUE}` TEXT, PRIMARY KEY(`${COLUMN_KEY}`));")
|
||||
// even if the old AsyncStorage has checks for not nullable keys
|
||||
// make sure we don't copy any, to not fail migration
|
||||
database.execSQL("DELETE FROM $oldTableName WHERE `${COLUMN_KEY}` IS NULL")
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO $TABLE_NAME (`${COLUMN_KEY}`, `${COLUMN_VALUE}`)
|
||||
SELECT `${COLUMN_KEY}`, `${COLUMN_VALUE}`
|
||||
FROM $oldTableName;
|
||||
""".trimIndent()
|
||||
)
|
||||
Log.e("AsyncStorage_Next", "Migration to Next storage completed.")
|
||||
}
|
||||
}
|
||||
|
||||
@Database(entities = [Entry::class], version = DATABASE_VERSION, exportSchema = true)
|
||||
internal abstract class StorageDb : RoomDatabase() {
|
||||
abstract fun storage(): StorageDao
|
||||
|
||||
companion object {
|
||||
private var instance: StorageDb? = null
|
||||
|
||||
fun getDatabase(context: Context): StorageDb {
|
||||
var inst = instance
|
||||
if (inst != null) {
|
||||
return inst
|
||||
}
|
||||
synchronized(this) {
|
||||
val oldDbFile = context.getDatabasePath("RKStorage")
|
||||
val db = Room.databaseBuilder(
|
||||
context, StorageDb::class.java, DATABASE_NAME
|
||||
)
|
||||
if (oldDbFile.exists()) {
|
||||
// migrate data from old database, if it exists
|
||||
db.createFromFile(oldDbFile).addMigrations(MIGRATION_TO_NEXT)
|
||||
}
|
||||
inst = db.build()
|
||||
instance = inst
|
||||
return instance!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AsyncStorageAccess {
|
||||
suspend fun getValues(keys: List<String>): List<Entry>
|
||||
suspend fun setValues(entries: List<Entry>)
|
||||
suspend fun removeValues(keys: List<String>)
|
||||
suspend fun getKeys(): List<String>
|
||||
suspend fun clear()
|
||||
suspend fun mergeValues(entries: List<Entry>)
|
||||
}
|
||||
|
||||
class StorageSupplier internal constructor(db: StorageDb) : AsyncStorageAccess {
|
||||
companion object {
|
||||
fun getInstance(ctx: Context): AsyncStorageAccess {
|
||||
return StorageSupplier(StorageDb.getDatabase(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
private val access = db.storage()
|
||||
|
||||
override suspend fun getValues(keys: List<String>) = access.getValues(keys)
|
||||
override suspend fun setValues(entries: List<Entry>) = access.setValues(entries)
|
||||
override suspend fun removeValues(keys: List<String>) = access.removeValues(keys)
|
||||
override suspend fun mergeValues(entries: List<Entry>) = access.mergeValues(entries)
|
||||
override suspend fun getKeys() = access.getKeys()
|
||||
override suspend fun clear() = access.clear()
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.reactnativecommunity.asyncstorage.next
|
||||
|
||||
import com.facebook.react.bridge.JavaOnlyArray
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.BlockJUnit4ClassRunner
|
||||
|
||||
@RunWith(BlockJUnit4ClassRunner::class)
|
||||
class ArgumentHelpersTest {
|
||||
|
||||
@Test
|
||||
fun transformsArgumentsToEntryList() {
|
||||
val args = JavaOnlyArray.of(
|
||||
arrayListOf("key1", "value1"),
|
||||
arrayListOf("key2", "value2"),
|
||||
arrayListOf("key3", "value3")
|
||||
)
|
||||
assertThat(args.toEntryList()).isEqualTo(
|
||||
listOf(
|
||||
Entry("key1", "value1"),
|
||||
Entry("key2", "value2"),
|
||||
Entry("key3", "value3"),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transfersArgumentsToKeyList() {
|
||||
val keyList = listOf("key1", "key2", "key3")
|
||||
val args = keyList.toReadableArray()
|
||||
assertThat(args.toKeyList()).isEqualTo(keyList)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun throwsIfArgumentsNotValidFormat() {
|
||||
val invalid = arrayListOf("invalid")
|
||||
val args = JavaOnlyArray.of(invalid)
|
||||
val error = assertThrows(AsyncStorageError::class.java) {
|
||||
args.toEntryList()
|
||||
}
|
||||
|
||||
assertThat(error is AsyncStorageError).isTrue()
|
||||
assertThat(error).hasMessageThat()
|
||||
.isEqualTo("Invalid key-value format. Expected a list of [key, value] list.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun throwsIfArgumentKeyIsNullOrNotString() {
|
||||
val argsInvalidNull = JavaOnlyArray.of(arrayListOf(null, "invalid"))
|
||||
val errorArgsInvalidNull = assertThrows(AsyncStorageError::class.java) {
|
||||
argsInvalidNull.toEntryList()
|
||||
}
|
||||
assertThat(errorArgsInvalidNull is AsyncStorageError).isTrue()
|
||||
assertThat(errorArgsInvalidNull).hasMessageThat().isEqualTo("Key cannot be null.")
|
||||
|
||||
val notStringArgs = JavaOnlyArray.of(arrayListOf(123, "invalid"))
|
||||
val errorNotString = assertThrows(AsyncStorageError::class.java) {
|
||||
notStringArgs.toEntryList()
|
||||
}
|
||||
assertThat(errorNotString is AsyncStorageError).isTrue()
|
||||
assertThat(errorNotString).hasMessageThat()
|
||||
.isEqualTo("Provided key is not string. Only strings are supported as storage key.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun throwsIfArgumentValueNotString() {
|
||||
val invalidArgs = JavaOnlyArray.of(arrayListOf("my_key", 666))
|
||||
val error = assertThrows(AsyncStorageError::class.java) {
|
||||
invalidArgs.toEntryList()
|
||||
}
|
||||
assertThat(error is AsyncStorageError).isTrue()
|
||||
assertThat(error).hasMessageThat()
|
||||
.isEqualTo("Value for key \"my_key\" is not a string. Only strings are supported as a value.")
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Any?>.toReadableArray(): ReadableArray {
|
||||
val arr = JavaOnlyArray()
|
||||
forEach {
|
||||
when (it) {
|
||||
null -> arr.pushNull()
|
||||
is Boolean -> arr.pushBoolean(it)
|
||||
is Double -> arr.pushDouble(it)
|
||||
is Int -> arr.pushInt(it)
|
||||
is String -> arr.pushString(it)
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package com.reactnativecommunity.asyncstorage.next
|
||||
|
||||
import androidx.room.Room
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.json.JSONObject
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.random.Random
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AsyncStorageAccessTest {
|
||||
private lateinit var asyncStorage: AsyncStorageAccess
|
||||
private lateinit var database: StorageDb
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
database = Room.inMemoryDatabaseBuilder(
|
||||
InstrumentationRegistry.getInstrumentation().context, StorageDb::class.java
|
||||
).allowMainThreadQueries().build()
|
||||
asyncStorage = StorageSupplier(database)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
database.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun performsBasicGetSetRemoveOperations() = runBlocking {
|
||||
val entriesCount = 10
|
||||
val entries = createRandomEntries(entriesCount)
|
||||
val keys = entries.map { it.key }
|
||||
assertThat(asyncStorage.getValues(keys)).hasSize(0)
|
||||
asyncStorage.setValues(entries)
|
||||
assertThat(asyncStorage.getValues(keys)).hasSize(entriesCount)
|
||||
val indicesToRemove = (1..4).map { Random.nextInt(0, entriesCount) }.distinct()
|
||||
val toRemove = entries.filterIndexed { index, _ -> indicesToRemove.contains(index) }
|
||||
asyncStorage.removeValues(toRemove.map { it.key })
|
||||
val currentEntries = asyncStorage.getValues(keys)
|
||||
assertThat(currentEntries).hasSize(entriesCount - toRemove.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readsAllKeysAndClearsDb() = runBlocking {
|
||||
val entries = createRandomEntries(8)
|
||||
val keys = entries.map { it.key }
|
||||
asyncStorage.setValues(entries)
|
||||
val dbKeys = asyncStorage.getKeys()
|
||||
assertThat(dbKeys).isEqualTo(keys)
|
||||
asyncStorage.clear()
|
||||
assertThat(asyncStorage.getValues(keys)).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mergesDeeplyTwoValues() = runBlocking {
|
||||
val initialEntry = Entry("key", VALUE_INITIAL)
|
||||
val overrideEntry = Entry("key", VALUE_OVERRIDES)
|
||||
asyncStorage.setValues(listOf(initialEntry))
|
||||
asyncStorage.mergeValues(listOf(overrideEntry))
|
||||
val current = asyncStorage.getValues(listOf("key"))[0]
|
||||
assertThat(current.value).isEqualTo(VALUE_MERGED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatesExistingValues() = runBlocking {
|
||||
val key = "test_key"
|
||||
val value = "test_value"
|
||||
val entries = listOf(Entry(key, value))
|
||||
assertThat(asyncStorage.getValues(listOf(key))).hasSize(0)
|
||||
asyncStorage.setValues(entries)
|
||||
assertThat(asyncStorage.getValues(listOf(key))).isEqualTo(entries)
|
||||
val modifiedEntries = listOf(Entry(key, "updatedValue"))
|
||||
asyncStorage.setValues(modifiedEntries)
|
||||
assertThat(asyncStorage.getValues(listOf(key))).isEqualTo(modifiedEntries)
|
||||
}
|
||||
|
||||
|
||||
// Test Helpers
|
||||
private fun createRandomEntries(count: Int = Random.nextInt(10)): List<Entry> {
|
||||
val entries = mutableListOf<Entry>()
|
||||
for (i in 0 until count) {
|
||||
entries.add(Entry("key$i", "value$i"))
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
private val VALUE_INITIAL = JSONObject(
|
||||
"""
|
||||
{
|
||||
"key":"value",
|
||||
"key2":"override",
|
||||
"key3":{
|
||||
"key4":"value4",
|
||||
"key6":{
|
||||
"key7":"value7",
|
||||
"key8":"override"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimMargin()
|
||||
).toString()
|
||||
|
||||
private val VALUE_OVERRIDES = JSONObject(
|
||||
"""
|
||||
{
|
||||
"key2":"value2",
|
||||
"key3":{
|
||||
"key5":"value5",
|
||||
"key6":{
|
||||
"key8":"value8"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
).toString()
|
||||
|
||||
|
||||
private val VALUE_MERGED = JSONObject(
|
||||
"""
|
||||
{
|
||||
"key":"value",
|
||||
"key2":"value2",
|
||||
"key3":{
|
||||
"key4":"value4",
|
||||
"key6":{
|
||||
"key7":"value7",
|
||||
"key8":"value8"
|
||||
},
|
||||
"key5":"value5"
|
||||
}
|
||||
}
|
||||
""".trimMargin()
|
||||
).toString()
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
|
||||
// pretty print test results
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
tasks.withType(Test) {
|
||||
testLogging {
|
||||
|
||||
events TestLogEvent.FAILED,
|
||||
TestLogEvent.PASSED,
|
||||
TestLogEvent.SKIPPED,
|
||||
TestLogEvent.STANDARD_OUT
|
||||
exceptionFormat TestExceptionFormat.FULL
|
||||
showExceptions true
|
||||
showCauses true
|
||||
showStackTraces true
|
||||
|
||||
debug {
|
||||
events TestLogEvent.STARTED,
|
||||
TestLogEvent.FAILED,
|
||||
TestLogEvent.PASSED,
|
||||
TestLogEvent.SKIPPED,
|
||||
TestLogEvent.STANDARD_ERROR,
|
||||
TestLogEvent.STANDARD_OUT
|
||||
exceptionFormat TestExceptionFormat.FULL
|
||||
}
|
||||
info.events = debug.events
|
||||
info.exceptionFormat = debug.exceptionFormat
|
||||
|
||||
afterSuite { desc, result ->
|
||||
if (!desc.parent) { // will match the outermost suite
|
||||
def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
|
||||
def startItem = '| ', endItem = ' |'
|
||||
def repeatLength = startItem.length() + output.length() + endItem.length()
|
||||
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTInvalidating.h>
|
||||
|
||||
#import "RNCAsyncStorageDelegate.h"
|
||||
|
||||
/**
|
||||
* A simple, asynchronous, persistent, key-value storage system designed as a
|
||||
* backend to the AsyncStorage JS module, which is modeled after LocalStorage.
|
||||
*
|
||||
* Current implementation stores small values in serialized dictionary and
|
||||
* larger values in separate files. Since we use a serial file queue
|
||||
* `RKFileQueue`, reading/writing from multiple threads should be perceived as
|
||||
* being atomic, unless someone bypasses the `RNCAsyncStorage` API.
|
||||
*
|
||||
* Keys and values must always be strings or an error is returned.
|
||||
*/
|
||||
@interface RNCAsyncStorage : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||
|
||||
@property (nonatomic, weak, nullable) id<RNCAsyncStorageDelegate> delegate;
|
||||
|
||||
@property (nonatomic, assign) BOOL clearOnInvalidate;
|
||||
|
||||
@property (nonatomic, readonly, getter=isValid) BOOL valid;
|
||||
|
||||
// Clear the RNCAsyncStorage data from native code
|
||||
- (void)clearAllData;
|
||||
|
||||
// For clearing data when the bridge may not exist, e.g. when logging out.
|
||||
+ (void)clearAllData;
|
||||
|
||||
// Grab data from the cache. ResponseBlock result array will have an error at position 0, and an
|
||||
// array of arrays at position 1.
|
||||
- (void)multiGet:(NSArray<NSString *> *)keys callback:(RCTResponseSenderBlock)callback;
|
||||
|
||||
// Add multiple key value pairs to the cache.
|
||||
- (void)multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs
|
||||
callback:(RCTResponseSenderBlock)callback;
|
||||
|
||||
// Interface for natively fetching all the keys from the storage data.
|
||||
- (void)getAllKeys:(RCTResponseSenderBlock)callback;
|
||||
|
||||
@end
|
@ -0,0 +1,898 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNCAsyncStorage.h"
|
||||
|
||||
#import <CommonCrypto/CommonCryptor.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1";
|
||||
static NSString *const RCTOldStorageDirectory = @"RNCAsyncLocalStorage_V1";
|
||||
static NSString *const RCTExpoStorageDirectory = @"RCTAsyncLocalStorage";
|
||||
static NSString *const RCTManifestFileName = @"manifest.json";
|
||||
static const NSUInteger RCTInlineValueThreshold = 1024;
|
||||
|
||||
#pragma mark - Static helper functions
|
||||
|
||||
static NSDictionary *RCTErrorForKey(NSString *key)
|
||||
{
|
||||
if (![key isKindOfClass:[NSString class]]) {
|
||||
return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key": key});
|
||||
} else if (key.length < 1) {
|
||||
return RCTMakeAndLogError(
|
||||
@"Invalid key - must be at least one character. Key: ", key, @{@"key": key});
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString *path, NSNumber *isExcluded)
|
||||
{
|
||||
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
||||
|
||||
BOOL isDir;
|
||||
BOOL exists = [fileManager fileExistsAtPath:path isDirectory:&isDir];
|
||||
BOOL success = false;
|
||||
|
||||
if (isDir && exists) {
|
||||
NSURL *pathUrl = [NSURL fileURLWithPath:path];
|
||||
NSError *error = nil;
|
||||
success = [pathUrl setResourceValue:isExcluded
|
||||
forKey:NSURLIsExcludedFromBackupKey
|
||||
error:&error];
|
||||
|
||||
if (!success) {
|
||||
NSLog(@"Could not exclude AsyncStorage dir from backup %@", error);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> **errors)
|
||||
{
|
||||
if (error && errors) {
|
||||
if (!*errors) {
|
||||
*errors = [NSMutableArray new];
|
||||
}
|
||||
[*errors addObject:error];
|
||||
}
|
||||
}
|
||||
|
||||
static NSArray<NSDictionary *> *RCTMakeErrors(NSArray<id<NSObject>> *results)
|
||||
{
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
for (id object in results) {
|
||||
if ([object isKindOfClass:[NSError class]]) {
|
||||
NSError *error = (NSError *)object;
|
||||
NSDictionary *keyError = RCTMakeError(error.localizedDescription, error, nil);
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
static NSString *RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut)
|
||||
{
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
|
||||
NSError *error;
|
||||
NSStringEncoding encoding;
|
||||
NSString *entryString = [NSString stringWithContentsOfFile:filePath
|
||||
usedEncoding:&encoding
|
||||
error:&error];
|
||||
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
|
||||
|
||||
if (error) {
|
||||
if (errorOut) {
|
||||
*errorOut = RCTMakeError(@"Failed to read storage file.", error, extraData);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (encoding != NSUTF8StringEncoding) {
|
||||
if (errorOut) {
|
||||
*errorOut =
|
||||
RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), extraData);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
return entryString;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// DO NOT USE
|
||||
// This is used internally to migrate data from the old file location to the new one.
|
||||
// Please use `RCTCreateStorageDirectoryPath` instead
|
||||
static NSString *RCTCreateStorageDirectoryPath_deprecated(NSString *storageDir)
|
||||
{
|
||||
NSString *storageDirectoryPath;
|
||||
#if TARGET_OS_TV
|
||||
storageDirectoryPath =
|
||||
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
||||
#else
|
||||
storageDirectoryPath =
|
||||
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
|
||||
#endif
|
||||
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
|
||||
return storageDirectoryPath;
|
||||
}
|
||||
|
||||
static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir)
|
||||
{
|
||||
NSString *storageDirectoryPath = @"";
|
||||
|
||||
#if TARGET_OS_TV
|
||||
storageDirectoryPath =
|
||||
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
||||
#else
|
||||
storageDirectoryPath =
|
||||
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
|
||||
.firstObject;
|
||||
// We should use the "Application Support/[bundleID]" folder for persistent data storage that's
|
||||
// hidden from users
|
||||
storageDirectoryPath = [storageDirectoryPath
|
||||
stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]];
|
||||
#endif
|
||||
|
||||
// Per Apple's docs, all app content in Application Support must be within a subdirectory of the
|
||||
// app's bundle identifier
|
||||
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
|
||||
|
||||
return storageDirectoryPath;
|
||||
}
|
||||
|
||||
static NSString *RCTGetStorageDirectory()
|
||||
{
|
||||
static NSString *storageDirectory = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
storageDirectory = RCTCreateStorageDirectoryPath(RCTStorageDirectory);
|
||||
});
|
||||
return storageDirectory;
|
||||
}
|
||||
|
||||
static NSString *RCTCreateManifestFilePath(NSString *storageDirectory)
|
||||
{
|
||||
return [storageDirectory stringByAppendingPathComponent:RCTManifestFileName];
|
||||
}
|
||||
|
||||
static NSString *RCTGetManifestFilePath()
|
||||
{
|
||||
static NSString *manifestFilePath = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manifestFilePath = RCTCreateManifestFilePath(RCTStorageDirectory);
|
||||
});
|
||||
return manifestFilePath;
|
||||
}
|
||||
|
||||
// Only merges objects - all other types are just clobbered (including arrays)
|
||||
static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
|
||||
{
|
||||
BOOL modified = NO;
|
||||
for (NSString *key in source) {
|
||||
id sourceValue = source[key];
|
||||
id destinationValue = destination[key];
|
||||
if ([sourceValue isKindOfClass:[NSDictionary class]]) {
|
||||
if ([destinationValue isKindOfClass:[NSDictionary class]]) {
|
||||
if ([destinationValue classForCoder] != [NSMutableDictionary class]) {
|
||||
destinationValue = [destinationValue mutableCopy];
|
||||
}
|
||||
if (RCTMergeRecursive(destinationValue, sourceValue)) {
|
||||
destination[key] = destinationValue;
|
||||
modified = YES;
|
||||
}
|
||||
} else {
|
||||
destination[key] = [sourceValue copy];
|
||||
modified = YES;
|
||||
}
|
||||
} else if (![source isEqual:destinationValue]) {
|
||||
destination[key] = [sourceValue copy];
|
||||
modified = YES;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
static dispatch_queue_t RCTGetMethodQueue()
|
||||
{
|
||||
// We want all instances to share the same queue since they will be reading/writing the same
|
||||
// files.
|
||||
static dispatch_queue_t queue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue =
|
||||
dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
static NSCache *RCTGetCache()
|
||||
{
|
||||
// We want all instances to share the same cache since they will be reading/writing the same
|
||||
// files.
|
||||
static NSCache *cache;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
cache = [NSCache new];
|
||||
cache.totalCostLimit = 2 * 1024 * 1024; // 2MB
|
||||
|
||||
#if !TARGET_OS_OSX
|
||||
// Clear cache in the event of a memory warning
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(__unused NSNotification *note) {
|
||||
[cache removeAllObjects];
|
||||
}];
|
||||
#endif // !TARGET_OS_OSX
|
||||
});
|
||||
return cache;
|
||||
}
|
||||
|
||||
static BOOL RCTHasCreatedStorageDirectory = NO;
|
||||
static NSDictionary *RCTDeleteStorageDirectory()
|
||||
{
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error];
|
||||
RCTHasCreatedStorageDirectory = NO;
|
||||
return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil;
|
||||
}
|
||||
|
||||
static NSDate *RCTManifestModificationDate(NSString *manifestFilePath)
|
||||
{
|
||||
NSDictionary *attributes =
|
||||
[[NSFileManager defaultManager] attributesOfItemAtPath:manifestFilePath error:nil];
|
||||
return [attributes fileModificationDate];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an NSException used during Storage Directory Migration.
|
||||
*/
|
||||
static void RCTStorageDirectoryMigrationLogError(NSString *reason, NSError *error)
|
||||
{
|
||||
RCTLogWarn(@"%@: %@", reason, error ? error.description : @"");
|
||||
}
|
||||
|
||||
static void RCTStorageDirectoryCleanupOld(NSString *oldDirectoryPath)
|
||||
{
|
||||
NSError *error;
|
||||
if (![[NSFileManager defaultManager] removeItemAtPath:oldDirectoryPath error:&error]) {
|
||||
RCTStorageDirectoryMigrationLogError(
|
||||
@"Failed to remove old storage directory during migration", error);
|
||||
}
|
||||
}
|
||||
|
||||
static void _createStorageDirectory(NSString *storageDirectory, NSError **error)
|
||||
{
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:storageDirectory
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:error];
|
||||
}
|
||||
|
||||
static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath,
|
||||
NSString *newDirectoryPath,
|
||||
BOOL shouldCleanupOldDirectory)
|
||||
{
|
||||
NSError *error;
|
||||
// Migrate data by copying old storage directory to new storage directory location
|
||||
if (![[NSFileManager defaultManager] copyItemAtPath:oldDirectoryPath
|
||||
toPath:newDirectoryPath
|
||||
error:&error]) {
|
||||
// the new storage directory "Application Support/[bundleID]/RCTAsyncLocalStorage_V1" seems
|
||||
// unable to migrate because folder "Application Support/[bundleID]" doesn't exist.. create
|
||||
// this folder and attempt folder copying again
|
||||
if (error != nil && error.code == 4 &&
|
||||
[newDirectoryPath isEqualToString:RCTGetStorageDirectory()]) {
|
||||
NSError *error = nil;
|
||||
_createStorageDirectory(RCTCreateStorageDirectoryPath(@""), &error);
|
||||
if (error == nil) {
|
||||
RCTStorageDirectoryMigrate(
|
||||
oldDirectoryPath, newDirectoryPath, shouldCleanupOldDirectory);
|
||||
} else {
|
||||
RCTStorageDirectoryMigrationLogError(
|
||||
@"Failed to create storage directory during migration.", error);
|
||||
}
|
||||
} else {
|
||||
RCTStorageDirectoryMigrationLogError(
|
||||
@"Failed to copy old storage directory to new storage directory location during "
|
||||
@"migration",
|
||||
error);
|
||||
}
|
||||
} else if (shouldCleanupOldDirectory) {
|
||||
// If copying succeeds, remove old storage directory
|
||||
RCTStorageDirectoryCleanupOld(oldDirectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which of RCTOldStorageDirectory or RCTExpoStorageDirectory needs to migrated.
|
||||
* If both exist, we remove the least recently modified and return the most recently modified.
|
||||
* Otherwise, this will return the path to whichever directory exists.
|
||||
* If no directory exists, then return nil.
|
||||
*/
|
||||
static NSString *RCTGetStoragePathForMigration()
|
||||
{
|
||||
BOOL isDir;
|
||||
NSString *oldStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTOldStorageDirectory);
|
||||
NSString *expoStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTExpoStorageDirectory);
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL oldStorageDirectoryExists =
|
||||
[fileManager fileExistsAtPath:oldStoragePath isDirectory:&isDir] && isDir;
|
||||
BOOL expoStorageDirectoryExists =
|
||||
[fileManager fileExistsAtPath:expoStoragePath isDirectory:&isDir] && isDir;
|
||||
|
||||
// Check if both the old storage directory and Expo storage directory exist
|
||||
if (oldStorageDirectoryExists && expoStorageDirectoryExists) {
|
||||
// If the old storage has been modified more recently than Expo storage, then clear Expo
|
||||
// storage. Otherwise, clear the old storage.
|
||||
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(oldStoragePath))
|
||||
compare:RCTManifestModificationDate(RCTCreateManifestFilePath(expoStoragePath))] ==
|
||||
NSOrderedDescending) {
|
||||
RCTStorageDirectoryCleanupOld(expoStoragePath);
|
||||
return oldStoragePath;
|
||||
} else {
|
||||
RCTStorageDirectoryCleanupOld(oldStoragePath);
|
||||
return expoStoragePath;
|
||||
}
|
||||
} else if (oldStorageDirectoryExists) {
|
||||
return oldStoragePath;
|
||||
} else if (expoStorageDirectoryExists) {
|
||||
return expoStoragePath;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This check is added to make sure that anyone coming from pre-1.2.2 does not lose cached data.
|
||||
* Check that data is migrated from the old location to the new location
|
||||
* fromStorageDirectory: the directory where the older data lives
|
||||
* toStorageDirectory: the directory where the new data should live
|
||||
* shouldCleanupOldDirectoryAndOverwriteNewDirectory: YES if we should delete the old directory's
|
||||
* contents and overwrite the new directory's contents during the migration to the new directory
|
||||
*/
|
||||
static void
|
||||
RCTStorageDirectoryMigrationCheck(NSString *fromStorageDirectory,
|
||||
NSString *toStorageDirectory,
|
||||
BOOL shouldCleanupOldDirectoryAndOverwriteNewDirectory)
|
||||
{
|
||||
NSError *error;
|
||||
BOOL isDir;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
// If the old directory exists, it means we may need to migrate old data to the new directory
|
||||
if ([fileManager fileExistsAtPath:fromStorageDirectory isDirectory:&isDir] && isDir) {
|
||||
// Check if the new storage directory location already exists
|
||||
if ([fileManager fileExistsAtPath:toStorageDirectory]) {
|
||||
// If new storage location exists, check if the new storage has been modified sooner in
|
||||
// which case we may want to cleanup the old location
|
||||
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(toStorageDirectory))
|
||||
compare:RCTManifestModificationDate(
|
||||
RCTCreateManifestFilePath(fromStorageDirectory))] == 1) {
|
||||
// If new location has been modified more recently, simply clean out old data
|
||||
if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
|
||||
RCTStorageDirectoryCleanupOld(fromStorageDirectory);
|
||||
}
|
||||
} else if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
|
||||
// If old location has been modified more recently, remove new storage and migrate
|
||||
if (![fileManager removeItemAtPath:toStorageDirectory error:&error]) {
|
||||
RCTStorageDirectoryMigrationLogError(
|
||||
@"Failed to remove new storage directory during migration", error);
|
||||
} else {
|
||||
RCTStorageDirectoryMigrate(fromStorageDirectory,
|
||||
toStorageDirectory,
|
||||
shouldCleanupOldDirectoryAndOverwriteNewDirectory);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If new storage location doesn't exist, migrate data
|
||||
RCTStorageDirectoryMigrate(fromStorageDirectory,
|
||||
toStorageDirectory,
|
||||
shouldCleanupOldDirectoryAndOverwriteNewDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RNCAsyncStorage
|
||||
|
||||
@implementation RNCAsyncStorage {
|
||||
BOOL _haveSetup;
|
||||
// The manifest is a dictionary of all keys with small values inlined. Null values indicate
|
||||
// values that are stored in separate files (as opposed to nil values which don't exist). The
|
||||
// manifest is read off disk at startup, and written to disk after all mutations.
|
||||
NSMutableDictionary<NSString *, NSString *> *_manifest;
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Get the path to any old storage directory that needs to be migrated. If multiple exist,
|
||||
// the oldest are removed and the most recently modified is returned.
|
||||
NSString *oldStoragePath = RCTGetStoragePathForMigration();
|
||||
if (oldStoragePath != nil) {
|
||||
// Migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" or
|
||||
// "Documents/.../RCTAsyncLocalStorage" to "Documents/.../RCTAsyncLocalStorage_V1"
|
||||
RCTStorageDirectoryMigrationCheck(
|
||||
oldStoragePath, RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), YES);
|
||||
}
|
||||
|
||||
// Migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to
|
||||
// "Application Support/[bundleID]/RCTAsyncLocalStorage_V1"
|
||||
RCTStorageDirectoryMigrationCheck(RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory),
|
||||
RCTCreateStorageDirectoryPath(RCTStorageDirectory),
|
||||
NO);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return RCTGetMethodQueue();
|
||||
}
|
||||
|
||||
- (void)clearAllData
|
||||
{
|
||||
dispatch_async(RCTGetMethodQueue(), ^{
|
||||
[self->_manifest removeAllObjects];
|
||||
[RCTGetCache() removeAllObjects];
|
||||
RCTDeleteStorageDirectory();
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)clearAllData
|
||||
{
|
||||
dispatch_async(RCTGetMethodQueue(), ^{
|
||||
[RCTGetCache() removeAllObjects];
|
||||
RCTDeleteStorageDirectory();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if (_clearOnInvalidate) {
|
||||
[RCTGetCache() removeAllObjects];
|
||||
RCTDeleteStorageDirectory();
|
||||
}
|
||||
_clearOnInvalidate = NO;
|
||||
[_manifest removeAllObjects];
|
||||
_haveSetup = NO;
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _haveSetup;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (NSString *)_filePathForKey:(NSString *)key
|
||||
{
|
||||
NSString *safeFileName = RCTMD5Hash(key);
|
||||
return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName];
|
||||
}
|
||||
|
||||
- (NSDictionary *)_ensureSetup
|
||||
{
|
||||
RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread");
|
||||
|
||||
#if TARGET_OS_TV
|
||||
RCTLogWarn(
|
||||
@"Persistent storage is not supported on tvOS, your data may be removed at any point.");
|
||||
#endif
|
||||
|
||||
NSError *error = nil;
|
||||
if (!RCTHasCreatedStorageDirectory) {
|
||||
_createStorageDirectory(RCTGetStorageDirectory(), &error);
|
||||
if (error) {
|
||||
return RCTMakeError(@"Failed to create storage directory.", error, nil);
|
||||
}
|
||||
RCTHasCreatedStorageDirectory = YES;
|
||||
}
|
||||
|
||||
if (!_haveSetup) {
|
||||
// iCloud backup exclusion
|
||||
NSNumber *isExcludedFromBackup =
|
||||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTAsyncStorageExcludeFromBackup"];
|
||||
if (isExcludedFromBackup == nil) {
|
||||
// by default, we want to exclude AsyncStorage data from backup
|
||||
isExcludedFromBackup = @YES;
|
||||
}
|
||||
RCTAsyncStorageSetExcludedFromBackup(RCTCreateStorageDirectoryPath(RCTStorageDirectory),
|
||||
isExcludedFromBackup);
|
||||
|
||||
NSDictionary *errorOut = nil;
|
||||
NSString *serialized = RCTReadFile(RCTCreateStorageDirectoryPath(RCTGetManifestFilePath()),
|
||||
RCTManifestFileName,
|
||||
&errorOut);
|
||||
if (!serialized) {
|
||||
if (errorOut) {
|
||||
// We cannot simply create a new manifest in case the file does exist but we have no
|
||||
// access to it. This can happen when data protection is enabled for the app and we
|
||||
// are trying to read the manifest while the device is locked. (The app can be
|
||||
// started by the system even if the device is locked due to e.g. a geofence event.)
|
||||
RCTLogError(
|
||||
@"Could not open the existing manifest, perhaps data protection is "
|
||||
@"enabled?\n\n%@",
|
||||
errorOut);
|
||||
return errorOut;
|
||||
} else {
|
||||
// We can get nil without errors only when the file does not exist.
|
||||
RCTLogTrace(@"Manifest does not exist - creating a new one.\n\n%@", errorOut);
|
||||
_manifest = [NSMutableDictionary new];
|
||||
}
|
||||
} else {
|
||||
_manifest = RCTJSONParseMutable(serialized, &error);
|
||||
if (!_manifest) {
|
||||
RCTLogError(@"Failed to parse manifest - creating a new one.\n\n%@", error);
|
||||
_manifest = [NSMutableDictionary new];
|
||||
}
|
||||
}
|
||||
_haveSetup = YES;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)_writeManifest:(NSMutableArray<NSDictionary *> **)errors
|
||||
{
|
||||
NSError *error;
|
||||
NSString *serialized = RCTJSONStringify(_manifest, &error);
|
||||
[serialized writeToFile:RCTCreateStorageDirectoryPath(RCTGetManifestFilePath())
|
||||
atomically:YES
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&error];
|
||||
NSDictionary *errorOut;
|
||||
if (error) {
|
||||
errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil);
|
||||
RCTAppendError(errorOut, errors);
|
||||
}
|
||||
return errorOut;
|
||||
}
|
||||
|
||||
- (NSDictionary *)_appendItemForKey:(NSString *)key
|
||||
toArray:(NSMutableArray<NSArray<NSString *> *> *)result
|
||||
{
|
||||
NSDictionary *errorOut = RCTErrorForKey(key);
|
||||
if (errorOut) {
|
||||
return errorOut;
|
||||
}
|
||||
NSString *value = [self _getValueForKey:key errorOut:&errorOut];
|
||||
[result addObject:@[key, RCTNullIfNil(value)]]; // Insert null if missing or failure.
|
||||
return errorOut;
|
||||
}
|
||||
|
||||
- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut
|
||||
{
|
||||
NSString *value =
|
||||
_manifest[key]; // nil means missing, null means there may be a data file, else: NSString
|
||||
if (value == (id)kCFNull) {
|
||||
value = [RCTGetCache() objectForKey:key];
|
||||
if (!value) {
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
value = RCTReadFile(filePath, key, errorOut);
|
||||
if (value) {
|
||||
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
||||
} else {
|
||||
// file does not exist after all, so remove from manifest (no need to save
|
||||
// manifest immediately though, as cost of checking again next time is negligible)
|
||||
[_manifest removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
- (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL *)changedManifest
|
||||
{
|
||||
if (entry.count != 2) {
|
||||
return RCTMakeAndLogError(
|
||||
@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil);
|
||||
}
|
||||
NSString *key = entry[0];
|
||||
NSDictionary *errorOut = RCTErrorForKey(key);
|
||||
if (errorOut) {
|
||||
return errorOut;
|
||||
}
|
||||
NSString *value = entry[1];
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
NSError *error;
|
||||
if (value.length <= RCTInlineValueThreshold) {
|
||||
if (_manifest[key] == (id)kCFNull) {
|
||||
// If the value already existed but wasn't inlined, remove the old file.
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||
[RCTGetCache() removeObjectForKey:key];
|
||||
}
|
||||
*changedManifest = YES;
|
||||
_manifest[key] = value;
|
||||
return nil;
|
||||
}
|
||||
[value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
|
||||
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
||||
if (error) {
|
||||
errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key});
|
||||
} else if (_manifest[key] != (id)kCFNull) {
|
||||
*changedManifest = YES;
|
||||
_manifest[key] = (id)kCFNull;
|
||||
}
|
||||
return errorOut;
|
||||
}
|
||||
|
||||
- (void)_multiGet:(NSArray<NSString *> *)keys
|
||||
callback:(RCTResponseSenderBlock)callback
|
||||
getter:(NSString * (^)(NSUInteger i, NSString *key, NSDictionary **errorOut))getValue
|
||||
{
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
NSMutableArray<NSArray<NSString *> *> *result = [NSMutableArray arrayWithCapacity:keys.count];
|
||||
for (NSUInteger i = 0; i < keys.count; ++i) {
|
||||
NSString *key = keys[i];
|
||||
id keyError;
|
||||
id value = getValue(i, key, &keyError);
|
||||
[result addObject:@[key, RCTNullIfNil(value)]];
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors), result]);
|
||||
}
|
||||
|
||||
- (BOOL)_passthroughDelegate
|
||||
{
|
||||
return
|
||||
[self.delegate respondsToSelector:@selector(isPassthrough)] && self.delegate.isPassthrough;
|
||||
}
|
||||
|
||||
#pragma mark - Exported JS Functions
|
||||
|
||||
// clang-format off
|
||||
RCT_EXPORT_METHOD(multiGet:(NSArray<NSString *> *)keys
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
// clang-format on
|
||||
{
|
||||
if (self.delegate != nil) {
|
||||
[self.delegate
|
||||
valuesForKeys:keys
|
||||
completion:^(NSArray<id<NSObject>> *valuesOrErrors) {
|
||||
[self _multiGet:keys
|
||||
callback:callback
|
||||
getter:^NSString *(NSUInteger i, NSString *key, NSDictionary **errorOut) {
|
||||
id valueOrError = valuesOrErrors[i];
|
||||
if ([valueOrError isKindOfClass:[NSError class]]) {
|
||||
NSError *error = (NSError *)valueOrError;
|
||||
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
|
||||
*errorOut =
|
||||
RCTMakeError(error.localizedDescription, error, extraData);
|
||||
return nil;
|
||||
} else {
|
||||
return [valueOrError isKindOfClass:[NSString class]]
|
||||
? (NSString *)valueOrError
|
||||
: nil;
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
if (![self _passthroughDelegate]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *errorOut = [self _ensureSetup];
|
||||
if (errorOut) {
|
||||
callback(@[@[errorOut], (id)kCFNull]);
|
||||
return;
|
||||
}
|
||||
[self _multiGet:keys
|
||||
callback:callback
|
||||
getter:^(NSUInteger i, NSString *key, NSDictionary **errorOut) {
|
||||
return [self _getValueForKey:key errorOut:errorOut];
|
||||
}];
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
RCT_EXPORT_METHOD(multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
// clang-format on
|
||||
{
|
||||
if (self.delegate != nil) {
|
||||
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||
for (NSArray<NSString *> *entry in kvPairs) {
|
||||
[keys addObject:entry[0]];
|
||||
[values addObject:entry[1]];
|
||||
}
|
||||
[self.delegate setValues:values
|
||||
forKeys:keys
|
||||
completion:^(NSArray<id<NSObject>> *results) {
|
||||
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}];
|
||||
|
||||
if (![self _passthroughDelegate]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *errorOut = [self _ensureSetup];
|
||||
if (errorOut) {
|
||||
callback(@[@[errorOut]]);
|
||||
return;
|
||||
}
|
||||
BOOL changedManifest = NO;
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
for (NSArray<NSString *> *entry in kvPairs) {
|
||||
NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
if (changedManifest) {
|
||||
[self _writeManifest:&errors];
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
RCT_EXPORT_METHOD(multiMerge:(NSArray<NSArray<NSString *> *> *)kvPairs
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
// clang-format on
|
||||
{
|
||||
if (self.delegate != nil) {
|
||||
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||
for (NSArray<NSString *> *entry in kvPairs) {
|
||||
[keys addObject:entry[0]];
|
||||
[values addObject:entry[1]];
|
||||
}
|
||||
[self.delegate mergeValues:values
|
||||
forKeys:keys
|
||||
completion:^(NSArray<id<NSObject>> *results) {
|
||||
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}];
|
||||
|
||||
if (![self _passthroughDelegate]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *errorOut = [self _ensureSetup];
|
||||
if (errorOut) {
|
||||
callback(@[@[errorOut]]);
|
||||
return;
|
||||
}
|
||||
BOOL changedManifest = NO;
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
for (__strong NSArray<NSString *> *entry in kvPairs) {
|
||||
NSDictionary *keyError;
|
||||
NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError];
|
||||
if (!keyError) {
|
||||
if (value) {
|
||||
NSError *jsonError;
|
||||
NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError);
|
||||
if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &jsonError))) {
|
||||
entry = @[entry[0], RCTNullIfNil(RCTJSONStringify(mergedVal, NULL))];
|
||||
}
|
||||
if (jsonError) {
|
||||
keyError = RCTJSErrorFromNSError(jsonError);
|
||||
}
|
||||
}
|
||||
if (!keyError) {
|
||||
keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
||||
}
|
||||
}
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
if (changedManifest) {
|
||||
[self _writeManifest:&errors];
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
RCT_EXPORT_METHOD(multiRemove:(NSArray<NSString *> *)keys
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
// clang-format on
|
||||
{
|
||||
if (self.delegate != nil) {
|
||||
[self.delegate removeValuesForKeys:keys
|
||||
completion:^(NSArray<id<NSObject>> *results) {
|
||||
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}];
|
||||
|
||||
if (![self _passthroughDelegate]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *errorOut = [self _ensureSetup];
|
||||
if (errorOut) {
|
||||
callback(@[@[errorOut]]);
|
||||
return;
|
||||
}
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
BOOL changedManifest = NO;
|
||||
for (NSString *key in keys) {
|
||||
NSDictionary *keyError = RCTErrorForKey(key);
|
||||
if (!keyError) {
|
||||
if (_manifest[key] == (id)kCFNull) {
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||
[RCTGetCache() removeObjectForKey:key];
|
||||
}
|
||||
if (_manifest[key]) {
|
||||
changedManifest = YES;
|
||||
[_manifest removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
if (changedManifest) {
|
||||
[self _writeManifest:&errors];
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
|
||||
// clang-format on
|
||||
{
|
||||
if (self.delegate != nil) {
|
||||
[self.delegate removeAllValues:^(NSError *error) {
|
||||
NSDictionary *result = nil;
|
||||
if (error != nil) {
|
||||
result = RCTMakeError(error.localizedDescription, error, nil);
|
||||
}
|
||||
callback(@[RCTNullIfNil(result)]);
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
[_manifest removeAllObjects];
|
||||
[RCTGetCache() removeAllObjects];
|
||||
NSDictionary *error = RCTDeleteStorageDirectory();
|
||||
callback(@[RCTNullIfNil(error)]);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback)
|
||||
// clang-format on
|
||||
{
|
||||
if (self.delegate != nil) {
|
||||
[self.delegate allKeys:^(NSArray<id<NSObject>> *keys) {
|
||||
callback(@[(id)kCFNull, keys]);
|
||||
}];
|
||||
|
||||
if (![self _passthroughDelegate]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *errorOut = [self _ensureSetup];
|
||||
if (errorOut) {
|
||||
callback(@[errorOut, (id)kCFNull]);
|
||||
} else {
|
||||
callback(@[(id)kCFNull, _manifest.allKeys]);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,283 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||
747102F0243FFB7400D4F466 /* RNCAsyncStorage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||
747102F1243FFB7400D4F466 /* RNCAsyncStorageDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
747102F0243FFB7400D4F466 /* RNCAsyncStorage.h in CopyFiles */,
|
||||
747102F1243FFB7400D4F466 /* RNCAsyncStorageDelegate.h in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCAsyncStorage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCAsyncStorageDelegate.h; sourceTree = "<group>"; };
|
||||
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCAsyncStorage.h; sourceTree = "<group>"; };
|
||||
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCAsyncStorage.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */,
|
||||
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */,
|
||||
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
19F94B1D2239A948006921A9 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */,
|
||||
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */;
|
||||
buildPhases = (
|
||||
19F94B1D2239A948006921A9 /* Headers */,
|
||||
58B511D71A9E6C8500147676 /* Sources */,
|
||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RNCAsyncStorage;
|
||||
productName = RCTDataManager;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0830;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
58B511DA1A9E6C8500147676 = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = 58B511D21A9E6C8500147676;
|
||||
productRefGroup = 58B511D21A9E6C8500147676;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNCAsyncStorage;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511F11A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNCAsyncStorage;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511ED1A9E6C8500147676 /* Debug */,
|
||||
58B511EE1A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511F01A9E6C8500147676 /* Debug */,
|
||||
58B511F11A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^RNCAsyncStorageCompletion)(NSError *_Nullable error);
|
||||
typedef void (^RNCAsyncStorageResultCallback)(NSArray<id<NSObject>> *valuesOrErrors);
|
||||
|
||||
@protocol RNCAsyncStorageDelegate <NSObject>
|
||||
|
||||
/*!
|
||||
* Returns all keys currently stored. If none, an empty array is returned.
|
||||
* @param block Block to call with result.
|
||||
*/
|
||||
- (void)allKeys:(RNCAsyncStorageResultCallback)block;
|
||||
|
||||
/*!
|
||||
* Merges values with the corresponding values stored at specified keys.
|
||||
* @param values Values to merge.
|
||||
* @param keys Keys to the values that should be merged with.
|
||||
* @param block Block to call with merged result.
|
||||
*/
|
||||
- (void)mergeValues:(NSArray<NSString *> *)values
|
||||
forKeys:(NSArray<NSString *> *)keys
|
||||
completion:(RNCAsyncStorageResultCallback)block;
|
||||
|
||||
/*!
|
||||
* Removes all values from the store.
|
||||
* @param block Block to call with result.
|
||||
*/
|
||||
- (void)removeAllValues:(RNCAsyncStorageCompletion)block;
|
||||
|
||||
/*!
|
||||
* Removes all values associated with specified keys.
|
||||
* @param keys Keys of values to remove.
|
||||
* @param block Block to call with result.
|
||||
*/
|
||||
- (void)removeValuesForKeys:(NSArray<NSString *> *)keys
|
||||
completion:(RNCAsyncStorageResultCallback)block;
|
||||
|
||||
/*!
|
||||
* Sets specified key-value pairs.
|
||||
* @param values Values to set.
|
||||
* @param keys Keys of specified values to set.
|
||||
* @param block Block to call with result.
|
||||
*/
|
||||
- (void)setValues:(NSArray<NSString *> *)values
|
||||
forKeys:(NSArray<NSString *> *)keys
|
||||
completion:(RNCAsyncStorageResultCallback)block;
|
||||
|
||||
/*!
|
||||
* Returns values associated with specified keys.
|
||||
* @param keys Keys of values to return.
|
||||
* @param block Block to call with result.
|
||||
*/
|
||||
- (void)valuesForKeys:(NSArray<NSString *> *)keys completion:(RNCAsyncStorageResultCallback)block;
|
||||
|
||||
@optional
|
||||
|
||||
/*!
|
||||
* Returns whether the delegate should be treated as a passthrough.
|
||||
*/
|
||||
@property (nonatomic, readonly, getter=isPassthrough) BOOL passthrough;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,9 @@
|
||||
import type {
|
||||
AsyncStorageHook,
|
||||
AsyncStorageStatic,
|
||||
} from '../lib/typescript/types';
|
||||
|
||||
export function useAsyncStorage(key: string): AsyncStorageHook;
|
||||
|
||||
declare const AsyncStorage: AsyncStorageStatic;
|
||||
export default AsyncStorage;
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* @format
|
||||
*/
|
||||
|
||||
const merge = require('merge-options').bind({
|
||||
concatArrays: true,
|
||||
ignoreUndefined: true,
|
||||
});
|
||||
|
||||
const asMock = {
|
||||
__INTERNAL_MOCK_STORAGE__: {},
|
||||
|
||||
setItem: jest.fn(async (key, value, callback) => {
|
||||
const setResult = await asMock.multiSet([[key, value]], undefined);
|
||||
|
||||
callback && callback(setResult);
|
||||
return setResult;
|
||||
}),
|
||||
|
||||
getItem: jest.fn(async (key, callback) => {
|
||||
const getResult = await asMock.multiGet([key], undefined);
|
||||
|
||||
const result = getResult[0] ? getResult[0][1] : null;
|
||||
|
||||
callback && callback(null, result);
|
||||
return result;
|
||||
}),
|
||||
|
||||
removeItem: jest.fn((key, callback) => asMock.multiRemove([key], callback)),
|
||||
mergeItem: jest.fn((key, value, callback) =>
|
||||
asMock.multiMerge([[key, value]], callback)
|
||||
),
|
||||
|
||||
clear: jest.fn(_clear),
|
||||
getAllKeys: jest.fn(_getAllKeys),
|
||||
flushGetRequests: jest.fn(),
|
||||
|
||||
multiGet: jest.fn(_multiGet),
|
||||
multiSet: jest.fn(_multiSet),
|
||||
multiRemove: jest.fn(_multiRemove),
|
||||
multiMerge: jest.fn(_multiMerge),
|
||||
useAsyncStorage: jest.fn((key) => {
|
||||
return {
|
||||
getItem: (...args) => asMock.getItem(key, ...args),
|
||||
setItem: (...args) => asMock.setItem(key, ...args),
|
||||
mergeItem: (...args) => asMock.mergeItem(key, ...args),
|
||||
removeItem: (...args) => asMock.removeItem(key, ...args),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
async function _multiSet(keyValuePairs, callback) {
|
||||
keyValuePairs.forEach((keyValue) => {
|
||||
const key = keyValue[0];
|
||||
|
||||
asMock.__INTERNAL_MOCK_STORAGE__[key] = keyValue[1];
|
||||
});
|
||||
callback && callback(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
async function _multiGet(keys, callback) {
|
||||
const values = keys.map((key) => [
|
||||
key,
|
||||
asMock.__INTERNAL_MOCK_STORAGE__[key] || null,
|
||||
]);
|
||||
callback && callback(null, values);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
async function _multiRemove(keys, callback) {
|
||||
keys.forEach((key) => {
|
||||
if (asMock.__INTERNAL_MOCK_STORAGE__[key]) {
|
||||
delete asMock.__INTERNAL_MOCK_STORAGE__[key];
|
||||
}
|
||||
});
|
||||
|
||||
callback && callback(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
async function _clear(callback) {
|
||||
asMock.__INTERNAL_MOCK_STORAGE__ = {};
|
||||
|
||||
callback && callback(null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function _getAllKeys() {
|
||||
return Object.keys(asMock.__INTERNAL_MOCK_STORAGE__);
|
||||
}
|
||||
|
||||
async function _multiMerge(keyValuePairs, callback) {
|
||||
keyValuePairs.forEach((keyValue) => {
|
||||
const [key, value] = keyValue;
|
||||
const oldValue = asMock.__INTERNAL_MOCK_STORAGE__[key];
|
||||
asMock.__INTERNAL_MOCK_STORAGE__[key] =
|
||||
oldValue != null
|
||||
? JSON.stringify(merge(JSON.parse(oldValue), JSON.parse(value)))
|
||||
: value;
|
||||
});
|
||||
|
||||
callback && callback(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = asMock;
|
@ -0,0 +1,164 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _mergeOptions = _interopRequireDefault(require("merge-options"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
// @ts-ignore Cannot find module 'merge-options' or its corresponding type declarations
|
||||
const merge = _mergeOptions.default.bind({
|
||||
concatArrays: true,
|
||||
ignoreUndefined: true
|
||||
});
|
||||
|
||||
function mergeLocalStorageItem(key, value) {
|
||||
const oldValue = window.localStorage.getItem(key);
|
||||
|
||||
if (oldValue) {
|
||||
const oldObject = JSON.parse(oldValue);
|
||||
const newObject = JSON.parse(value);
|
||||
const nextValue = JSON.stringify(merge(oldObject, newObject));
|
||||
window.localStorage.setItem(key, nextValue);
|
||||
} else {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function createPromise(getValue, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const value = getValue();
|
||||
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||
resolve(value);
|
||||
} catch (err) {
|
||||
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createPromiseAll(promises, callback, processResult) {
|
||||
return Promise.all(promises).then(result => {
|
||||
const value = (processResult === null || processResult === void 0 ? void 0 : processResult(result)) ?? null;
|
||||
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||
return Promise.resolve(value);
|
||||
}, errors => {
|
||||
callback === null || callback === void 0 ? void 0 : callback(errors);
|
||||
return Promise.reject(errors);
|
||||
});
|
||||
}
|
||||
|
||||
const AsyncStorage = {
|
||||
/**
|
||||
* Fetches `key` value.
|
||||
*/
|
||||
getItem: (key, callback) => {
|
||||
return createPromise(() => window.localStorage.getItem(key), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets `value` for `key`.
|
||||
*/
|
||||
setItem: (key, value, callback) => {
|
||||
return createPromise(() => window.localStorage.setItem(key, value), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a `key`
|
||||
*/
|
||||
removeItem: (key, callback) => {
|
||||
return createPromise(() => window.localStorage.removeItem(key), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges existing value with input value, assuming they are stringified JSON.
|
||||
*/
|
||||
mergeItem: (key, value, callback) => {
|
||||
return createPromise(() => mergeLocalStorageItem(key, value), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* AsyncStorage for the domain.
|
||||
*/
|
||||
clear: callback => {
|
||||
return createPromise(() => window.localStorage.clear(), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to the app, for all callers, libraries, etc.
|
||||
*/
|
||||
getAllKeys: callback => {
|
||||
return createPromise(() => {
|
||||
const numberOfKeys = window.localStorage.length;
|
||||
const keys = [];
|
||||
|
||||
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||
const key = window.localStorage.key(i) || '';
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* (stub) Flushes any pending requests using a single batch call to get the data.
|
||||
*/
|
||||
flushGetRequests: () => undefined,
|
||||
|
||||
/**
|
||||
* multiGet resolves to an array of key-value pair arrays that matches the
|
||||
* input format of multiSet.
|
||||
*
|
||||
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||
*/
|
||||
multiGet: (keys, callback) => {
|
||||
const promises = keys.map(key => AsyncStorage.getItem(key));
|
||||
|
||||
const processResult = result => result.map((value, i) => [keys[i], value]);
|
||||
|
||||
return createPromiseAll(promises, callback, processResult);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of key-value array pairs.
|
||||
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiSet: (keyValuePairs, callback) => {
|
||||
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all the keys in the `keys` array.
|
||||
*/
|
||||
multiRemove: (keys, callback) => {
|
||||
const promises = keys.map(key => AsyncStorage.removeItem(key));
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of key-value array pairs and merges them with existing
|
||||
* values, assuming they are stringified JSON.
|
||||
*
|
||||
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiMerge: (keyValuePairs, callback) => {
|
||||
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
|
||||
return createPromiseAll(promises, callback);
|
||||
}
|
||||
};
|
||||
var _default = AsyncStorage;
|
||||
exports.default = _default;
|
||||
//# sourceMappingURL=AsyncStorage.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,366 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _helpers = require("./helpers");
|
||||
|
||||
var _RCTAsyncStorage = _interopRequireDefault(require("./RCTAsyncStorage"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
if (!_RCTAsyncStorage.default) {
|
||||
throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
|
||||
|
||||
To fix this issue try these steps:
|
||||
|
||||
• Rebuild and restart the app.
|
||||
|
||||
• Run the packager with \`--reset-cache\` flag.
|
||||
|
||||
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||
|
||||
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||
|
||||
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||
`);
|
||||
}
|
||||
/**
|
||||
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||
* storage system that is global to the app. It should be used instead of
|
||||
* LocalStorage.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||
*/
|
||||
|
||||
|
||||
const AsyncStorage = (() => {
|
||||
let _getRequests = [];
|
||||
let _getKeys = [];
|
||||
let _immediate = null;
|
||||
return {
|
||||
/**
|
||||
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||
*/
|
||||
getItem: (key, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
(0, _helpers.checkValidInput)(key);
|
||||
|
||||
_RCTAsyncStorage.default.multiGet([key], (errors, result) => {
|
||||
var _result$;
|
||||
|
||||
// Unpack result to get value from [[key,value]]
|
||||
const value = result !== null && result !== void 0 && (_result$ = result[0]) !== null && _result$ !== void 0 && _result$[1] ? result[0][1] : null;
|
||||
const errs = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0], value);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the value for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||
*/
|
||||
setItem: (key, value, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
(0, _helpers.checkValidInput)(key, value);
|
||||
|
||||
_RCTAsyncStorage.default.multiSet([[key, value]], errors => {
|
||||
const errs = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||
*/
|
||||
removeItem: (key, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
(0, _helpers.checkValidInput)(key);
|
||||
|
||||
_RCTAsyncStorage.default.multiRemove([key], errors => {
|
||||
const errs = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges an existing `key` value with an input value, assuming both values
|
||||
* are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||
*/
|
||||
mergeItem: (key, value, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
(0, _helpers.checkValidInput)(key, value);
|
||||
|
||||
_RCTAsyncStorage.default.multiMerge([[key, value]], errors => {
|
||||
const errs = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||
* your app's keys.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||
*/
|
||||
clear: callback => {
|
||||
return new Promise((resolve, reject) => {
|
||||
_RCTAsyncStorage.default.clear(error => {
|
||||
const err = (0, _helpers.convertError)(error);
|
||||
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||
*/
|
||||
getAllKeys: callback => {
|
||||
return new Promise((resolve, reject) => {
|
||||
_RCTAsyncStorage.default.getAllKeys((error, keys) => {
|
||||
const err = (0, _helpers.convertError)(error);
|
||||
callback === null || callback === void 0 ? void 0 : callback(err, keys);
|
||||
|
||||
if (keys) {
|
||||
resolve(keys);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The following batched functions are useful for executing a lot of
|
||||
* operations at once, allowing for native optimizations and provide the
|
||||
* convenience of a single callback after all operations are complete.
|
||||
*
|
||||
* These functions return arrays of errors, potentially one for every key.
|
||||
* For key-specific errors, the Error object will have a key property to
|
||||
* indicate which key caused the error.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Flushes any pending requests using a single batch call to get the data.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||
* */
|
||||
flushGetRequests: () => {
|
||||
const getRequests = _getRequests;
|
||||
const getKeys = _getKeys;
|
||||
_getRequests = [];
|
||||
_getKeys = [];
|
||||
|
||||
_RCTAsyncStorage.default.multiGet(getKeys, (errors, result) => {
|
||||
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
|
||||
// it's much, much faster in practice for the data sets we deal with (we avoid
|
||||
// allocating result pair arrays). This was heavily benchmarked.
|
||||
//
|
||||
// Is there a way to avoid using the map but fix the bug in this breaking test?
|
||||
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
|
||||
const map = {};
|
||||
result === null || result === void 0 ? void 0 : result.forEach(_ref => {
|
||||
let [key, value] = _ref;
|
||||
map[key] = value;
|
||||
return value;
|
||||
});
|
||||
const reqLength = getRequests.length;
|
||||
/**
|
||||
* As mentioned few lines above, this method could be called with the array of potential error,
|
||||
* in case of anything goes wrong. The problem is, if any of the batched calls fails
|
||||
* the rest of them would fail too, but the error would be consumed by just one. The rest
|
||||
* would simply return `undefined` as their result, rendering false negatives.
|
||||
*
|
||||
* In order to avoid this situation, in case of any call failing,
|
||||
* the rest of them will be rejected as well (with the same error).
|
||||
*/
|
||||
|
||||
const errorList = (0, _helpers.convertErrors)(errors);
|
||||
const error = errorList !== null && errorList !== void 0 && errorList.length ? errorList[0] : null;
|
||||
|
||||
for (let i = 0; i < reqLength; i++) {
|
||||
var _request$callback2, _request$resolve;
|
||||
|
||||
const request = getRequests[i];
|
||||
|
||||
if (error) {
|
||||
var _request$callback, _request$reject;
|
||||
|
||||
(_request$callback = request.callback) === null || _request$callback === void 0 ? void 0 : _request$callback.call(request, errorList);
|
||||
(_request$reject = request.reject) === null || _request$reject === void 0 ? void 0 : _request$reject.call(request, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
const requestResult = request.keys.map(key => [key, map[key]]);
|
||||
(_request$callback2 = request.callback) === null || _request$callback2 === void 0 ? void 0 : _request$callback2.call(request, null, requestResult);
|
||||
(_request$resolve = request.resolve) === null || _request$resolve === void 0 ? void 0 : _request$resolve.call(request, requestResult);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* This allows you to batch the fetching of items given an array of `key`
|
||||
* inputs. Your callback will be invoked with an array of corresponding
|
||||
* key-value pairs found.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||
*/
|
||||
multiGet: (keys, callback) => {
|
||||
if (!_immediate) {
|
||||
_immediate = setImmediate(() => {
|
||||
_immediate = null;
|
||||
AsyncStorage.flushGetRequests();
|
||||
});
|
||||
}
|
||||
|
||||
const getRequest = {
|
||||
keys: keys,
|
||||
callback: callback,
|
||||
// do we need this?
|
||||
keyIndex: _getKeys.length,
|
||||
resolve: null,
|
||||
reject: null
|
||||
};
|
||||
const promiseResult = new Promise((resolve, reject) => {
|
||||
getRequest.resolve = resolve;
|
||||
getRequest.reject = reject;
|
||||
});
|
||||
|
||||
_getRequests.push(getRequest); // avoid fetching duplicates
|
||||
|
||||
|
||||
keys.forEach(key => {
|
||||
if (_getKeys.indexOf(key) === -1) {
|
||||
_getKeys.push(key);
|
||||
}
|
||||
});
|
||||
return promiseResult;
|
||||
},
|
||||
|
||||
/**
|
||||
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||
* the operation completes you'll get a single callback with any errors.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||
*/
|
||||
multiSet: (keyValuePairs, callback) => {
|
||||
(0, _helpers.checkValidArgs)(keyValuePairs, callback);
|
||||
return new Promise((resolve, reject) => {
|
||||
keyValuePairs.forEach(_ref2 => {
|
||||
let [key, value] = _ref2;
|
||||
(0, _helpers.checkValidInput)(key, value);
|
||||
});
|
||||
|
||||
_RCTAsyncStorage.default.multiSet(keyValuePairs, errors => {
|
||||
const error = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this to batch the deletion of all keys in the `keys` array.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||
*/
|
||||
multiRemove: (keys, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
keys.forEach(key => (0, _helpers.checkValidInput)(key));
|
||||
|
||||
_RCTAsyncStorage.default.multiRemove(keys, errors => {
|
||||
const error = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Batch operation to merge in existing and new values for a given set of
|
||||
* keys. This assumes that the values are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||
*/
|
||||
multiMerge: (keyValuePairs, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
_RCTAsyncStorage.default.multiMerge(keyValuePairs, errors => {
|
||||
const error = (0, _helpers.convertErrors)(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var _default = AsyncStorage;
|
||||
exports.default = _default;
|
||||
//# sourceMappingURL=AsyncStorage.native.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _reactNative = require("react-native");
|
||||
|
||||
var _shouldFallbackToLegacyNativeModule = require("./shouldFallbackToLegacyNativeModule");
|
||||
|
||||
// @ts-ignore Module '"react-native"' has no exported member 'TurboModuleRegistry'.
|
||||
let RCTAsyncStorage = _reactNative.NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows
|
||||
_reactNative.NativeModules['RNC_AsyncSQLiteDBStorage'] || _reactNative.NativeModules['RNCAsyncStorage'];
|
||||
|
||||
if (!RCTAsyncStorage && (0, _shouldFallbackToLegacyNativeModule.shouldFallbackToLegacyNativeModule)()) {
|
||||
// TurboModuleRegistry falls back to NativeModules so we don't have to try go
|
||||
// assign NativeModules' counterparts if TurboModuleRegistry would resolve
|
||||
// with undefined.
|
||||
if (_reactNative.TurboModuleRegistry) {
|
||||
RCTAsyncStorage = _reactNative.TurboModuleRegistry.get('AsyncSQLiteDBStorage') || _reactNative.TurboModuleRegistry.get('AsyncLocalStorage');
|
||||
} else {
|
||||
RCTAsyncStorage = _reactNative.NativeModules['AsyncSQLiteDBStorage'] || _reactNative.NativeModules['AsyncLocalStorage'];
|
||||
}
|
||||
}
|
||||
|
||||
var _default = RCTAsyncStorage;
|
||||
exports.default = _default;
|
||||
//# sourceMappingURL=RCTAsyncStorage.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["RCTAsyncStorage","NativeModules","shouldFallbackToLegacyNativeModule","TurboModuleRegistry","get"],"sources":["RCTAsyncStorage.ts"],"sourcesContent":["// @ts-ignore Module '\"react-native\"' has no exported member 'TurboModuleRegistry'.\nimport { NativeModules, TurboModuleRegistry } from 'react-native';\nimport { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';\n\nlet RCTAsyncStorage =\n NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows\n NativeModules['RNC_AsyncSQLiteDBStorage'] ||\n NativeModules['RNCAsyncStorage'];\n\nif (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {\n // TurboModuleRegistry falls back to NativeModules so we don't have to try go\n // assign NativeModules' counterparts if TurboModuleRegistry would resolve\n // with undefined.\n if (TurboModuleRegistry) {\n RCTAsyncStorage =\n TurboModuleRegistry.get('AsyncSQLiteDBStorage') ||\n TurboModuleRegistry.get('AsyncLocalStorage');\n } else {\n RCTAsyncStorage =\n NativeModules['AsyncSQLiteDBStorage'] ||\n NativeModules['AsyncLocalStorage'];\n }\n}\n\nexport default RCTAsyncStorage;\n"],"mappings":";;;;;;;AACA;;AACA;;AAFA;AAIA,IAAIA,eAAe,GACjBC,0BAAA,CAAc,sBAAd,KAAyC;AACzCA,0BAAA,CAAc,0BAAd,CADA,IAEAA,0BAAA,CAAc,iBAAd,CAHF;;AAKA,IAAI,CAACD,eAAD,IAAoB,IAAAE,sEAAA,GAAxB,EAA8D;EAC5D;EACA;EACA;EACA,IAAIC,gCAAJ,EAAyB;IACvBH,eAAe,GACbG,gCAAA,CAAoBC,GAApB,CAAwB,sBAAxB,KACAD,gCAAA,CAAoBC,GAApB,CAAwB,mBAAxB,CAFF;EAGD,CAJD,MAIO;IACLJ,eAAe,GACbC,0BAAA,CAAc,sBAAd,KACAA,0BAAA,CAAc,mBAAd,CAFF;EAGD;AACF;;eAEcD,e"}
|
@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.checkValidArgs = checkValidArgs;
|
||||
exports.checkValidInput = checkValidInput;
|
||||
exports.convertError = convertError;
|
||||
exports.convertErrors = convertErrors;
|
||||
|
||||
function checkValidArgs(keyValuePairs, callback) {
|
||||
if (!Array.isArray(keyValuePairs) || keyValuePairs.length === 0 || !Array.isArray(keyValuePairs[0])) {
|
||||
throw new Error('[AsyncStorage] Expected array of key-value pairs as first argument to multiSet');
|
||||
}
|
||||
|
||||
if (callback && typeof callback !== 'function') {
|
||||
if (Array.isArray(callback)) {
|
||||
throw new Error('[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?');
|
||||
}
|
||||
|
||||
throw new Error('[AsyncStorage] Expected function as second argument to multiSet');
|
||||
}
|
||||
}
|
||||
|
||||
function checkValidInput() {
|
||||
for (var _len = arguments.length, input = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
input[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
const [key, value] = input;
|
||||
|
||||
if (typeof key !== 'string') {
|
||||
console.warn(`[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${key}\n`);
|
||||
}
|
||||
|
||||
if (input.length > 1 && typeof value !== 'string') {
|
||||
if (value == null) {
|
||||
throw new Error(`[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||
} else {
|
||||
console.warn(`[AsyncStorage] The value for key "${key}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertError(error) {
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const out = new Error(error.message);
|
||||
out.key = error.key;
|
||||
return out;
|
||||
}
|
||||
|
||||
function convertErrors(errs) {
|
||||
const errors = ensureArray(errs);
|
||||
return errors ? errors.map(e => convertError(e)) : null;
|
||||
}
|
||||
|
||||
function ensureArray(e) {
|
||||
if (Array.isArray(e)) {
|
||||
return e.length === 0 ? null : e;
|
||||
} else if (e) {
|
||||
return [e];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=helpers.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["checkValidArgs","keyValuePairs","callback","Array","isArray","length","Error","checkValidInput","input","key","value","console","warn","convertError","error","out","message","convertErrors","errs","errors","ensureArray","map","e"],"sources":["helpers.ts"],"sourcesContent":["import type { ErrorLike } from './types';\n\nexport function checkValidArgs(keyValuePairs: unknown[], callback: unknown) {\n if (\n !Array.isArray(keyValuePairs) ||\n keyValuePairs.length === 0 ||\n !Array.isArray(keyValuePairs[0])\n ) {\n throw new Error(\n '[AsyncStorage] Expected array of key-value pairs as first argument to multiSet'\n );\n }\n\n if (callback && typeof callback !== 'function') {\n if (Array.isArray(callback)) {\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?'\n );\n }\n\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet'\n );\n }\n}\n\nexport function checkValidInput(...input: unknown[]) {\n const [key, value] = input;\n\n if (typeof key !== 'string') {\n console.warn(\n `[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\\nKey passed: ${key}\\n`\n );\n }\n\n if (input.length > 1 && typeof value !== 'string') {\n if (value == null) {\n throw new Error(\n `[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n } else {\n console.warn(\n `[AsyncStorage] The value for key \"${key}\" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n }\n }\n}\n\nexport function convertError(error?: ErrorLike): Error | null {\n if (!error) {\n return null;\n }\n\n const out = new Error(error.message);\n (out as any).key = error.key;\n return out;\n}\n\nexport function convertErrors(\n errs?: ErrorLike[]\n): ReadonlyArray<Error | null> | null {\n const errors = ensureArray(errs);\n return errors ? errors.map((e) => convertError(e)) : null;\n}\n\nfunction ensureArray(e?: ErrorLike | ErrorLike[]): ErrorLike[] | null {\n if (Array.isArray(e)) {\n return e.length === 0 ? null : e;\n } else if (e) {\n return [e];\n } else {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;AAEO,SAASA,cAAT,CAAwBC,aAAxB,EAAkDC,QAAlD,EAAqE;EAC1E,IACE,CAACC,KAAK,CAACC,OAAN,CAAcH,aAAd,CAAD,IACAA,aAAa,CAACI,MAAd,KAAyB,CADzB,IAEA,CAACF,KAAK,CAACC,OAAN,CAAcH,aAAa,CAAC,CAAD,CAA3B,CAHH,EAIE;IACA,MAAM,IAAIK,KAAJ,CACJ,gFADI,CAAN;EAGD;;EAED,IAAIJ,QAAQ,IAAI,OAAOA,QAAP,KAAoB,UAApC,EAAgD;IAC9C,IAAIC,KAAK,CAACC,OAAN,CAAcF,QAAd,CAAJ,EAA6B;MAC3B,MAAM,IAAII,KAAJ,CACJ,6IADI,CAAN;IAGD;;IAED,MAAM,IAAIA,KAAJ,CACJ,iEADI,CAAN;EAGD;AACF;;AAEM,SAASC,eAAT,GAA8C;EAAA,kCAAlBC,KAAkB;IAAlBA,KAAkB;EAAA;;EACnD,MAAM,CAACC,GAAD,EAAMC,KAAN,IAAeF,KAArB;;EAEA,IAAI,OAAOC,GAAP,KAAe,QAAnB,EAA6B;IAC3BE,OAAO,CAACC,IAAR,CACG,wBAAuB,OAAOH,GAAI,iHAAgHA,GAAI,IADzJ;EAGD;;EAED,IAAID,KAAK,CAACH,MAAN,GAAe,CAAf,IAAoB,OAAOK,KAAP,KAAiB,QAAzC,EAAmD;IACjD,IAAIA,KAAK,IAAI,IAAb,EAAmB;MACjB,MAAM,IAAIJ,KAAJ,CACH,gJAA+II,KAAM,iBAAgBD,GAAI,IADtK,CAAN;IAGD,CAJD,MAIO;MACLE,OAAO,CAACC,IAAR,CACG,qCAAoCH,GAAI,4GAA2GC,KAAM,iBAAgBD,GAAI,IADhL;IAGD;EACF;AACF;;AAEM,SAASI,YAAT,CAAsBC,KAAtB,EAAuD;EAC5D,IAAI,CAACA,KAAL,EAAY;IACV,OAAO,IAAP;EACD;;EAED,MAAMC,GAAG,GAAG,IAAIT,KAAJ,CAAUQ,KAAK,CAACE,OAAhB,CAAZ;EACCD,GAAD,CAAaN,GAAb,GAAmBK,KAAK,CAACL,GAAzB;EACA,OAAOM,GAAP;AACD;;AAEM,SAASE,aAAT,CACLC,IADK,EAE+B;EACpC,MAAMC,MAAM,GAAGC,WAAW,CAACF,IAAD,CAA1B;EACA,OAAOC,MAAM,GAAGA,MAAM,CAACE,GAAP,CAAYC,CAAD,IAAOT,YAAY,CAACS,CAAD,CAA9B,CAAH,GAAwC,IAArD;AACD;;AAED,SAASF,WAAT,CAAqBE,CAArB,EAAsE;EACpE,IAAInB,KAAK,CAACC,OAAN,CAAckB,CAAd,CAAJ,EAAsB;IACpB,OAAOA,CAAC,CAACjB,MAAF,KAAa,CAAb,GAAiB,IAAjB,GAAwBiB,CAA/B;EACD,CAFD,MAEO,IAAIA,CAAJ,EAAO;IACZ,OAAO,CAACA,CAAD,CAAP;EACD,CAFM,MAEA;IACL,OAAO,IAAP;EACD;AACF"}
|
@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.useAsyncStorage = useAsyncStorage;
|
||||
|
||||
var _AsyncStorage = _interopRequireDefault(require("./AsyncStorage"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function useAsyncStorage(key) {
|
||||
return {
|
||||
getItem: function () {
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return _AsyncStorage.default.getItem(key, ...args);
|
||||
},
|
||||
setItem: function () {
|
||||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
args[_key2] = arguments[_key2];
|
||||
}
|
||||
|
||||
return _AsyncStorage.default.setItem(key, ...args);
|
||||
},
|
||||
mergeItem: function () {
|
||||
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
||||
args[_key3] = arguments[_key3];
|
||||
}
|
||||
|
||||
return _AsyncStorage.default.mergeItem(key, ...args);
|
||||
},
|
||||
removeItem: function () {
|
||||
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
||||
args[_key4] = arguments[_key4];
|
||||
}
|
||||
|
||||
return _AsyncStorage.default.removeItem(key, ...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=hooks.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["useAsyncStorage","key","getItem","args","AsyncStorage","setItem","mergeItem","removeItem"],"sources":["hooks.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\nimport type { AsyncStorageHook } from './types';\n\nexport function useAsyncStorage(key: string): AsyncStorageHook {\n return {\n getItem: (...args) => AsyncStorage.getItem(key, ...args),\n setItem: (...args) => AsyncStorage.setItem(key, ...args),\n mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),\n removeItem: (...args) => AsyncStorage.removeItem(key, ...args),\n };\n}\n"],"mappings":";;;;;;;AAAA;;;;AAGO,SAASA,eAAT,CAAyBC,GAAzB,EAAwD;EAC7D,OAAO;IACLC,OAAO,EAAE;MAAA,kCAAIC,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaF,OAAb,CAAqBD,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CADJ;IAELE,OAAO,EAAE;MAAA,mCAAIF,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaC,OAAb,CAAqBJ,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CAFJ;IAGLG,SAAS,EAAE;MAAA,mCAAIH,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaE,SAAb,CAAuBL,GAAvB,EAA4B,GAAGE,IAA/B,CAAb;IAAA,CAHN;IAILI,UAAU,EAAE;MAAA,mCAAIJ,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaG,UAAb,CAAwBN,GAAxB,EAA6B,GAAGE,IAAhC,CAAb;IAAA;EAJP,CAAP;AAMD"}
|
@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
Object.defineProperty(exports, "useAsyncStorage", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _hooks.useAsyncStorage;
|
||||
}
|
||||
});
|
||||
|
||||
var _AsyncStorage = _interopRequireDefault(require("./AsyncStorage"));
|
||||
|
||||
var _hooks = require("./hooks");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var _default = _AsyncStorage.default;
|
||||
exports.default = _default;
|
||||
//# sourceMappingURL=index.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["AsyncStorage"],"sources":["index.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\n\nexport { useAsyncStorage } from './hooks';\n\nexport type { AsyncStorageStatic } from './types';\n\nexport default AsyncStorage;\n"],"mappings":";;;;;;;;;;;;;AAAA;;AAEA;;;;eAIeA,qB"}
|
@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.shouldFallbackToLegacyNativeModule = shouldFallbackToLegacyNativeModule;
|
||||
|
||||
var _reactNative = require("react-native");
|
||||
|
||||
function shouldFallbackToLegacyNativeModule() {
|
||||
var _NativeModules$Native, _NativeModules$Native2;
|
||||
|
||||
const expoConstants = (_NativeModules$Native = _reactNative.NativeModules['NativeUnimoduleProxy']) === null || _NativeModules$Native === void 0 ? void 0 : (_NativeModules$Native2 = _NativeModules$Native.modulesConstants) === null || _NativeModules$Native2 === void 0 ? void 0 : _NativeModules$Native2.ExponentConstants;
|
||||
|
||||
if (expoConstants) {
|
||||
/**
|
||||
* In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.
|
||||
* In bare React Native apps using expo-constants, appOwnership is never defined, so
|
||||
* isLegacySdkVersion will be false in that context.
|
||||
*/
|
||||
const isLegacySdkVersion = expoConstants.appOwnership && !expoConstants.executionEnvironment;
|
||||
/**
|
||||
* Expo managed apps don't include the @react-native-async-storage/async-storage
|
||||
* native modules yet, but the API interface is the same, so we can use the version
|
||||
* exported from React Native still.
|
||||
*
|
||||
* If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this
|
||||
* will likely not be valid anymore, and the package will need to be included in the Expo SDK
|
||||
* to continue to work.
|
||||
*/
|
||||
|
||||
if (isLegacySdkVersion || ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//# sourceMappingURL=shouldFallbackToLegacyNativeModule.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["shouldFallbackToLegacyNativeModule","expoConstants","NativeModules","modulesConstants","ExponentConstants","isLegacySdkVersion","appOwnership","executionEnvironment","includes"],"sources":["shouldFallbackToLegacyNativeModule.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\n\nexport function shouldFallbackToLegacyNativeModule(): boolean {\n const expoConstants =\n NativeModules['NativeUnimoduleProxy']?.modulesConstants?.ExponentConstants;\n\n if (expoConstants) {\n /**\n * In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.\n * In bare React Native apps using expo-constants, appOwnership is never defined, so\n * isLegacySdkVersion will be false in that context.\n */\n const isLegacySdkVersion =\n expoConstants.appOwnership && !expoConstants.executionEnvironment;\n\n /**\n * Expo managed apps don't include the @react-native-async-storage/async-storage\n * native modules yet, but the API interface is the same, so we can use the version\n * exported from React Native still.\n *\n * If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this\n * will likely not be valid anymore, and the package will need to be included in the Expo SDK\n * to continue to work.\n */\n if (\n isLegacySdkVersion ||\n ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)\n ) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":";;;;;;;AAAA;;AAEO,SAASA,kCAAT,GAAuD;EAAA;;EAC5D,MAAMC,aAAa,4BACjBC,0BAAA,CAAc,sBAAd,CADiB,oFACjB,sBAAuCC,gBADtB,2DACjB,uBAAyDC,iBAD3D;;EAGA,IAAIH,aAAJ,EAAmB;IACjB;AACJ;AACA;AACA;AACA;IACI,MAAMI,kBAAkB,GACtBJ,aAAa,CAACK,YAAd,IAA8B,CAACL,aAAa,CAACM,oBAD/C;IAGA;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;IACI,IACEF,kBAAkB,IAClB,CAAC,aAAD,EAAgB,YAAhB,EAA8BG,QAA9B,CAAuCP,aAAa,CAACM,oBAArD,CAFF,EAGE;MACA,OAAO,IAAP;IACD;EACF;;EAED,OAAO,KAAP;AACD"}
|
@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=types.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
// @ts-ignore Cannot find module 'merge-options' or its corresponding type declarations
|
||||
import mergeOptions from 'merge-options';
|
||||
const merge = mergeOptions.bind({
|
||||
concatArrays: true,
|
||||
ignoreUndefined: true
|
||||
});
|
||||
|
||||
function mergeLocalStorageItem(key, value) {
|
||||
const oldValue = window.localStorage.getItem(key);
|
||||
|
||||
if (oldValue) {
|
||||
const oldObject = JSON.parse(oldValue);
|
||||
const newObject = JSON.parse(value);
|
||||
const nextValue = JSON.stringify(merge(oldObject, newObject));
|
||||
window.localStorage.setItem(key, nextValue);
|
||||
} else {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function createPromise(getValue, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const value = getValue();
|
||||
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||
resolve(value);
|
||||
} catch (err) {
|
||||
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createPromiseAll(promises, callback, processResult) {
|
||||
return Promise.all(promises).then(result => {
|
||||
const value = (processResult === null || processResult === void 0 ? void 0 : processResult(result)) ?? null;
|
||||
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||
return Promise.resolve(value);
|
||||
}, errors => {
|
||||
callback === null || callback === void 0 ? void 0 : callback(errors);
|
||||
return Promise.reject(errors);
|
||||
});
|
||||
}
|
||||
|
||||
const AsyncStorage = {
|
||||
/**
|
||||
* Fetches `key` value.
|
||||
*/
|
||||
getItem: (key, callback) => {
|
||||
return createPromise(() => window.localStorage.getItem(key), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets `value` for `key`.
|
||||
*/
|
||||
setItem: (key, value, callback) => {
|
||||
return createPromise(() => window.localStorage.setItem(key, value), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a `key`
|
||||
*/
|
||||
removeItem: (key, callback) => {
|
||||
return createPromise(() => window.localStorage.removeItem(key), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges existing value with input value, assuming they are stringified JSON.
|
||||
*/
|
||||
mergeItem: (key, value, callback) => {
|
||||
return createPromise(() => mergeLocalStorageItem(key, value), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* AsyncStorage for the domain.
|
||||
*/
|
||||
clear: callback => {
|
||||
return createPromise(() => window.localStorage.clear(), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to the app, for all callers, libraries, etc.
|
||||
*/
|
||||
getAllKeys: callback => {
|
||||
return createPromise(() => {
|
||||
const numberOfKeys = window.localStorage.length;
|
||||
const keys = [];
|
||||
|
||||
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||
const key = window.localStorage.key(i) || '';
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* (stub) Flushes any pending requests using a single batch call to get the data.
|
||||
*/
|
||||
flushGetRequests: () => undefined,
|
||||
|
||||
/**
|
||||
* multiGet resolves to an array of key-value pair arrays that matches the
|
||||
* input format of multiSet.
|
||||
*
|
||||
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||
*/
|
||||
multiGet: (keys, callback) => {
|
||||
const promises = keys.map(key => AsyncStorage.getItem(key));
|
||||
|
||||
const processResult = result => result.map((value, i) => [keys[i], value]);
|
||||
|
||||
return createPromiseAll(promises, callback, processResult);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of key-value array pairs.
|
||||
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiSet: (keyValuePairs, callback) => {
|
||||
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all the keys in the `keys` array.
|
||||
*/
|
||||
multiRemove: (keys, callback) => {
|
||||
const promises = keys.map(key => AsyncStorage.removeItem(key));
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of key-value array pairs and merges them with existing
|
||||
* values, assuming they are stringified JSON.
|
||||
*
|
||||
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiMerge: (keyValuePairs, callback) => {
|
||||
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
|
||||
return createPromiseAll(promises, callback);
|
||||
}
|
||||
};
|
||||
export default AsyncStorage;
|
||||
//# sourceMappingURL=AsyncStorage.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,348 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import { checkValidArgs, checkValidInput, convertError, convertErrors } from './helpers';
|
||||
import RCTAsyncStorage from './RCTAsyncStorage';
|
||||
|
||||
if (!RCTAsyncStorage) {
|
||||
throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
|
||||
|
||||
To fix this issue try these steps:
|
||||
|
||||
• Rebuild and restart the app.
|
||||
|
||||
• Run the packager with \`--reset-cache\` flag.
|
||||
|
||||
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||
|
||||
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||
|
||||
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||
`);
|
||||
}
|
||||
/**
|
||||
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||
* storage system that is global to the app. It should be used instead of
|
||||
* LocalStorage.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||
*/
|
||||
|
||||
|
||||
const AsyncStorage = (() => {
|
||||
let _getRequests = [];
|
||||
let _getKeys = [];
|
||||
let _immediate = null;
|
||||
return {
|
||||
/**
|
||||
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||
*/
|
||||
getItem: (key, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key);
|
||||
RCTAsyncStorage.multiGet([key], (errors, result) => {
|
||||
var _result$;
|
||||
|
||||
// Unpack result to get value from [[key,value]]
|
||||
const value = result !== null && result !== void 0 && (_result$ = result[0]) !== null && _result$ !== void 0 && _result$[1] ? result[0][1] : null;
|
||||
const errs = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0], value);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the value for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||
*/
|
||||
setItem: (key, value, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key, value);
|
||||
RCTAsyncStorage.multiSet([[key, value]], errors => {
|
||||
const errs = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||
*/
|
||||
removeItem: (key, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key);
|
||||
RCTAsyncStorage.multiRemove([key], errors => {
|
||||
const errs = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges an existing `key` value with an input value, assuming both values
|
||||
* are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||
*/
|
||||
mergeItem: (key, value, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key, value);
|
||||
RCTAsyncStorage.multiMerge([[key, value]], errors => {
|
||||
const errs = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||
* your app's keys.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||
*/
|
||||
clear: callback => {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.clear(error => {
|
||||
const err = convertError(error);
|
||||
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||
*/
|
||||
getAllKeys: callback => {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.getAllKeys((error, keys) => {
|
||||
const err = convertError(error);
|
||||
callback === null || callback === void 0 ? void 0 : callback(err, keys);
|
||||
|
||||
if (keys) {
|
||||
resolve(keys);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The following batched functions are useful for executing a lot of
|
||||
* operations at once, allowing for native optimizations and provide the
|
||||
* convenience of a single callback after all operations are complete.
|
||||
*
|
||||
* These functions return arrays of errors, potentially one for every key.
|
||||
* For key-specific errors, the Error object will have a key property to
|
||||
* indicate which key caused the error.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Flushes any pending requests using a single batch call to get the data.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||
* */
|
||||
flushGetRequests: () => {
|
||||
const getRequests = _getRequests;
|
||||
const getKeys = _getKeys;
|
||||
_getRequests = [];
|
||||
_getKeys = [];
|
||||
RCTAsyncStorage.multiGet(getKeys, (errors, result) => {
|
||||
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
|
||||
// it's much, much faster in practice for the data sets we deal with (we avoid
|
||||
// allocating result pair arrays). This was heavily benchmarked.
|
||||
//
|
||||
// Is there a way to avoid using the map but fix the bug in this breaking test?
|
||||
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
|
||||
const map = {};
|
||||
result === null || result === void 0 ? void 0 : result.forEach(_ref => {
|
||||
let [key, value] = _ref;
|
||||
map[key] = value;
|
||||
return value;
|
||||
});
|
||||
const reqLength = getRequests.length;
|
||||
/**
|
||||
* As mentioned few lines above, this method could be called with the array of potential error,
|
||||
* in case of anything goes wrong. The problem is, if any of the batched calls fails
|
||||
* the rest of them would fail too, but the error would be consumed by just one. The rest
|
||||
* would simply return `undefined` as their result, rendering false negatives.
|
||||
*
|
||||
* In order to avoid this situation, in case of any call failing,
|
||||
* the rest of them will be rejected as well (with the same error).
|
||||
*/
|
||||
|
||||
const errorList = convertErrors(errors);
|
||||
const error = errorList !== null && errorList !== void 0 && errorList.length ? errorList[0] : null;
|
||||
|
||||
for (let i = 0; i < reqLength; i++) {
|
||||
var _request$callback2, _request$resolve;
|
||||
|
||||
const request = getRequests[i];
|
||||
|
||||
if (error) {
|
||||
var _request$callback, _request$reject;
|
||||
|
||||
(_request$callback = request.callback) === null || _request$callback === void 0 ? void 0 : _request$callback.call(request, errorList);
|
||||
(_request$reject = request.reject) === null || _request$reject === void 0 ? void 0 : _request$reject.call(request, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
const requestResult = request.keys.map(key => [key, map[key]]);
|
||||
(_request$callback2 = request.callback) === null || _request$callback2 === void 0 ? void 0 : _request$callback2.call(request, null, requestResult);
|
||||
(_request$resolve = request.resolve) === null || _request$resolve === void 0 ? void 0 : _request$resolve.call(request, requestResult);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* This allows you to batch the fetching of items given an array of `key`
|
||||
* inputs. Your callback will be invoked with an array of corresponding
|
||||
* key-value pairs found.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||
*/
|
||||
multiGet: (keys, callback) => {
|
||||
if (!_immediate) {
|
||||
_immediate = setImmediate(() => {
|
||||
_immediate = null;
|
||||
AsyncStorage.flushGetRequests();
|
||||
});
|
||||
}
|
||||
|
||||
const getRequest = {
|
||||
keys: keys,
|
||||
callback: callback,
|
||||
// do we need this?
|
||||
keyIndex: _getKeys.length,
|
||||
resolve: null,
|
||||
reject: null
|
||||
};
|
||||
const promiseResult = new Promise((resolve, reject) => {
|
||||
getRequest.resolve = resolve;
|
||||
getRequest.reject = reject;
|
||||
});
|
||||
|
||||
_getRequests.push(getRequest); // avoid fetching duplicates
|
||||
|
||||
|
||||
keys.forEach(key => {
|
||||
if (_getKeys.indexOf(key) === -1) {
|
||||
_getKeys.push(key);
|
||||
}
|
||||
});
|
||||
return promiseResult;
|
||||
},
|
||||
|
||||
/**
|
||||
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||
* the operation completes you'll get a single callback with any errors.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||
*/
|
||||
multiSet: (keyValuePairs, callback) => {
|
||||
checkValidArgs(keyValuePairs, callback);
|
||||
return new Promise((resolve, reject) => {
|
||||
keyValuePairs.forEach(_ref2 => {
|
||||
let [key, value] = _ref2;
|
||||
checkValidInput(key, value);
|
||||
});
|
||||
RCTAsyncStorage.multiSet(keyValuePairs, errors => {
|
||||
const error = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this to batch the deletion of all keys in the `keys` array.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||
*/
|
||||
multiRemove: (keys, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
keys.forEach(key => checkValidInput(key));
|
||||
RCTAsyncStorage.multiRemove(keys, errors => {
|
||||
const error = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Batch operation to merge in existing and new values for a given set of
|
||||
* keys. This assumes that the values are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||
*/
|
||||
multiMerge: (keyValuePairs, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge(keyValuePairs, errors => {
|
||||
const error = convertErrors(errors);
|
||||
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
export default AsyncStorage;
|
||||
//# sourceMappingURL=AsyncStorage.native.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,19 @@
|
||||
// @ts-ignore Module '"react-native"' has no exported member 'TurboModuleRegistry'.
|
||||
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
||||
import { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';
|
||||
let RCTAsyncStorage = NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows
|
||||
NativeModules['RNC_AsyncSQLiteDBStorage'] || NativeModules['RNCAsyncStorage'];
|
||||
|
||||
if (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {
|
||||
// TurboModuleRegistry falls back to NativeModules so we don't have to try go
|
||||
// assign NativeModules' counterparts if TurboModuleRegistry would resolve
|
||||
// with undefined.
|
||||
if (TurboModuleRegistry) {
|
||||
RCTAsyncStorage = TurboModuleRegistry.get('AsyncSQLiteDBStorage') || TurboModuleRegistry.get('AsyncLocalStorage');
|
||||
} else {
|
||||
RCTAsyncStorage = NativeModules['AsyncSQLiteDBStorage'] || NativeModules['AsyncLocalStorage'];
|
||||
}
|
||||
}
|
||||
|
||||
export default RCTAsyncStorage;
|
||||
//# sourceMappingURL=RCTAsyncStorage.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["NativeModules","TurboModuleRegistry","shouldFallbackToLegacyNativeModule","RCTAsyncStorage","get"],"sources":["RCTAsyncStorage.ts"],"sourcesContent":["// @ts-ignore Module '\"react-native\"' has no exported member 'TurboModuleRegistry'.\nimport { NativeModules, TurboModuleRegistry } from 'react-native';\nimport { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';\n\nlet RCTAsyncStorage =\n NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows\n NativeModules['RNC_AsyncSQLiteDBStorage'] ||\n NativeModules['RNCAsyncStorage'];\n\nif (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {\n // TurboModuleRegistry falls back to NativeModules so we don't have to try go\n // assign NativeModules' counterparts if TurboModuleRegistry would resolve\n // with undefined.\n if (TurboModuleRegistry) {\n RCTAsyncStorage =\n TurboModuleRegistry.get('AsyncSQLiteDBStorage') ||\n TurboModuleRegistry.get('AsyncLocalStorage');\n } else {\n RCTAsyncStorage =\n NativeModules['AsyncSQLiteDBStorage'] ||\n NativeModules['AsyncLocalStorage'];\n }\n}\n\nexport default RCTAsyncStorage;\n"],"mappings":"AAAA;AACA,SAASA,aAAT,EAAwBC,mBAAxB,QAAmD,cAAnD;AACA,SAASC,kCAAT,QAAmD,sCAAnD;AAEA,IAAIC,eAAe,GACjBH,aAAa,CAAC,sBAAD,CAAb,IAAyC;AACzCA,aAAa,CAAC,0BAAD,CADb,IAEAA,aAAa,CAAC,iBAAD,CAHf;;AAKA,IAAI,CAACG,eAAD,IAAoBD,kCAAkC,EAA1D,EAA8D;EAC5D;EACA;EACA;EACA,IAAID,mBAAJ,EAAyB;IACvBE,eAAe,GACbF,mBAAmB,CAACG,GAApB,CAAwB,sBAAxB,KACAH,mBAAmB,CAACG,GAApB,CAAwB,mBAAxB,CAFF;EAGD,CAJD,MAIO;IACLD,eAAe,GACbH,aAAa,CAAC,sBAAD,CAAb,IACAA,aAAa,CAAC,mBAAD,CAFf;EAGD;AACF;;AAED,eAAeG,eAAf"}
|
@ -0,0 +1,56 @@
|
||||
export function checkValidArgs(keyValuePairs, callback) {
|
||||
if (!Array.isArray(keyValuePairs) || keyValuePairs.length === 0 || !Array.isArray(keyValuePairs[0])) {
|
||||
throw new Error('[AsyncStorage] Expected array of key-value pairs as first argument to multiSet');
|
||||
}
|
||||
|
||||
if (callback && typeof callback !== 'function') {
|
||||
if (Array.isArray(callback)) {
|
||||
throw new Error('[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?');
|
||||
}
|
||||
|
||||
throw new Error('[AsyncStorage] Expected function as second argument to multiSet');
|
||||
}
|
||||
}
|
||||
export function checkValidInput() {
|
||||
for (var _len = arguments.length, input = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
input[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
const [key, value] = input;
|
||||
|
||||
if (typeof key !== 'string') {
|
||||
console.warn(`[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${key}\n`);
|
||||
}
|
||||
|
||||
if (input.length > 1 && typeof value !== 'string') {
|
||||
if (value == null) {
|
||||
throw new Error(`[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||
} else {
|
||||
console.warn(`[AsyncStorage] The value for key "${key}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
export function convertError(error) {
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const out = new Error(error.message);
|
||||
out.key = error.key;
|
||||
return out;
|
||||
}
|
||||
export function convertErrors(errs) {
|
||||
const errors = ensureArray(errs);
|
||||
return errors ? errors.map(e => convertError(e)) : null;
|
||||
}
|
||||
|
||||
function ensureArray(e) {
|
||||
if (Array.isArray(e)) {
|
||||
return e.length === 0 ? null : e;
|
||||
} else if (e) {
|
||||
return [e];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=helpers.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["checkValidArgs","keyValuePairs","callback","Array","isArray","length","Error","checkValidInput","input","key","value","console","warn","convertError","error","out","message","convertErrors","errs","errors","ensureArray","map","e"],"sources":["helpers.ts"],"sourcesContent":["import type { ErrorLike } from './types';\n\nexport function checkValidArgs(keyValuePairs: unknown[], callback: unknown) {\n if (\n !Array.isArray(keyValuePairs) ||\n keyValuePairs.length === 0 ||\n !Array.isArray(keyValuePairs[0])\n ) {\n throw new Error(\n '[AsyncStorage] Expected array of key-value pairs as first argument to multiSet'\n );\n }\n\n if (callback && typeof callback !== 'function') {\n if (Array.isArray(callback)) {\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?'\n );\n }\n\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet'\n );\n }\n}\n\nexport function checkValidInput(...input: unknown[]) {\n const [key, value] = input;\n\n if (typeof key !== 'string') {\n console.warn(\n `[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\\nKey passed: ${key}\\n`\n );\n }\n\n if (input.length > 1 && typeof value !== 'string') {\n if (value == null) {\n throw new Error(\n `[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n } else {\n console.warn(\n `[AsyncStorage] The value for key \"${key}\" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n }\n }\n}\n\nexport function convertError(error?: ErrorLike): Error | null {\n if (!error) {\n return null;\n }\n\n const out = new Error(error.message);\n (out as any).key = error.key;\n return out;\n}\n\nexport function convertErrors(\n errs?: ErrorLike[]\n): ReadonlyArray<Error | null> | null {\n const errors = ensureArray(errs);\n return errors ? errors.map((e) => convertError(e)) : null;\n}\n\nfunction ensureArray(e?: ErrorLike | ErrorLike[]): ErrorLike[] | null {\n if (Array.isArray(e)) {\n return e.length === 0 ? null : e;\n } else if (e) {\n return [e];\n } else {\n return null;\n }\n}\n"],"mappings":"AAEA,OAAO,SAASA,cAAT,CAAwBC,aAAxB,EAAkDC,QAAlD,EAAqE;EAC1E,IACE,CAACC,KAAK,CAACC,OAAN,CAAcH,aAAd,CAAD,IACAA,aAAa,CAACI,MAAd,KAAyB,CADzB,IAEA,CAACF,KAAK,CAACC,OAAN,CAAcH,aAAa,CAAC,CAAD,CAA3B,CAHH,EAIE;IACA,MAAM,IAAIK,KAAJ,CACJ,gFADI,CAAN;EAGD;;EAED,IAAIJ,QAAQ,IAAI,OAAOA,QAAP,KAAoB,UAApC,EAAgD;IAC9C,IAAIC,KAAK,CAACC,OAAN,CAAcF,QAAd,CAAJ,EAA6B;MAC3B,MAAM,IAAII,KAAJ,CACJ,6IADI,CAAN;IAGD;;IAED,MAAM,IAAIA,KAAJ,CACJ,iEADI,CAAN;EAGD;AACF;AAED,OAAO,SAASC,eAAT,GAA8C;EAAA,kCAAlBC,KAAkB;IAAlBA,KAAkB;EAAA;;EACnD,MAAM,CAACC,GAAD,EAAMC,KAAN,IAAeF,KAArB;;EAEA,IAAI,OAAOC,GAAP,KAAe,QAAnB,EAA6B;IAC3BE,OAAO,CAACC,IAAR,CACG,wBAAuB,OAAOH,GAAI,iHAAgHA,GAAI,IADzJ;EAGD;;EAED,IAAID,KAAK,CAACH,MAAN,GAAe,CAAf,IAAoB,OAAOK,KAAP,KAAiB,QAAzC,EAAmD;IACjD,IAAIA,KAAK,IAAI,IAAb,EAAmB;MACjB,MAAM,IAAIJ,KAAJ,CACH,gJAA+II,KAAM,iBAAgBD,GAAI,IADtK,CAAN;IAGD,CAJD,MAIO;MACLE,OAAO,CAACC,IAAR,CACG,qCAAoCH,GAAI,4GAA2GC,KAAM,iBAAgBD,GAAI,IADhL;IAGD;EACF;AACF;AAED,OAAO,SAASI,YAAT,CAAsBC,KAAtB,EAAuD;EAC5D,IAAI,CAACA,KAAL,EAAY;IACV,OAAO,IAAP;EACD;;EAED,MAAMC,GAAG,GAAG,IAAIT,KAAJ,CAAUQ,KAAK,CAACE,OAAhB,CAAZ;EACCD,GAAD,CAAaN,GAAb,GAAmBK,KAAK,CAACL,GAAzB;EACA,OAAOM,GAAP;AACD;AAED,OAAO,SAASE,aAAT,CACLC,IADK,EAE+B;EACpC,MAAMC,MAAM,GAAGC,WAAW,CAACF,IAAD,CAA1B;EACA,OAAOC,MAAM,GAAGA,MAAM,CAACE,GAAP,CAAYC,CAAD,IAAOT,YAAY,CAACS,CAAD,CAA9B,CAAH,GAAwC,IAArD;AACD;;AAED,SAASF,WAAT,CAAqBE,CAArB,EAAsE;EACpE,IAAInB,KAAK,CAACC,OAAN,CAAckB,CAAd,CAAJ,EAAsB;IACpB,OAAOA,CAAC,CAACjB,MAAF,KAAa,CAAb,GAAiB,IAAjB,GAAwBiB,CAA/B;EACD,CAFD,MAEO,IAAIA,CAAJ,EAAO;IACZ,OAAO,CAACA,CAAD,CAAP;EACD,CAFM,MAEA;IACL,OAAO,IAAP;EACD;AACF"}
|
@ -0,0 +1,34 @@
|
||||
import AsyncStorage from './AsyncStorage';
|
||||
export function useAsyncStorage(key) {
|
||||
return {
|
||||
getItem: function () {
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return AsyncStorage.getItem(key, ...args);
|
||||
},
|
||||
setItem: function () {
|
||||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
args[_key2] = arguments[_key2];
|
||||
}
|
||||
|
||||
return AsyncStorage.setItem(key, ...args);
|
||||
},
|
||||
mergeItem: function () {
|
||||
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
||||
args[_key3] = arguments[_key3];
|
||||
}
|
||||
|
||||
return AsyncStorage.mergeItem(key, ...args);
|
||||
},
|
||||
removeItem: function () {
|
||||
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
||||
args[_key4] = arguments[_key4];
|
||||
}
|
||||
|
||||
return AsyncStorage.removeItem(key, ...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=hooks.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["AsyncStorage","useAsyncStorage","key","getItem","args","setItem","mergeItem","removeItem"],"sources":["hooks.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\nimport type { AsyncStorageHook } from './types';\n\nexport function useAsyncStorage(key: string): AsyncStorageHook {\n return {\n getItem: (...args) => AsyncStorage.getItem(key, ...args),\n setItem: (...args) => AsyncStorage.setItem(key, ...args),\n mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),\n removeItem: (...args) => AsyncStorage.removeItem(key, ...args),\n };\n}\n"],"mappings":"AAAA,OAAOA,YAAP,MAAyB,gBAAzB;AAGA,OAAO,SAASC,eAAT,CAAyBC,GAAzB,EAAwD;EAC7D,OAAO;IACLC,OAAO,EAAE;MAAA,kCAAIC,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACG,OAAb,CAAqBD,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CADJ;IAELC,OAAO,EAAE;MAAA,mCAAID,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACK,OAAb,CAAqBH,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CAFJ;IAGLE,SAAS,EAAE;MAAA,mCAAIF,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACM,SAAb,CAAuBJ,GAAvB,EAA4B,GAAGE,IAA/B,CAAb;IAAA,CAHN;IAILG,UAAU,EAAE;MAAA,mCAAIH,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACO,UAAb,CAAwBL,GAAxB,EAA6B,GAAGE,IAAhC,CAAb;IAAA;EAJP,CAAP;AAMD"}
|
@ -0,0 +1,4 @@
|
||||
import AsyncStorage from './AsyncStorage';
|
||||
export { useAsyncStorage } from './hooks';
|
||||
export default AsyncStorage;
|
||||
//# sourceMappingURL=index.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["AsyncStorage","useAsyncStorage"],"sources":["index.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\n\nexport { useAsyncStorage } from './hooks';\n\nexport type { AsyncStorageStatic } from './types';\n\nexport default AsyncStorage;\n"],"mappings":"AAAA,OAAOA,YAAP,MAAyB,gBAAzB;AAEA,SAASC,eAAT,QAAgC,SAAhC;AAIA,eAAeD,YAAf"}
|
@ -0,0 +1,31 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
export function shouldFallbackToLegacyNativeModule() {
|
||||
var _NativeModules$Native, _NativeModules$Native2;
|
||||
|
||||
const expoConstants = (_NativeModules$Native = NativeModules['NativeUnimoduleProxy']) === null || _NativeModules$Native === void 0 ? void 0 : (_NativeModules$Native2 = _NativeModules$Native.modulesConstants) === null || _NativeModules$Native2 === void 0 ? void 0 : _NativeModules$Native2.ExponentConstants;
|
||||
|
||||
if (expoConstants) {
|
||||
/**
|
||||
* In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.
|
||||
* In bare React Native apps using expo-constants, appOwnership is never defined, so
|
||||
* isLegacySdkVersion will be false in that context.
|
||||
*/
|
||||
const isLegacySdkVersion = expoConstants.appOwnership && !expoConstants.executionEnvironment;
|
||||
/**
|
||||
* Expo managed apps don't include the @react-native-async-storage/async-storage
|
||||
* native modules yet, but the API interface is the same, so we can use the version
|
||||
* exported from React Native still.
|
||||
*
|
||||
* If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this
|
||||
* will likely not be valid anymore, and the package will need to be included in the Expo SDK
|
||||
* to continue to work.
|
||||
*/
|
||||
|
||||
if (isLegacySdkVersion || ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//# sourceMappingURL=shouldFallbackToLegacyNativeModule.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"names":["NativeModules","shouldFallbackToLegacyNativeModule","expoConstants","modulesConstants","ExponentConstants","isLegacySdkVersion","appOwnership","executionEnvironment","includes"],"sources":["shouldFallbackToLegacyNativeModule.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\n\nexport function shouldFallbackToLegacyNativeModule(): boolean {\n const expoConstants =\n NativeModules['NativeUnimoduleProxy']?.modulesConstants?.ExponentConstants;\n\n if (expoConstants) {\n /**\n * In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.\n * In bare React Native apps using expo-constants, appOwnership is never defined, so\n * isLegacySdkVersion will be false in that context.\n */\n const isLegacySdkVersion =\n expoConstants.appOwnership && !expoConstants.executionEnvironment;\n\n /**\n * Expo managed apps don't include the @react-native-async-storage/async-storage\n * native modules yet, but the API interface is the same, so we can use the version\n * exported from React Native still.\n *\n * If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this\n * will likely not be valid anymore, and the package will need to be included in the Expo SDK\n * to continue to work.\n */\n if (\n isLegacySdkVersion ||\n ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)\n ) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":"AAAA,SAASA,aAAT,QAA8B,cAA9B;AAEA,OAAO,SAASC,kCAAT,GAAuD;EAAA;;EAC5D,MAAMC,aAAa,4BACjBF,aAAa,CAAC,sBAAD,CADI,oFACjB,sBAAuCG,gBADtB,2DACjB,uBAAyDC,iBAD3D;;EAGA,IAAIF,aAAJ,EAAmB;IACjB;AACJ;AACA;AACA;AACA;IACI,MAAMG,kBAAkB,GACtBH,aAAa,CAACI,YAAd,IAA8B,CAACJ,aAAa,CAACK,oBAD/C;IAGA;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;IACI,IACEF,kBAAkB,IAClB,CAAC,aAAD,EAAgB,YAAhB,EAA8BG,QAA9B,CAAuCN,aAAa,CAACK,oBAArD,CAFF,EAGE;MACA,OAAO,IAAP;IACD;EACF;;EAED,OAAO,KAAP;AACD"}
|
@ -0,0 +1,2 @@
|
||||
|
||||
//# sourceMappingURL=types.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import type { AsyncStorageStatic } from './types';
|
||||
declare const AsyncStorage: AsyncStorageStatic;
|
||||
export default AsyncStorage;
|
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import type { AsyncStorageStatic } from './types';
|
||||
/**
|
||||
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||
* storage system that is global to the app. It should be used instead of
|
||||
* LocalStorage.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||
*/
|
||||
declare const AsyncStorage: AsyncStorageStatic;
|
||||
export default AsyncStorage;
|
@ -0,0 +1,2 @@
|
||||
declare let RCTAsyncStorage: any;
|
||||
export default RCTAsyncStorage;
|
@ -0,0 +1,5 @@
|
||||
import type { ErrorLike } from './types';
|
||||
export declare function checkValidArgs(keyValuePairs: unknown[], callback: unknown): void;
|
||||
export declare function checkValidInput(...input: unknown[]): void;
|
||||
export declare function convertError(error?: ErrorLike): Error | null;
|
||||
export declare function convertErrors(errs?: ErrorLike[]): ReadonlyArray<Error | null> | null;
|
@ -0,0 +1,2 @@
|
||||
import type { AsyncStorageHook } from './types';
|
||||
export declare function useAsyncStorage(key: string): AsyncStorageHook;
|
@ -0,0 +1,4 @@
|
||||
import AsyncStorage from './AsyncStorage';
|
||||
export { useAsyncStorage } from './hooks';
|
||||
export type { AsyncStorageStatic } from './types';
|
||||
export default AsyncStorage;
|
@ -0,0 +1 @@
|
||||
export declare function shouldFallbackToLegacyNativeModule(): boolean;
|
@ -0,0 +1,113 @@
|
||||
export declare type ErrorLike = {
|
||||
message: string;
|
||||
key: string;
|
||||
};
|
||||
export declare type Callback = (error?: Error | null) => void;
|
||||
export declare type CallbackWithResult<T> = (error?: Error | null, result?: T | null) => void;
|
||||
export declare type KeyValuePair = [string, string | null];
|
||||
export declare type MultiCallback = (errors?: readonly (Error | null)[] | null) => void;
|
||||
export declare type MultiGetCallback = (errors?: readonly (Error | null)[] | null, result?: readonly KeyValuePair[]) => void;
|
||||
export declare type MultiRequest = {
|
||||
keys: readonly string[];
|
||||
callback?: MultiGetCallback;
|
||||
keyIndex: number;
|
||||
resolve?: (result: readonly KeyValuePair[]) => void;
|
||||
reject?: (error?: any) => void;
|
||||
};
|
||||
export declare type AsyncStorageHook = {
|
||||
getItem: (callback?: CallbackWithResult<string>) => Promise<string | null>;
|
||||
setItem: (value: string, callback?: Callback) => Promise<void>;
|
||||
mergeItem: (value: string, callback?: Callback) => Promise<void>;
|
||||
removeItem: (callback?: Callback) => Promise<void>;
|
||||
};
|
||||
/**
|
||||
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||
* storage system that is global to the app. It should be used instead of
|
||||
* LocalStorage.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||
*/
|
||||
export declare type AsyncStorageStatic = {
|
||||
/**
|
||||
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||
*/
|
||||
getItem: (key: string, callback?: CallbackWithResult<string>) => Promise<string | null>;
|
||||
/**
|
||||
* Sets the value for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||
*/
|
||||
setItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||
/**
|
||||
* Removes an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||
*/
|
||||
removeItem: (key: string, callback?: Callback) => Promise<void>;
|
||||
/**
|
||||
* Merges an existing `key` value with an input value, assuming both values
|
||||
* are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||
*/
|
||||
mergeItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||
/**
|
||||
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||
* your app's keys.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||
*/
|
||||
clear: (callback?: Callback) => Promise<void>;
|
||||
/**
|
||||
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||
*/
|
||||
getAllKeys: (callback?: CallbackWithResult<readonly string[]>) => Promise<readonly string[]>;
|
||||
/**
|
||||
* The following batched functions are useful for executing a lot of
|
||||
* operations at once, allowing for native optimizations and provide the
|
||||
* convenience of a single callback after all operations are complete.
|
||||
*
|
||||
* These functions return arrays of errors, potentially one for every key.
|
||||
* For key-specific errors, the Error object will have a key property to
|
||||
* indicate which key caused the error.
|
||||
*/
|
||||
/**
|
||||
* Flushes any pending requests using a single batch call to get the data.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||
* */
|
||||
flushGetRequests: () => void;
|
||||
/**
|
||||
* This allows you to batch the fetching of items given an array of `key`
|
||||
* inputs. Your callback will be invoked with an array of corresponding
|
||||
* key-value pairs found.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||
*/
|
||||
multiGet: (keys: readonly string[], callback?: MultiGetCallback) => Promise<readonly KeyValuePair[]>;
|
||||
/**
|
||||
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||
* the operation completes you'll get a single callback with any errors.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||
*/
|
||||
multiSet: (keyValuePairs: [string, string][], callback?: MultiCallback) => Promise<void>;
|
||||
/**
|
||||
* Call this to batch the deletion of all keys in the `keys` array.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||
*/
|
||||
multiRemove: (keys: readonly string[], callback?: MultiCallback) => Promise<void>;
|
||||
/**
|
||||
* Batch operation to merge in existing and new values for a given set of
|
||||
* keys. This assumes that the values are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||
*/
|
||||
multiMerge: (keyValuePairs: [string, string][], callback?: MultiCallback) => Promise<void>;
|
||||
};
|
@ -0,0 +1,385 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||
3893A2E123C509D1009200E3 /* RNCAsyncStorage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||
3893A2E223C509D1009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||
3893A2E523C50AFE009200E3 /* RNCAsyncStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||
3893A2E623C50AFE009200E3 /* RNCAsyncStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||
3893A2E823C50AFE009200E3 /* RNCAsyncStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */; };
|
||||
3893A2EB23C50AFE009200E3 /* RNCAsyncStorage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||
3893A2EC23C50AFE009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
3893A2EA23C50AFE009200E3 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = include/RNCAsyncStorage;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
3893A2EB23C50AFE009200E3 /* RNCAsyncStorage.h in CopyFiles */,
|
||||
3893A2EC23C50AFE009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
3893A2E123C509D1009200E3 /* RNCAsyncStorage.h in CopyFiles */,
|
||||
3893A2E223C509D1009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCAsyncStorage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNCAsyncStorageDelegate.h; path = ../ios/RNCAsyncStorageDelegate.h; sourceTree = "<group>"; };
|
||||
3893A2F023C50AFE009200E3 /* libRNCAsyncStorage-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRNCAsyncStorage-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNCAsyncStorage.h; path = ../ios/RNCAsyncStorage.h; sourceTree = "<group>"; };
|
||||
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNCAsyncStorage.m; path = ../ios/RNCAsyncStorage.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
3893A2E923C50AFE009200E3 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */,
|
||||
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */,
|
||||
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
3893A2F023C50AFE009200E3 /* libRNCAsyncStorage-macOS.a */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
19F94B1D2239A948006921A9 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */,
|
||||
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3893A2E423C50AFE009200E3 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3893A2E523C50AFE009200E3 /* RNCAsyncStorage.h in Headers */,
|
||||
3893A2E623C50AFE009200E3 /* RNCAsyncStorageDelegate.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
3893A2E323C50AFE009200E3 /* RNCAsyncStorage-macOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3893A2ED23C50AFE009200E3 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage-macOS" */;
|
||||
buildPhases = (
|
||||
3893A2E423C50AFE009200E3 /* Headers */,
|
||||
3893A2E723C50AFE009200E3 /* Sources */,
|
||||
3893A2E923C50AFE009200E3 /* Frameworks */,
|
||||
3893A2EA23C50AFE009200E3 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "RNCAsyncStorage-macOS";
|
||||
productName = RCTDataManager;
|
||||
productReference = 3893A2F023C50AFE009200E3 /* libRNCAsyncStorage-macOS.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */;
|
||||
buildPhases = (
|
||||
19F94B1D2239A948006921A9 /* Headers */,
|
||||
58B511D71A9E6C8500147676 /* Sources */,
|
||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RNCAsyncStorage;
|
||||
productName = RCTDataManager;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0830;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
58B511DA1A9E6C8500147676 = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = 58B511D21A9E6C8500147676;
|
||||
productRefGroup = 58B511D21A9E6C8500147676;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */,
|
||||
3893A2E323C50AFE009200E3 /* RNCAsyncStorage-macOS */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
3893A2E723C50AFE009200E3 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3893A2E823C50AFE009200E3 /* RNCAsyncStorage.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
3893A2EE23C50AFE009200E3 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3893A2EF23C50AFE009200E3 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNCAsyncStorage;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511F11A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNCAsyncStorage;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
3893A2ED23C50AFE009200E3 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage-macOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3893A2EE23C50AFE009200E3 /* Debug */,
|
||||
3893A2EF23C50AFE009200E3 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511ED1A9E6C8500147676 /* Debug */,
|
||||
58B511EE1A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511F01A9E6C8500147676 /* Debug */,
|
||||
58B511F11A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1120"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3893A2E323C50AFE009200E3"
|
||||
BuildableName = "libRNCAsyncStorage-macOS.a"
|
||||
BlueprintName = "RNCAsyncStorage-macOS"
|
||||
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3893A2E323C50AFE009200E3"
|
||||
BuildableName = "libRNCAsyncStorage-macOS.a"
|
||||
BlueprintName = "RNCAsyncStorage-macOS"
|
||||
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1120"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
|
||||
BuildableName = "libRNCAsyncStorage.a"
|
||||
BlueprintName = "RNCAsyncStorage"
|
||||
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
|
||||
BuildableName = "libRNCAsyncStorage.a"
|
||||
BlueprintName = "RNCAsyncStorage"
|
||||
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -0,0 +1 @@
|
||||
../../../../react-native/cli.js
|
195
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/package.json
generated
vendored
195
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/package.json
generated
vendored
@ -0,0 +1,195 @@
|
||||
{
|
||||
"name": "@react-native-async-storage/async-storage",
|
||||
"version": "1.17.12",
|
||||
"description": "Asynchronous, persistent, key-value storage system for React Native.",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"module": "lib/module/index.js",
|
||||
"react-native": "src/index.ts",
|
||||
"types": "lib/typescript/index.d.ts",
|
||||
"files": [
|
||||
"RNCAsyncStorage.podspec",
|
||||
"android/build.gradle",
|
||||
"android/src",
|
||||
"android/testresults.gradle",
|
||||
"ios/",
|
||||
"jest/",
|
||||
"lib/",
|
||||
"macos/",
|
||||
"src/",
|
||||
"windows/"
|
||||
],
|
||||
"author": "Krzysztof Borowy <hello@krizzu.dev>",
|
||||
"contributors": [
|
||||
"Evan Bacon <bacon@expo.io> (https://github.com/evanbacon)",
|
||||
"Tommy Nguyen <4123478+tido64@users.noreply.github.com> (https://github.com/tido64)"
|
||||
],
|
||||
"homepage": "https://github.com/react-native-async-storage/async-storage#readme",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react native",
|
||||
"async storage",
|
||||
"asyncstorage",
|
||||
"storage"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/react-native-async-storage/async-storage.git"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "concurrently yarn:format:*",
|
||||
"format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')",
|
||||
"format:js": "prettier --write $(git ls-files '*.js' '*.json' '*.md' '*.ts' '*.tsx' '*.yml')",
|
||||
"prepare": "bob build",
|
||||
"start": "react-native start",
|
||||
"start:android": "react-native run-android",
|
||||
"start:ios": "react-native run-ios --project-path example/ios",
|
||||
"start:macos": "react-native run-macos --project-path example/macos --scheme AsyncStorageExample",
|
||||
"start:web": "expo start:web",
|
||||
"start:windows": "install-windows-test-app -p example/windows && react-native run-windows --root example --logging --no-packager --no-telemetry",
|
||||
"build:e2e:android": "scripts/android_e2e.sh 'build'",
|
||||
"build:e2e:ios": "scripts/ios_e2e.sh 'build'",
|
||||
"build:e2e:macos": "scripts/macos_e2e.sh 'build'",
|
||||
"bundle:android": "scripts/android_e2e.sh 'bundle'",
|
||||
"bundle:ios": "scripts/ios_e2e.sh 'bundle'",
|
||||
"bundle:macos": "react-native bundle --entry-file index.ts --platform macos --bundle-output example/index.macos.jsbundle",
|
||||
"test": "concurrently -n lint,ts yarn:test:lint yarn:test:ts",
|
||||
"test:lint": "eslint src/**/*.ts example/**/*.ts jest/*.js",
|
||||
"test:ts": "tsc --project tsconfig.all.json",
|
||||
"test:e2e:android": "detox test -c android.emu.release --maxConcurrency 1",
|
||||
"test:e2e:ios": "detox test -c ios.sim.release --maxConcurrency 1",
|
||||
"test:e2e:macos": "scripts/macos_e2e.sh 'test'"
|
||||
},
|
||||
"dependencies": {
|
||||
"merge-options": "^3.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": "^0.0.0-0 || 0.60 - 0.71 || 1000.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.0",
|
||||
"@babel/preset-env": "^7.1.6",
|
||||
"@react-native-community/eslint-config": "^3.0.0",
|
||||
"@semantic-release/changelog": "^6.0.0",
|
||||
"@semantic-release/git": "^10.0.0",
|
||||
"@types/lodash": "^4.14.184",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-native": "^0.68.0",
|
||||
"concurrently": "^6.4.0",
|
||||
"detox": "^19.4.5",
|
||||
"eslint": "^8.0.0",
|
||||
"expo": "^45.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-circus": "^26.6.1",
|
||||
"lodash": "^4.17.21",
|
||||
"prettier": "^2.5.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-native": "^0.68.0",
|
||||
"react-native-builder-bob": "^0.18.0",
|
||||
"react-native-macos": "^0.68.0",
|
||||
"react-native-test-app": "^2.3.10",
|
||||
"react-native-web": "^0.17.0",
|
||||
"react-native-windows": "^0.68.0",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"semantic-release": "^19.0.0",
|
||||
"typescript": "^4.5.0"
|
||||
},
|
||||
"packageManager": "yarn@3.4.1",
|
||||
"resolutions": {
|
||||
"npm/chalk": "^4.1.2"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native",
|
||||
"setupFiles": [
|
||||
"./example/jest.setup.js"
|
||||
]
|
||||
},
|
||||
"detox": {
|
||||
"test-runner": "jest",
|
||||
"runner-config": "example/e2e/config.json",
|
||||
"configurations": {
|
||||
"ios.sim.release": {
|
||||
"binaryPath": "example/ios/build/Build/Products/Release-iphonesimulator/ReactTestApp.app",
|
||||
"type": "ios.simulator",
|
||||
"device": {
|
||||
"type": "iPhone 13"
|
||||
}
|
||||
},
|
||||
"android.emu.release": {
|
||||
"binaryPath": "example/android/app/build/outputs/apk/release/app-release.apk",
|
||||
"testBinaryPath": "example/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
|
||||
"type": "android.emulator",
|
||||
"device": {
|
||||
"avdName": "E2E_API_30",
|
||||
"utilBinaryPaths": [
|
||||
"/var/tmp/test-butler.apk"
|
||||
]
|
||||
}
|
||||
},
|
||||
"android.emu.release.next": {
|
||||
"binaryPath": "example/android/app/build/outputs/apk/next/app-next.apk",
|
||||
"testBinaryPath": "example/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
|
||||
"type": "android.emulator",
|
||||
"device": {
|
||||
"avdName": "E2E_API_30",
|
||||
"utilBinaryPaths": [
|
||||
"/var/tmp/test-butler.apk"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"@react-native-community",
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"dot-notation": "off"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"endOfLine": "auto",
|
||||
"singleQuote": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.md",
|
||||
"options": {
|
||||
"proseWrap": "always"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"react-native-builder-bob": {
|
||||
"source": "src",
|
||||
"output": "lib",
|
||||
"targets": [
|
||||
"commonjs",
|
||||
"module",
|
||||
"typescript"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"branches": [
|
||||
"master"
|
||||
],
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"@semantic-release/npm",
|
||||
"@semantic-release/github",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": [
|
||||
"CHANGELOG.md",
|
||||
"package.json"
|
||||
],
|
||||
"message": "chore(release): ${nextRelease.version} [skip ci]"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,356 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
checkValidArgs,
|
||||
checkValidInput,
|
||||
convertError,
|
||||
convertErrors,
|
||||
} from './helpers';
|
||||
import RCTAsyncStorage from './RCTAsyncStorage';
|
||||
import type {
|
||||
AsyncStorageStatic,
|
||||
ErrorLike,
|
||||
KeyValuePair,
|
||||
MultiRequest,
|
||||
} from './types';
|
||||
|
||||
if (!RCTAsyncStorage) {
|
||||
throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
|
||||
|
||||
To fix this issue try these steps:
|
||||
|
||||
• Rebuild and restart the app.
|
||||
|
||||
• Run the packager with \`--reset-cache\` flag.
|
||||
|
||||
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||
|
||||
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||
|
||||
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||
`);
|
||||
}
|
||||
|
||||
/**
|
||||
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||
* storage system that is global to the app. It should be used instead of
|
||||
* LocalStorage.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||
*/
|
||||
const AsyncStorage = ((): AsyncStorageStatic => {
|
||||
let _getRequests: MultiRequest[] = [];
|
||||
let _getKeys: string[] = [];
|
||||
let _immediate: ReturnType<typeof setImmediate> | null = null;
|
||||
|
||||
return {
|
||||
/**
|
||||
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||
*/
|
||||
getItem: (key, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key);
|
||||
RCTAsyncStorage.multiGet(
|
||||
[key],
|
||||
(errors?: ErrorLike[], result?: string[][]) => {
|
||||
// Unpack result to get value from [[key,value]]
|
||||
const value = result?.[0]?.[1] ? result[0][1] : null;
|
||||
const errs = convertErrors(errors);
|
||||
callback?.(errs?.[0], value);
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the value for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||
*/
|
||||
setItem: (key, value, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key, value);
|
||||
RCTAsyncStorage.multiSet([[key, value]], (errors?: ErrorLike[]) => {
|
||||
const errs = convertErrors(errors);
|
||||
callback?.(errs?.[0]);
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||
*/
|
||||
removeItem: (key, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key);
|
||||
RCTAsyncStorage.multiRemove([key], (errors?: ErrorLike[]) => {
|
||||
const errs = convertErrors(errors);
|
||||
callback?.(errs?.[0]);
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges an existing `key` value with an input value, assuming both values
|
||||
* are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||
*/
|
||||
mergeItem: (key, value, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
checkValidInput(key, value);
|
||||
RCTAsyncStorage.multiMerge([[key, value]], (errors?: ErrorLike[]) => {
|
||||
const errs = convertErrors(errors);
|
||||
callback?.(errs?.[0]);
|
||||
if (errs) {
|
||||
reject(errs[0]);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||
* your app's keys.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||
*/
|
||||
clear: (callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.clear((error?: ErrorLike) => {
|
||||
const err = convertError(error);
|
||||
callback?.(err);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||
*/
|
||||
getAllKeys: (callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.getAllKeys((error?: ErrorLike, keys?: string[]) => {
|
||||
const err = convertError(error);
|
||||
callback?.(err, keys);
|
||||
if (keys) {
|
||||
resolve(keys);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The following batched functions are useful for executing a lot of
|
||||
* operations at once, allowing for native optimizations and provide the
|
||||
* convenience of a single callback after all operations are complete.
|
||||
*
|
||||
* These functions return arrays of errors, potentially one for every key.
|
||||
* For key-specific errors, the Error object will have a key property to
|
||||
* indicate which key caused the error.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Flushes any pending requests using a single batch call to get the data.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||
* */
|
||||
flushGetRequests: () => {
|
||||
const getRequests = _getRequests;
|
||||
const getKeys = _getKeys;
|
||||
|
||||
_getRequests = [];
|
||||
_getKeys = [];
|
||||
|
||||
RCTAsyncStorage.multiGet(
|
||||
getKeys,
|
||||
(errors?: ErrorLike[], result?: string[][]) => {
|
||||
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
|
||||
// it's much, much faster in practice for the data sets we deal with (we avoid
|
||||
// allocating result pair arrays). This was heavily benchmarked.
|
||||
//
|
||||
// Is there a way to avoid using the map but fix the bug in this breaking test?
|
||||
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
|
||||
const map: Record<string, string> = {};
|
||||
result?.forEach(([key, value]) => {
|
||||
map[key] = value;
|
||||
return value;
|
||||
});
|
||||
const reqLength = getRequests.length;
|
||||
|
||||
/**
|
||||
* As mentioned few lines above, this method could be called with the array of potential error,
|
||||
* in case of anything goes wrong. The problem is, if any of the batched calls fails
|
||||
* the rest of them would fail too, but the error would be consumed by just one. The rest
|
||||
* would simply return `undefined` as their result, rendering false negatives.
|
||||
*
|
||||
* In order to avoid this situation, in case of any call failing,
|
||||
* the rest of them will be rejected as well (with the same error).
|
||||
*/
|
||||
const errorList = convertErrors(errors);
|
||||
const error = errorList?.length ? errorList[0] : null;
|
||||
|
||||
for (let i = 0; i < reqLength; i++) {
|
||||
const request = getRequests[i];
|
||||
if (error) {
|
||||
request.callback?.(errorList);
|
||||
request.reject?.(error);
|
||||
continue;
|
||||
}
|
||||
const requestResult = request.keys.map<KeyValuePair>((key) => [
|
||||
key,
|
||||
map[key],
|
||||
]);
|
||||
request.callback?.(null, requestResult);
|
||||
request.resolve?.(requestResult);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* This allows you to batch the fetching of items given an array of `key`
|
||||
* inputs. Your callback will be invoked with an array of corresponding
|
||||
* key-value pairs found.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||
*/
|
||||
multiGet: (keys, callback) => {
|
||||
if (!_immediate) {
|
||||
_immediate = setImmediate(() => {
|
||||
_immediate = null;
|
||||
AsyncStorage.flushGetRequests();
|
||||
});
|
||||
}
|
||||
|
||||
const getRequest: MultiRequest = {
|
||||
keys: keys,
|
||||
callback: callback,
|
||||
// do we need this?
|
||||
keyIndex: _getKeys.length,
|
||||
resolve: null as any,
|
||||
reject: null as any,
|
||||
};
|
||||
|
||||
const promiseResult = new Promise<readonly KeyValuePair[]>(
|
||||
(resolve, reject) => {
|
||||
getRequest.resolve = resolve;
|
||||
getRequest.reject = reject;
|
||||
}
|
||||
);
|
||||
|
||||
_getRequests.push(getRequest);
|
||||
// avoid fetching duplicates
|
||||
keys.forEach((key) => {
|
||||
if (_getKeys.indexOf(key) === -1) {
|
||||
_getKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
return promiseResult;
|
||||
},
|
||||
|
||||
/**
|
||||
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||
* the operation completes you'll get a single callback with any errors.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||
*/
|
||||
multiSet: (keyValuePairs, callback) => {
|
||||
checkValidArgs(keyValuePairs, callback);
|
||||
return new Promise((resolve, reject) => {
|
||||
keyValuePairs.forEach(([key, value]) => {
|
||||
checkValidInput(key, value);
|
||||
});
|
||||
|
||||
RCTAsyncStorage.multiSet(keyValuePairs, (errors?: ErrorLike[]) => {
|
||||
const error = convertErrors(errors);
|
||||
callback?.(error);
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this to batch the deletion of all keys in the `keys` array.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||
*/
|
||||
multiRemove: (keys, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
keys.forEach((key) => checkValidInput(key));
|
||||
|
||||
RCTAsyncStorage.multiRemove(keys, (errors?: ErrorLike[]) => {
|
||||
const error = convertErrors(errors);
|
||||
callback?.(error);
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Batch operation to merge in existing and new values for a given set of
|
||||
* keys. This assumes that the values are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||
*/
|
||||
multiMerge: (keyValuePairs, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge(keyValuePairs, (errors?: ErrorLike[]) => {
|
||||
const error = convertErrors(errors);
|
||||
callback?.(error);
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
export default AsyncStorage;
|
173
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts
generated
vendored
173
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts
generated
vendored
@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// @ts-ignore Cannot find module 'merge-options' or its corresponding type declarations
|
||||
import mergeOptions from 'merge-options';
|
||||
import type {
|
||||
AsyncStorageStatic,
|
||||
MultiCallback,
|
||||
MultiGetCallback,
|
||||
} from './types';
|
||||
|
||||
const merge = mergeOptions.bind({
|
||||
concatArrays: true,
|
||||
ignoreUndefined: true,
|
||||
});
|
||||
|
||||
function mergeLocalStorageItem(key: string, value: string) {
|
||||
const oldValue = window.localStorage.getItem(key);
|
||||
if (oldValue) {
|
||||
const oldObject = JSON.parse(oldValue);
|
||||
const newObject = JSON.parse(value);
|
||||
const nextValue = JSON.stringify(merge(oldObject, newObject));
|
||||
window.localStorage.setItem(key, nextValue);
|
||||
} else {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function createPromise<Result, Callback extends Function>(
|
||||
getValue: () => Result,
|
||||
callback?: Callback
|
||||
): Promise<Result> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const value = getValue();
|
||||
callback?.(null, value);
|
||||
resolve(value);
|
||||
} catch (err) {
|
||||
callback?.(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createPromiseAll<ReturnType, Result, ResultProcessor extends Function>(
|
||||
promises: Promise<Result>[],
|
||||
callback?: MultiCallback | MultiGetCallback,
|
||||
processResult?: ResultProcessor
|
||||
): Promise<ReturnType> {
|
||||
return Promise.all(promises).then(
|
||||
(result) => {
|
||||
const value = processResult?.(result) ?? null;
|
||||
callback?.(null, value);
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
(errors) => {
|
||||
callback?.(errors);
|
||||
return Promise.reject(errors);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const AsyncStorage: AsyncStorageStatic = {
|
||||
/**
|
||||
* Fetches `key` value.
|
||||
*/
|
||||
getItem: (key, callback) => {
|
||||
return createPromise(() => window.localStorage.getItem(key), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets `value` for `key`.
|
||||
*/
|
||||
setItem: (key, value, callback) => {
|
||||
return createPromise(
|
||||
() => window.localStorage.setItem(key, value),
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a `key`
|
||||
*/
|
||||
removeItem: (key, callback) => {
|
||||
return createPromise(() => window.localStorage.removeItem(key), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges existing value with input value, assuming they are stringified JSON.
|
||||
*/
|
||||
mergeItem: (key, value, callback) => {
|
||||
return createPromise(() => mergeLocalStorageItem(key, value), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* AsyncStorage for the domain.
|
||||
*/
|
||||
clear: (callback) => {
|
||||
return createPromise(() => window.localStorage.clear(), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to the app, for all callers, libraries, etc.
|
||||
*/
|
||||
getAllKeys: (callback) => {
|
||||
return createPromise(() => {
|
||||
const numberOfKeys = window.localStorage.length;
|
||||
const keys: string[] = [];
|
||||
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||
const key = window.localStorage.key(i) || '';
|
||||
keys.push(key);
|
||||
}
|
||||
return keys;
|
||||
}, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* (stub) Flushes any pending requests using a single batch call to get the data.
|
||||
*/
|
||||
flushGetRequests: () => undefined,
|
||||
|
||||
/**
|
||||
* multiGet resolves to an array of key-value pair arrays that matches the
|
||||
* input format of multiSet.
|
||||
*
|
||||
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||
*/
|
||||
multiGet: (keys, callback) => {
|
||||
const promises = keys.map((key) => AsyncStorage.getItem(key));
|
||||
const processResult = (result: string[]) =>
|
||||
result.map((value, i) => [keys[i], value]);
|
||||
return createPromiseAll(promises, callback, processResult);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of key-value array pairs.
|
||||
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiSet: (keyValuePairs, callback) => {
|
||||
const promises = keyValuePairs.map((item) =>
|
||||
AsyncStorage.setItem(item[0], item[1])
|
||||
);
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all the keys in the `keys` array.
|
||||
*/
|
||||
multiRemove: (keys, callback) => {
|
||||
const promises = keys.map((key) => AsyncStorage.removeItem(key));
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of key-value array pairs and merges them with existing
|
||||
* values, assuming they are stringified JSON.
|
||||
*
|
||||
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiMerge: (keyValuePairs, callback) => {
|
||||
const promises = keyValuePairs.map((item) =>
|
||||
AsyncStorage.mergeItem(item[0], item[1])
|
||||
);
|
||||
return createPromiseAll(promises, callback);
|
||||
},
|
||||
};
|
||||
|
||||
export default AsyncStorage;
|
@ -0,0 +1,25 @@
|
||||
// @ts-ignore Module '"react-native"' has no exported member 'TurboModuleRegistry'.
|
||||
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
||||
import { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';
|
||||
|
||||
let RCTAsyncStorage =
|
||||
NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows
|
||||
NativeModules['RNC_AsyncSQLiteDBStorage'] ||
|
||||
NativeModules['RNCAsyncStorage'];
|
||||
|
||||
if (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {
|
||||
// TurboModuleRegistry falls back to NativeModules so we don't have to try go
|
||||
// assign NativeModules' counterparts if TurboModuleRegistry would resolve
|
||||
// with undefined.
|
||||
if (TurboModuleRegistry) {
|
||||
RCTAsyncStorage =
|
||||
TurboModuleRegistry.get('AsyncSQLiteDBStorage') ||
|
||||
TurboModuleRegistry.get('AsyncLocalStorage');
|
||||
} else {
|
||||
RCTAsyncStorage =
|
||||
NativeModules['AsyncSQLiteDBStorage'] ||
|
||||
NativeModules['AsyncLocalStorage'];
|
||||
}
|
||||
}
|
||||
|
||||
export default RCTAsyncStorage;
|
74
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/helpers.ts
generated
vendored
74
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/helpers.ts
generated
vendored
@ -0,0 +1,74 @@
|
||||
import type { ErrorLike } from './types';
|
||||
|
||||
export function checkValidArgs(keyValuePairs: unknown[], callback: unknown) {
|
||||
if (
|
||||
!Array.isArray(keyValuePairs) ||
|
||||
keyValuePairs.length === 0 ||
|
||||
!Array.isArray(keyValuePairs[0])
|
||||
) {
|
||||
throw new Error(
|
||||
'[AsyncStorage] Expected array of key-value pairs as first argument to multiSet'
|
||||
);
|
||||
}
|
||||
|
||||
if (callback && typeof callback !== 'function') {
|
||||
if (Array.isArray(callback)) {
|
||||
throw new Error(
|
||||
'[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?'
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'[AsyncStorage] Expected function as second argument to multiSet'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkValidInput(...input: unknown[]) {
|
||||
const [key, value] = input;
|
||||
|
||||
if (typeof key !== 'string') {
|
||||
console.warn(
|
||||
`[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${key}\n`
|
||||
);
|
||||
}
|
||||
|
||||
if (input.length > 1 && typeof value !== 'string') {
|
||||
if (value == null) {
|
||||
throw new Error(
|
||||
`[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\nPassed value: ${value}\nPassed key: ${key}\n`
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
`[AsyncStorage] The value for key "${key}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${key}\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function convertError(error?: ErrorLike): Error | null {
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const out = new Error(error.message);
|
||||
(out as any).key = error.key;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function convertErrors(
|
||||
errs?: ErrorLike[]
|
||||
): ReadonlyArray<Error | null> | null {
|
||||
const errors = ensureArray(errs);
|
||||
return errors ? errors.map((e) => convertError(e)) : null;
|
||||
}
|
||||
|
||||
function ensureArray(e?: ErrorLike | ErrorLike[]): ErrorLike[] | null {
|
||||
if (Array.isArray(e)) {
|
||||
return e.length === 0 ? null : e;
|
||||
} else if (e) {
|
||||
return [e];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
11
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/hooks.ts
generated
vendored
11
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/hooks.ts
generated
vendored
@ -0,0 +1,11 @@
|
||||
import AsyncStorage from './AsyncStorage';
|
||||
import type { AsyncStorageHook } from './types';
|
||||
|
||||
export function useAsyncStorage(key: string): AsyncStorageHook {
|
||||
return {
|
||||
getItem: (...args) => AsyncStorage.getItem(key, ...args),
|
||||
setItem: (...args) => AsyncStorage.setItem(key, ...args),
|
||||
mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),
|
||||
removeItem: (...args) => AsyncStorage.removeItem(key, ...args),
|
||||
};
|
||||
}
|
7
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/index.ts
generated
vendored
7
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/index.ts
generated
vendored
@ -0,0 +1,7 @@
|
||||
import AsyncStorage from './AsyncStorage';
|
||||
|
||||
export { useAsyncStorage } from './hooks';
|
||||
|
||||
export type { AsyncStorageStatic } from './types';
|
||||
|
||||
export default AsyncStorage;
|
@ -0,0 +1,34 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
export function shouldFallbackToLegacyNativeModule(): boolean {
|
||||
const expoConstants =
|
||||
NativeModules['NativeUnimoduleProxy']?.modulesConstants?.ExponentConstants;
|
||||
|
||||
if (expoConstants) {
|
||||
/**
|
||||
* In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.
|
||||
* In bare React Native apps using expo-constants, appOwnership is never defined, so
|
||||
* isLegacySdkVersion will be false in that context.
|
||||
*/
|
||||
const isLegacySdkVersion =
|
||||
expoConstants.appOwnership && !expoConstants.executionEnvironment;
|
||||
|
||||
/**
|
||||
* Expo managed apps don't include the @react-native-async-storage/async-storage
|
||||
* native modules yet, but the API interface is the same, so we can use the version
|
||||
* exported from React Native still.
|
||||
*
|
||||
* If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this
|
||||
* will likely not be valid anymore, and the package will need to be included in the Expo SDK
|
||||
* to continue to work.
|
||||
*/
|
||||
if (
|
||||
isLegacySdkVersion ||
|
||||
['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
155
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/types.ts
generated
vendored
155
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/types.ts
generated
vendored
@ -0,0 +1,155 @@
|
||||
export type ErrorLike = {
|
||||
message: string;
|
||||
key: string;
|
||||
};
|
||||
|
||||
export type Callback = (error?: Error | null) => void;
|
||||
|
||||
export type CallbackWithResult<T> = (
|
||||
error?: Error | null,
|
||||
result?: T | null
|
||||
) => void;
|
||||
|
||||
export type KeyValuePair = [string, string | null];
|
||||
|
||||
export type MultiCallback = (errors?: readonly (Error | null)[] | null) => void;
|
||||
|
||||
export type MultiGetCallback = (
|
||||
errors?: readonly (Error | null)[] | null,
|
||||
result?: readonly KeyValuePair[]
|
||||
) => void;
|
||||
|
||||
export type MultiRequest = {
|
||||
keys: readonly string[];
|
||||
callback?: MultiGetCallback;
|
||||
keyIndex: number;
|
||||
resolve?: (result: readonly KeyValuePair[]) => void;
|
||||
reject?: (error?: any) => void;
|
||||
};
|
||||
|
||||
export type AsyncStorageHook = {
|
||||
getItem: (callback?: CallbackWithResult<string>) => Promise<string | null>;
|
||||
setItem: (value: string, callback?: Callback) => Promise<void>;
|
||||
mergeItem: (value: string, callback?: Callback) => Promise<void>;
|
||||
removeItem: (callback?: Callback) => Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||
* storage system that is global to the app. It should be used instead of
|
||||
* LocalStorage.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||
*/
|
||||
export type AsyncStorageStatic = {
|
||||
/**
|
||||
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||
*/
|
||||
getItem: (
|
||||
key: string,
|
||||
callback?: CallbackWithResult<string>
|
||||
) => Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Sets the value for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||
*/
|
||||
setItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes an item for a `key` and invokes a callback upon completion.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||
*/
|
||||
removeItem: (key: string, callback?: Callback) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Merges an existing `key` value with an input value, assuming both values
|
||||
* are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||
*/
|
||||
mergeItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||
* your app's keys.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||
*/
|
||||
clear: (callback?: Callback) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||
*/
|
||||
getAllKeys: (
|
||||
callback?: CallbackWithResult<readonly string[]>
|
||||
) => Promise<readonly string[]>;
|
||||
|
||||
/**
|
||||
* The following batched functions are useful for executing a lot of
|
||||
* operations at once, allowing for native optimizations and provide the
|
||||
* convenience of a single callback after all operations are complete.
|
||||
*
|
||||
* These functions return arrays of errors, potentially one for every key.
|
||||
* For key-specific errors, the Error object will have a key property to
|
||||
* indicate which key caused the error.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Flushes any pending requests using a single batch call to get the data.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||
* */
|
||||
flushGetRequests: () => void;
|
||||
|
||||
/**
|
||||
* This allows you to batch the fetching of items given an array of `key`
|
||||
* inputs. Your callback will be invoked with an array of corresponding
|
||||
* key-value pairs found.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||
*/
|
||||
multiGet: (
|
||||
keys: readonly string[],
|
||||
callback?: MultiGetCallback
|
||||
) => Promise<readonly KeyValuePair[]>;
|
||||
|
||||
/**
|
||||
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||
* the operation completes you'll get a single callback with any errors.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||
*/
|
||||
multiSet: (
|
||||
keyValuePairs: [string, string][],
|
||||
callback?: MultiCallback
|
||||
) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Call this to batch the deletion of all keys in the `keys` array.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||
*/
|
||||
multiRemove: (
|
||||
keys: readonly string[],
|
||||
callback?: MultiCallback
|
||||
) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Batch operation to merge in existing and new values for a given set of
|
||||
* keys. This assumes that the values are stringified JSON.
|
||||
*
|
||||
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||
*/
|
||||
multiMerge: (
|
||||
keyValuePairs: [string, string][],
|
||||
callback?: MultiCallback
|
||||
) => Promise<void>;
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<!--
|
||||
To customize common C++/WinRT project properties:
|
||||
* right-click the project node
|
||||
* expand the Common Properties item
|
||||
* select the C++/WinRT property page
|
||||
|
||||
For more advanced scenarios, and complete documentation, please see:
|
||||
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||
-->
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup />
|
||||
</Project>
|
@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<ProjectGuid>{4855D892-E16C-404D-8286-0089E0F7F9C4}</ProjectGuid>
|
||||
<ProjectName>ReactNativeAsyncStorage</ProjectName>
|
||||
<RootNamespace>ReactNativeAsyncStorage</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>16.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.16299.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="ReactNativeWindowsProps">
|
||||
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="PropertySheet.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="ReactNativeWindowsPropertySheets">
|
||||
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||
<!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
|
||||
<AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
|
||||
<ModuleDefinitionFile>..\code\ReactNativeAsyncStorage.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winsqlite3.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\code\pch.h" />
|
||||
<ClInclude Include="..\code\ReactPackageProvider.h">
|
||||
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\code\DBStorage.h" />
|
||||
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\code\pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="..\code\ReactPackageProvider.cpp">
|
||||
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(ReactNativeWindowsDir)\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj">
|
||||
<Project>{f7d32bd0-2749-483e-9a0d-1635ef7e3136}</Project>
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ReactNativeWindowsTargets">
|
||||
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureReactNativeWindowsTargets" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references targets in your node_modules\react-native-windows folder. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props'))" />
|
||||
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets'))" />
|
||||
</Target>
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="Deploy" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Generated Files">
|
||||
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\code\pch.h" />
|
||||
<ClInclude Include="..\code\ReactPackageProvider.h" />
|
||||
<ClInclude Include="..\code\DBStorage.h" />
|
||||
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\code\pch.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="..\code\ReactPackageProvider.cpp" />
|
||||
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
@ -0,0 +1,195 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29215.179
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Folly", "..\node_modules\react-native-windows\Folly\Folly.vcxproj", "{A990658C-CE31-4BCC-976F-0FC6B1AF693D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modules\react-native-windows\ReactCommon\ReactCommon.vcxproj", "{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactWindowsCore", "..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxproj", "{11C084A3-A57C-4296-A679-CAC17B603144}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Shared", "..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems", "{0CC28589-39E4-4288-B162-97B959F8B843}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Universal", "..\node_modules\react-native-windows\JSI\Universal\JSI.Universal.vcxproj", "{A62D504A-16B8-41D2-9F19-E2E86019E5E4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.ReactNative.SharedManaged", "..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.shproj", "{67A1076F-7790-4203-86EA-4402CCB5E782}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeAsyncStorage61", "ReactNativeAsyncStorage61\ReactNativeAsyncStorage61.vcxproj", "{4855D892-E16C-404D-8286-0089E0F7F9C4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{4855d892-e16c-404d-8286-0089e0f7f9c4}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13
|
||||
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.Build.0 = Debug|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.Build.0 = Release|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.ActiveCfg = Release|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.Build.0 = Release|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.Build.0 = Release|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{0CC28589-39E4-4288-B162-97B959F8B843} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{67A1076F-7790-4203-86EA-4402CCB5E782} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1F02BFA9-97C8-4ACF-A348-B3166C3BC7EA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<!--
|
||||
To customize common C++/WinRT project properties:
|
||||
* right-click the project node
|
||||
* expand the Common Properties item
|
||||
* select the C++/WinRT property page
|
||||
|
||||
For more advanced scenarios, and complete documentation, please see:
|
||||
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||
-->
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup />
|
||||
</Project>
|
@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<ProjectGuid>{4855D892-E16C-404D-8286-0089E0F7F9C4}</ProjectGuid>
|
||||
<ProjectName>ReactNativeAsyncStorage61</ProjectName>
|
||||
<RootNamespace>ReactNativeAsyncStorage</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.15063.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="ReactNativeWindowsProps">
|
||||
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
<Import Project="$(ReactNativeWindowsDir)\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems" Label="Shared" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="PropertySheet.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||
<!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
|
||||
<AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;RNW_61;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
|
||||
<ModuleDefinitionFile>..\code\ReactNativeAsyncStorage.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winsqlite3.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\code\pch.h" />
|
||||
<ClInclude Include="..\code\ReactPackageProvider.h">
|
||||
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\code\DBStorage.h" />
|
||||
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\code\pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="..\code\ReactPackageProvider.cpp">
|
||||
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(ReactNativeWindowsDir)\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj">
|
||||
<Project>{f7d32bd0-2749-483e-9a0d-1635ef7e3136}</Project>
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="Deploy"/>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Generated Files">
|
||||
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\code\pch.h" />
|
||||
<ClInclude Include="..\code\ReactPackageProvider.h" />
|
||||
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||
<ClInclude Include="..\code\DBStorage.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\code\pch.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="..\code\ReactPackageProvider.cpp" />
|
||||
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
</packages>
|
@ -0,0 +1,192 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeAsyncStorage", "ReactNativeAsyncStorage\ReactNativeAsyncStorage.vcxproj", "{4855D892-E16C-404D-8286-0089E0F7F9C4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{4F6E56C3-12C5-4457-9239-0ACF0B7150A8}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Folly", "..\node_modules\react-native-windows\Folly\Folly.vcxproj", "{A990658C-CE31-4BCC-976F-0FC6B1AF693D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Universal", "..\node_modules\react-native-windows\JSI\Universal\JSI.Universal.vcxproj", "{A62D504A-16B8-41D2-9F19-E2E86019E5E4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Shared", "..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems", "{0CC28589-39E4-4288-B162-97B959F8B843}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modules\react-native-windows\ReactCommon\ReactCommon.vcxproj", "{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactWindowsCore", "..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxproj", "{11C084A3-A57C-4296-A679-CAC17B603144}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\node_modules\react-native-windows\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxitems*{11c084a3-a57c-4296-a679-cac17b603144}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9
|
||||
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.Build.0 = Debug|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.Build.0 = Release|ARM
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.ActiveCfg = Release|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.Build.0 = Release|x64
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.Build.0 = Release|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{A62D504A-16B8-41D2-9F19-E2E86019E5E4} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{0CC28589-39E4-4288-B162-97B959F8B843} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{11C084A3-A57C-4296-A679-CAC17B603144} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
{EF074BA1-2D54-4D49-A28E-5E040B47CD2E} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1F02BFA9-97C8-4ACF-A348-B3166C3BC7EA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -0,0 +1,599 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
#include "pch.h"
|
||||
|
||||
#include "DBStorage.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
using namespace Microsoft::ReactNative;
|
||||
using namespace Windows::ApplicationModel::Core;
|
||||
using namespace Windows::Data::Json;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage;
|
||||
} // namespace winrt
|
||||
|
||||
// All functions below return std::nullopt on error.
|
||||
#define CHECK(expr) \
|
||||
if (!(expr)) { \
|
||||
return std::nullopt; \
|
||||
}
|
||||
|
||||
// Convenience macro to call CheckSQLiteResult.
|
||||
#define CHECK_SQL_OK(expr) CHECK(CheckSQLiteResult(db, m_errorManager, (expr)))
|
||||
|
||||
namespace
|
||||
{
|
||||
// To implement safe operator& for unique_ptr.
|
||||
template <typename T, typename TDeleter>
|
||||
struct UniquePtrSetter {
|
||||
UniquePtrSetter(std::unique_ptr<T, TDeleter> &ptr) noexcept : m_ptr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
~UniquePtrSetter()
|
||||
{
|
||||
m_ptr = {m_rawPtr, m_ptr.get_deleter()};
|
||||
}
|
||||
|
||||
operator T **() noexcept
|
||||
{
|
||||
return &m_rawPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_rawPtr{};
|
||||
std::unique_ptr<T, TDeleter> &m_ptr;
|
||||
};
|
||||
|
||||
template <typename T, typename TDeleter>
|
||||
UniquePtrSetter<T, TDeleter> operator&(std::unique_ptr<T, TDeleter> &ptr) noexcept
|
||||
{
|
||||
return UniquePtrSetter<T, TDeleter>(ptr);
|
||||
}
|
||||
|
||||
using ExecCallback = int(SQLITE_CALLBACK *)(void *callbackData,
|
||||
int columnCount,
|
||||
char **columnTexts,
|
||||
char **columnNames);
|
||||
|
||||
// Execute the provided SQLite statement (and optional execCallback & user data
|
||||
// in callbackData). On error, report it to the errorManager and return std::nullopt.
|
||||
std::optional<bool> Exec(sqlite3 *db,
|
||||
DBStorage::ErrorManager &errorManager,
|
||||
const char *statement,
|
||||
ExecCallback execCallback = nullptr,
|
||||
void *callbackData = nullptr) noexcept
|
||||
{
|
||||
auto errMsg = std::unique_ptr<char, decltype(&sqlite3_free)>{nullptr, &sqlite3_free};
|
||||
int rc = sqlite3_exec(db, statement, execCallback, callbackData, &errMsg);
|
||||
if (errMsg) {
|
||||
return errorManager.AddError(errMsg.get());
|
||||
}
|
||||
if (rc != SQLITE_OK) {
|
||||
return errorManager.AddError(sqlite3_errmsg(db));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convenience wrapper for using Exec with lambda expressions.
|
||||
template <typename Fn>
|
||||
std::optional<bool>
|
||||
Exec(sqlite3 *db, DBStorage::ErrorManager &errorManager, const char *statement, Fn &fn) noexcept
|
||||
{
|
||||
return Exec(
|
||||
db,
|
||||
errorManager,
|
||||
statement,
|
||||
[](void *callbackData, int columnCount, char **columnTexts, char **columnNames) {
|
||||
return (*static_cast<Fn *>(callbackData))(columnCount, columnTexts, columnNames);
|
||||
},
|
||||
&fn);
|
||||
}
|
||||
|
||||
// Check that the args collection size is less than SQLITE_LIMIT_VARIABLE_NUMBER, and that
|
||||
// every member of args is not an empty string.
|
||||
// On error, report it to the errorManager and return std::nullopt.
|
||||
std::optional<bool> CheckArgs(sqlite3 *db,
|
||||
DBStorage::ErrorManager &errorManager,
|
||||
const std::vector<std::string> &args) noexcept
|
||||
{
|
||||
int varLimit = sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
|
||||
auto argCount = args.size();
|
||||
if (argCount > static_cast<size_t>(std::numeric_limits<int>::max()) ||
|
||||
static_cast<int>(argCount) > varLimit) {
|
||||
char errorMsg[60];
|
||||
sprintf_s(errorMsg, "Too many keys. Maximum supported keys :%d.", varLimit);
|
||||
return errorManager.AddError(errorMsg);
|
||||
}
|
||||
for (int i = 0; i < static_cast<int>(argCount); i++) {
|
||||
if (args[i].empty()) {
|
||||
return errorManager.AddError("The key must be a non-empty string.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// RAII object to manage SQLite transaction. On destruction, if
|
||||
// Commit() has not been called, rolls back the transactions.
|
||||
// The provided SQLite connection handle & errorManager must outlive
|
||||
// the Sqlite3Transaction object.
|
||||
struct Sqlite3Transaction {
|
||||
Sqlite3Transaction(sqlite3 *db, DBStorage::ErrorManager &errorManager) noexcept
|
||||
: m_db(db), m_errorManager(errorManager)
|
||||
{
|
||||
if (!Exec(m_db, m_errorManager, "BEGIN TRANSACTION")) {
|
||||
m_db = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Sqlite3Transaction(const Sqlite3Transaction &) = delete;
|
||||
Sqlite3Transaction &operator=(const Sqlite3Transaction &) = delete;
|
||||
|
||||
~Sqlite3Transaction()
|
||||
{
|
||||
Rollback();
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return m_db != nullptr;
|
||||
}
|
||||
|
||||
std::optional<bool> Commit() noexcept
|
||||
{
|
||||
if (m_db) {
|
||||
return Exec(std::exchange(m_db, nullptr), m_errorManager, "COMMIT");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<bool> Rollback() noexcept
|
||||
{
|
||||
if (m_db) {
|
||||
return Exec(std::exchange(m_db, nullptr), m_errorManager, "ROLLBACK");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
sqlite3 *m_db{};
|
||||
DBStorage::ErrorManager &m_errorManager;
|
||||
};
|
||||
|
||||
// Append argCount variables to prefix in a comma-separated list.
|
||||
std::string MakeSQLiteParameterizedStatement(const char *prefix, int argCount) noexcept
|
||||
{
|
||||
assert(argCount != 0);
|
||||
std::string result(prefix);
|
||||
result.reserve(result.size() + (argCount * 2) + 1);
|
||||
result += '(';
|
||||
for (int x = 0; x < argCount - 1; x++) {
|
||||
result += "?,";
|
||||
}
|
||||
result += "?)";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if sqliteResult is SQLITE_OK.
|
||||
// If not, report the error to the errorManager and return std::nullopt.
|
||||
std::optional<bool> CheckSQLiteResult(sqlite3 *db,
|
||||
DBStorage::ErrorManager &errorManager,
|
||||
int sqliteResult) noexcept
|
||||
{
|
||||
if (sqliteResult == SQLITE_OK) {
|
||||
return true;
|
||||
} else {
|
||||
return errorManager.AddError(sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
|
||||
using StatementPtr = std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)>;
|
||||
|
||||
// A convenience wrapper for sqlite3_prepare_v2 function.
|
||||
int PrepareStatement(sqlite3 *db,
|
||||
const std::string &statementText,
|
||||
sqlite3_stmt **statement) noexcept
|
||||
{
|
||||
return sqlite3_prepare_v2(db, statementText.c_str(), -1, statement, nullptr);
|
||||
}
|
||||
|
||||
// A convenience wrapper for sqlite3_bind_text function.
|
||||
int BindString(StatementPtr &statement, int index, const std::string &str) noexcept
|
||||
{
|
||||
return sqlite3_bind_text(statement.get(), index, str.c_str(), -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
// Merge source into destination.
|
||||
// It only merges objects - all other types are just clobbered (including arrays).
|
||||
void MergeJsonObjects(winrt::JsonObject const &destination,
|
||||
winrt::JsonObject const &source) noexcept
|
||||
{
|
||||
for (auto keyValue : source) {
|
||||
auto key = keyValue.Key();
|
||||
auto sourceValue = keyValue.Value();
|
||||
if (destination.HasKey(key)) {
|
||||
auto destinationValue = destination.GetNamedValue(key);
|
||||
if (destinationValue.ValueType() == winrt::JsonValueType::Object &&
|
||||
sourceValue.ValueType() == winrt::JsonValueType::Object) {
|
||||
MergeJsonObjects(destinationValue.GetObject(), sourceValue.GetObject());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
destination.SetNamedValue(key, sourceValue);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Initialize storage. On error, report it to the errorManager and return std::nullopt.
|
||||
std::optional<sqlite3 *>
|
||||
DBStorage::InitializeStorage(DBStorage::ErrorManager &errorManager) noexcept
|
||||
{
|
||||
winrt::slim_lock_guard guard{m_lock};
|
||||
if (m_db) {
|
||||
return m_db.get();
|
||||
}
|
||||
|
||||
std::string path;
|
||||
try {
|
||||
if (auto pathInspectable =
|
||||
winrt::CoreApplication::Properties().TryLookup(s_dbPathProperty)) {
|
||||
path = winrt::to_string(winrt::unbox_value<winrt::hstring>(pathInspectable));
|
||||
} else {
|
||||
auto const localAppDataPath = winrt::ApplicationData::Current().LocalFolder().Path();
|
||||
path = winrt::to_string(localAppDataPath) + "\\AsyncStorage.db";
|
||||
}
|
||||
} catch (const winrt::hresult_error &error) {
|
||||
errorManager.AddError(winrt::to_string(error.message()));
|
||||
return errorManager.AddError(
|
||||
"Please specify 'React-Native-Community-Async-Storage-Database-Path' in "
|
||||
"CoreApplication::Properties");
|
||||
}
|
||||
|
||||
auto db = DatabasePtr{nullptr, &sqlite3_close};
|
||||
if (sqlite3_open_v2(path.c_str(),
|
||||
&db,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
|
||||
nullptr) != SQLITE_OK) {
|
||||
if (db) {
|
||||
return errorManager.AddError(sqlite3_errmsg(db.get()));
|
||||
} else {
|
||||
return errorManager.AddError("Storage database cannot be opened.");
|
||||
}
|
||||
}
|
||||
|
||||
int userVersion = 0;
|
||||
auto getUserVersionCallback =
|
||||
[](void *callbackData, int colomnCount, char **columnTexts, char ** /*columnNames*/) {
|
||||
if (colomnCount < 1) {
|
||||
return 1;
|
||||
}
|
||||
*static_cast<int *>(callbackData) = atoi(columnTexts[0]);
|
||||
return SQLITE_OK;
|
||||
};
|
||||
|
||||
CHECK(
|
||||
Exec(db.get(), errorManager, "PRAGMA user_version", getUserVersionCallback, &userVersion));
|
||||
|
||||
if (userVersion == 0) {
|
||||
CHECK(Exec(db.get(),
|
||||
errorManager,
|
||||
"CREATE TABLE IF NOT EXISTS AsyncLocalStorage(key TEXT PRIMARY KEY, value TEXT "
|
||||
"NOT NULL); PRAGMA user_version=1"));
|
||||
}
|
||||
|
||||
m_db = std::move(db);
|
||||
return m_db.get();
|
||||
}
|
||||
|
||||
DBStorage::~DBStorage()
|
||||
{
|
||||
decltype(m_tasks) tasks;
|
||||
{
|
||||
// If there is an in-progress async task, cancel it and wait on the condition_variable for
|
||||
// the async task to acknowledge cancellation by nulling out m_action. Once m_action is
|
||||
// null, it is safe to proceed with closing the DB connection. The DB connection is closed
|
||||
// by the m_db destructor.
|
||||
winrt::slim_lock_guard guard{m_lock};
|
||||
swap(tasks, m_tasks);
|
||||
if (m_action) {
|
||||
m_action.Cancel();
|
||||
m_cv.wait(m_lock, [this]() { return m_action == nullptr; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Under the lock, add a task to m_tasks and, if no async task is in progress schedule it.
|
||||
void DBStorage::AddTask(ErrorManager &errorManager,
|
||||
std::function<void(DBStorage::DBTask &task, sqlite3 *db)> &&onRun) noexcept
|
||||
{
|
||||
winrt::slim_lock_guard guard(m_lock);
|
||||
m_tasks.push_back(std::make_unique<DBTask>(errorManager, std::move(onRun)));
|
||||
if (!m_action) {
|
||||
m_action = RunTasks();
|
||||
}
|
||||
}
|
||||
|
||||
// On a background thread, while the async task has not been canceled and
|
||||
// there are more tasks to do, run the tasks. When there are either no more
|
||||
// tasks or cancellation has been requested, set m_action to null to report
|
||||
// that and complete the coroutine. N.B., it is important that detecting that
|
||||
// m_tasks is empty and acknowledging completion is done atomically; otherwise
|
||||
// there would be a race between the background task detecting m_tasks.empty()
|
||||
// and AddTask checking the coroutine is running.
|
||||
winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() noexcept
|
||||
{
|
||||
auto cancellationToken = co_await winrt::get_cancellation_token();
|
||||
co_await winrt::resume_background();
|
||||
for (;;) {
|
||||
decltype(m_tasks) tasks;
|
||||
sqlite3 *db{nullptr};
|
||||
{
|
||||
winrt::slim_lock_guard guard(m_lock);
|
||||
if (m_tasks.empty()) {
|
||||
m_action = nullptr;
|
||||
m_cv.notify_all();
|
||||
co_return;
|
||||
}
|
||||
std::swap(tasks, m_tasks);
|
||||
db = m_db.get();
|
||||
}
|
||||
|
||||
for (auto &task : tasks) {
|
||||
if (!cancellationToken()) {
|
||||
task->Run(*this, db);
|
||||
} else {
|
||||
task->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new Error to the error list.
|
||||
// Return std::nullopt for convenience to other methods that use std::nullopt to indicate error
|
||||
// result.
|
||||
std::nullopt_t DBStorage::ErrorManager::AddError(std::string &&message) noexcept
|
||||
{
|
||||
m_errors.push_back(Error{std::move(message)});
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool DBStorage::ErrorManager::HasErrors() const noexcept
|
||||
{
|
||||
return !m_errors.empty();
|
||||
}
|
||||
|
||||
const std::vector<DBStorage::Error> &DBStorage::ErrorManager::GetErrorList() const noexcept
|
||||
{
|
||||
if (HasErrors()) {
|
||||
return m_errors;
|
||||
}
|
||||
static std::vector<DBStorage::Error> s_unknownError{Error{"Unknown error."}};
|
||||
return s_unknownError;
|
||||
}
|
||||
|
||||
DBStorage::Error DBStorage::ErrorManager::GetCombinedError() const noexcept
|
||||
{
|
||||
auto &errors = GetErrorList();
|
||||
if (errors.size() == 1) {
|
||||
return errors[0];
|
||||
}
|
||||
|
||||
std::string combinedMessage;
|
||||
for (const auto &error : errors) {
|
||||
combinedMessage += error.Message + '\n';
|
||||
}
|
||||
return Error{std::move(combinedMessage)};
|
||||
}
|
||||
|
||||
DBStorage::DBTask::DBTask(DBStorage::ErrorManager &errorManager,
|
||||
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept
|
||||
: m_errorManager(errorManager), m_onRun(std::move(onRun))
|
||||
{
|
||||
}
|
||||
|
||||
void DBStorage::DBTask::Run(DBStorage &storage, sqlite3 *db) noexcept
|
||||
{
|
||||
if (!db) {
|
||||
// We initialize DB handler on demand to report errors in the task context.
|
||||
if (auto res = storage.InitializeStorage(m_errorManager)) {
|
||||
db = *res;
|
||||
}
|
||||
}
|
||||
if (db) {
|
||||
m_onRun(*this, db);
|
||||
}
|
||||
}
|
||||
|
||||
void DBStorage::DBTask::Cancel() noexcept
|
||||
{
|
||||
m_errorManager.AddError("Task is canceled.");
|
||||
}
|
||||
|
||||
std::optional<std::vector<DBStorage::KeyValue>>
|
||||
DBStorage::DBTask::MultiGet(sqlite3 *db, const std::vector<std::string> &keys) noexcept
|
||||
{
|
||||
CHECK(!m_errorManager.HasErrors());
|
||||
CHECK(CheckArgs(db, m_errorManager, keys));
|
||||
|
||||
auto argCount = static_cast<int>(keys.size());
|
||||
auto sql = MakeSQLiteParameterizedStatement(
|
||||
"SELECT key, value FROM AsyncLocalStorage WHERE key IN ", argCount);
|
||||
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
||||
CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
|
||||
}
|
||||
|
||||
std::vector<DBStorage::KeyValue> result;
|
||||
for (;;) {
|
||||
auto stepResult = sqlite3_step(statement.get());
|
||||
if (stepResult == SQLITE_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult != SQLITE_ROW) {
|
||||
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
auto key = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 0));
|
||||
if (!key) {
|
||||
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||
}
|
||||
auto value = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 1));
|
||||
if (!value) {
|
||||
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||
}
|
||||
result.push_back(KeyValue{key, value});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<bool> DBStorage::DBTask::MultiSet(sqlite3 *db,
|
||||
const std::vector<KeyValue> &keyValues) noexcept
|
||||
{
|
||||
CHECK(!m_errorManager.HasErrors());
|
||||
if (keyValues.empty()) {
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
Sqlite3Transaction transaction(db, m_errorManager);
|
||||
CHECK(transaction);
|
||||
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
||||
CHECK_SQL_OK(
|
||||
PrepareStatement(db, "INSERT OR REPLACE INTO AsyncLocalStorage VALUES(?, ?)", &statement));
|
||||
for (const auto &keyValue : keyValues) {
|
||||
CHECK_SQL_OK(BindString(statement, 1, keyValue.Key));
|
||||
CHECK_SQL_OK(BindString(statement, 2, keyValue.Value));
|
||||
auto rc = sqlite3_step(statement.get());
|
||||
CHECK(rc == SQLITE_DONE || CheckSQLiteResult(db, m_errorManager, rc));
|
||||
CHECK_SQL_OK(sqlite3_reset(statement.get()));
|
||||
}
|
||||
CHECK(transaction.Commit());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<bool> DBStorage::DBTask::MultiMerge(sqlite3 *db,
|
||||
const std::vector<KeyValue> &keyValues) noexcept
|
||||
{
|
||||
CHECK(!m_errorManager.HasErrors());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::unordered_map<std::string, std::string> newValues;
|
||||
keys.reserve(keyValues.size());
|
||||
for (const auto &keyValue : keyValues) {
|
||||
keys.push_back(keyValue.Key);
|
||||
newValues.try_emplace(keyValue.Key, keyValue.Value);
|
||||
}
|
||||
|
||||
auto oldValues = MultiGet(db, keys);
|
||||
CHECK(oldValues);
|
||||
|
||||
std::vector<KeyValue> mergedResults;
|
||||
for (size_t i = 0; i < oldValues->size(); i++) {
|
||||
auto &key = oldValues->at(i).Key;
|
||||
auto &oldValue = oldValues->at(i).Value;
|
||||
auto &newValue = newValues[key];
|
||||
|
||||
winrt::JsonObject oldJson;
|
||||
winrt::JsonObject newJson;
|
||||
if (winrt::JsonObject::TryParse(winrt::to_hstring(oldValue), oldJson) &&
|
||||
winrt::JsonObject::TryParse(winrt::to_hstring(newValue), newJson)) {
|
||||
MergeJsonObjects(oldJson, newJson);
|
||||
mergedResults.push_back(KeyValue{key, winrt::to_string(oldJson.ToString())});
|
||||
} else {
|
||||
return m_errorManager.AddError("Values must be valid JSON object strings");
|
||||
}
|
||||
}
|
||||
|
||||
return MultiSet(db, mergedResults);
|
||||
}
|
||||
|
||||
std::optional<bool> DBStorage::DBTask::MultiRemove(sqlite3 *db,
|
||||
const std::vector<std::string> &keys) noexcept
|
||||
{
|
||||
CHECK(!m_errorManager.HasErrors());
|
||||
CHECK(CheckArgs(db, m_errorManager, keys));
|
||||
auto argCount = static_cast<int>(keys.size());
|
||||
auto sql =
|
||||
MakeSQLiteParameterizedStatement("DELETE FROM AsyncLocalStorage WHERE key IN ", argCount);
|
||||
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
||||
CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
|
||||
}
|
||||
for (;;) {
|
||||
auto stepResult = sqlite3_step(statement.get());
|
||||
if (stepResult == SQLITE_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult != SQLITE_ROW) {
|
||||
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::string>> DBStorage::DBTask::GetAllKeys(sqlite3 *db) noexcept
|
||||
{
|
||||
CHECK(!m_errorManager.HasErrors());
|
||||
std::vector<std::string> result;
|
||||
auto getAllKeysCallback = [&](int columnCount, char **columnTexts, char **) {
|
||||
if (columnCount > 0) {
|
||||
result.emplace_back(columnTexts[0]);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
};
|
||||
|
||||
CHECK(Exec(db, m_errorManager, "SELECT key FROM AsyncLocalStorage", getAllKeysCallback));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<bool> DBStorage::DBTask::RemoveAll(sqlite3 *db) noexcept
|
||||
{
|
||||
CHECK(!m_errorManager.HasErrors());
|
||||
CHECK(Exec(db, m_errorManager, "DELETE FROM AsyncLocalStorage"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read KeyValue from a JSON array.
|
||||
void ReadValue(const winrt::IJSValueReader &reader,
|
||||
/*out*/ DBStorage::KeyValue &value) noexcept
|
||||
{
|
||||
if (reader.ValueType() == winrt::JSValueType::Array) {
|
||||
int index = 0;
|
||||
while (reader.GetNextArrayItem()) {
|
||||
if (index == 0) {
|
||||
ReadValue(reader, value.Key);
|
||||
} else if (index == 1) {
|
||||
ReadValue(reader, value.Value);
|
||||
} else {
|
||||
// Read the array till the end to keep reader in a good state.
|
||||
winrt::SkipValue<winrt::JSValue>(reader);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
} else {
|
||||
// To keep reader in a good state.
|
||||
winrt::SkipValue<winrt::JSValue>(reader);
|
||||
}
|
||||
}
|
||||
|
||||
// Write KeyValue to a JSON array.
|
||||
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
||||
const DBStorage::KeyValue &value) noexcept
|
||||
{
|
||||
writer.WriteArrayBegin();
|
||||
WriteValue(writer, value.Key);
|
||||
WriteValue(writer, value.Value);
|
||||
writer.WriteArrayEnd();
|
||||
}
|
||||
|
||||
// Write Error object to JSON.
|
||||
void WriteValue(const winrt::IJSValueWriter &writer, const DBStorage::Error &value) noexcept
|
||||
{
|
||||
writer.WriteObjectBegin();
|
||||
winrt::WriteProperty(writer, L"message", value.Message);
|
||||
writer.WriteObjectEnd();
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <winsqlite/winsqlite3.h>
|
||||
|
||||
#include "NativeModules.h"
|
||||
|
||||
struct DBStorage {
|
||||
// To pass KeyValue pairs in the native module API.
|
||||
// It has custom ReadValue and WriteValue to read/write to/from JSON.
|
||||
struct KeyValue {
|
||||
std::string Key;
|
||||
std::string Value;
|
||||
};
|
||||
|
||||
// An Error object for the native module API.
|
||||
// It has a custom WriteValue to write to JSON.
|
||||
struct Error {
|
||||
std::string Message;
|
||||
};
|
||||
|
||||
// An error list shared between Promise and DBTask.
|
||||
struct ErrorManager {
|
||||
std::nullopt_t AddError(std::string &&message) noexcept;
|
||||
bool HasErrors() const noexcept;
|
||||
const std::vector<Error> &GetErrorList() const noexcept;
|
||||
Error GetCombinedError() const noexcept;
|
||||
|
||||
private:
|
||||
std::vector<Error> m_errors;
|
||||
};
|
||||
|
||||
// Ensure that only one result onResolve or onReject callback is called once.
|
||||
template <typename TOnResolve, typename TOnReject>
|
||||
struct Promise {
|
||||
|
||||
Promise(TOnResolve &&onResolve, TOnReject &&onReject) noexcept
|
||||
: m_onResolve(std::move(onResolve)), m_onReject(std::move(onReject))
|
||||
{
|
||||
}
|
||||
|
||||
~Promise()
|
||||
{
|
||||
Reject();
|
||||
}
|
||||
|
||||
Promise(const Promise &other) = delete;
|
||||
Promise &operator=(const Promise &other) = delete;
|
||||
|
||||
ErrorManager &GetErrorManager() noexcept
|
||||
{
|
||||
return m_errorManager;
|
||||
}
|
||||
|
||||
template <typename TValue>
|
||||
void Resolve(const TValue &value) noexcept
|
||||
{
|
||||
Complete([&] { m_onResolve(value); });
|
||||
}
|
||||
|
||||
void Reject() noexcept
|
||||
{
|
||||
Complete([&] {
|
||||
// Ensure that we have at least one error on rejection.
|
||||
if (!m_errorManager.HasErrors()) {
|
||||
m_errorManager.AddError("Promise is rejected.");
|
||||
}
|
||||
m_onReject(m_errorManager);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename TValue>
|
||||
void ResolveOrReject(const std::optional<TValue> &value) noexcept
|
||||
{
|
||||
if (value) {
|
||||
Resolve(*value);
|
||||
} else {
|
||||
Reject();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Fn>
|
||||
void Complete(Fn &&fn)
|
||||
{
|
||||
if (m_isCompleted.test_and_set() == false) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ErrorManager m_errorManager;
|
||||
std::atomic_flag m_isCompleted = ATOMIC_FLAG_INIT;
|
||||
TOnResolve m_onResolve;
|
||||
TOnReject m_onReject;
|
||||
};
|
||||
|
||||
// An asynchronous task that run in a background thread.
|
||||
struct DBTask {
|
||||
DBTask(ErrorManager &errorManager,
|
||||
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept;
|
||||
|
||||
DBTask() = default;
|
||||
DBTask(const DBTask &) = delete;
|
||||
DBTask &operator=(const DBTask &) = delete;
|
||||
|
||||
void Run(DBStorage &storage, sqlite3 *db) noexcept;
|
||||
void Cancel() noexcept;
|
||||
|
||||
std::optional<std::vector<KeyValue>>
|
||||
MultiGet(sqlite3 *db, const std::vector<std::string> &keys) noexcept;
|
||||
std::optional<bool> MultiSet(sqlite3 *db, const std::vector<KeyValue> &keyValues) noexcept;
|
||||
std::optional<bool> MultiMerge(sqlite3 *db,
|
||||
const std::vector<KeyValue> &keyValues) noexcept;
|
||||
std::optional<bool> MultiRemove(sqlite3 *db, const std::vector<std::string> &keys) noexcept;
|
||||
std::optional<std::vector<std::string>> GetAllKeys(sqlite3 *db) noexcept;
|
||||
std::optional<bool> RemoveAll(sqlite3 *db) noexcept;
|
||||
|
||||
private:
|
||||
std::function<void(DBTask &task, sqlite3 *db)> m_onRun;
|
||||
ErrorManager &m_errorManager;
|
||||
};
|
||||
|
||||
using DatabasePtr = std::unique_ptr<sqlite3, decltype(&sqlite3_close)>;
|
||||
|
||||
std::optional<sqlite3 *> InitializeStorage(ErrorManager &errorManager) noexcept;
|
||||
~DBStorage();
|
||||
|
||||
template <typename TOnResolve, typename TOnReject>
|
||||
static auto CreatePromise(TOnResolve &&onResolve, TOnReject &&onReject) noexcept
|
||||
{
|
||||
using PromiseType = Promise<std::decay_t<TOnResolve>, std::decay_t<TOnReject>>;
|
||||
return std::make_shared<PromiseType>(std::forward<TOnResolve>(onResolve),
|
||||
std::forward<TOnReject>(onReject));
|
||||
}
|
||||
|
||||
void AddTask(ErrorManager &errorManager,
|
||||
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept;
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction RunTasks() noexcept;
|
||||
|
||||
private:
|
||||
static constexpr auto s_dbPathProperty = L"React-Native-Community-Async-Storage-Database-Path";
|
||||
|
||||
DatabasePtr m_db{nullptr, &sqlite3_close};
|
||||
winrt::slim_mutex m_lock;
|
||||
winrt::slim_condition_variable m_cv;
|
||||
winrt::Windows::Foundation::IAsyncAction m_action{nullptr};
|
||||
std::vector<std::unique_ptr<DBTask>> m_tasks;
|
||||
};
|
||||
|
||||
void ReadValue(const winrt::Microsoft::ReactNative::IJSValueReader &reader,
|
||||
/*out*/ DBStorage::KeyValue &value) noexcept;
|
||||
|
||||
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
||||
const DBStorage::KeyValue &value) noexcept;
|
||||
|
||||
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
||||
const DBStorage::Error &value) noexcept;
|
@ -0,0 +1,118 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
#pragma once
|
||||
|
||||
#include "DBStorage.h"
|
||||
#include "NativeModules.h"
|
||||
|
||||
namespace winrt::ReactNativeAsyncStorage::implementation
|
||||
{
|
||||
REACT_MODULE(RNCAsyncStorage)
|
||||
struct RNCAsyncStorage {
|
||||
|
||||
REACT_METHOD(multiGet)
|
||||
void multiGet(
|
||||
std::vector<std::string> &&keys,
|
||||
std::function<void(const std::vector<DBStorage::Error> &errors,
|
||||
const std::vector<DBStorage::KeyValue> &result)> &&callback) noexcept
|
||||
{
|
||||
auto promise = DBStorage::CreatePromise(
|
||||
[callback](const std::vector<DBStorage::KeyValue> &result) {
|
||||
callback({}, result);
|
||||
},
|
||||
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||
callback(errorManager.GetErrorList(), {});
|
||||
});
|
||||
m_dbStorage.AddTask(
|
||||
promise->GetErrorManager(),
|
||||
[promise, keys = std::move(keys)](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||
promise->ResolveOrReject(task.MultiGet(db, keys));
|
||||
});
|
||||
}
|
||||
|
||||
REACT_METHOD(multiSet)
|
||||
void multiSet(
|
||||
std::vector<DBStorage::KeyValue> &&keyValues,
|
||||
std::function<void(const std::vector<DBStorage::Error> &errors)> &&callback) noexcept
|
||||
{
|
||||
auto promise =
|
||||
DBStorage::CreatePromise([callback](bool /*value*/) { callback({}); },
|
||||
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||
callback(errorManager.GetErrorList());
|
||||
});
|
||||
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||
[promise, keyValues = std::move(keyValues)](DBStorage::DBTask &task,
|
||||
sqlite3 *db) noexcept {
|
||||
promise->ResolveOrReject(task.MultiSet(db, keyValues));
|
||||
});
|
||||
}
|
||||
|
||||
REACT_METHOD(multiMerge)
|
||||
void multiMerge(
|
||||
std::vector<DBStorage::KeyValue> &&keyValues,
|
||||
std::function<void(const std::vector<DBStorage::Error> &errors)> &&callback) noexcept
|
||||
{
|
||||
auto promise =
|
||||
DBStorage::CreatePromise([callback](bool /*value*/) { callback({}); },
|
||||
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||
callback(errorManager.GetErrorList());
|
||||
});
|
||||
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||
[promise, keyValues = std::move(keyValues)](DBStorage::DBTask &task,
|
||||
sqlite3 *db) noexcept {
|
||||
promise->ResolveOrReject(task.MultiMerge(db, keyValues));
|
||||
});
|
||||
}
|
||||
|
||||
REACT_METHOD(multiRemove)
|
||||
void multiRemove(
|
||||
std::vector<std::string> &&keys,
|
||||
std::function<void(const std::vector<DBStorage::Error> &errors)> &&callback) noexcept
|
||||
{
|
||||
auto promise =
|
||||
DBStorage::CreatePromise([callback](bool /*value*/) { callback({}); },
|
||||
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||
callback(errorManager.GetErrorList());
|
||||
});
|
||||
m_dbStorage.AddTask(
|
||||
promise->GetErrorManager(),
|
||||
[promise, keys = std::move(keys)](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||
promise->ResolveOrReject(task.MultiRemove(db, keys));
|
||||
});
|
||||
}
|
||||
|
||||
REACT_METHOD(getAllKeys)
|
||||
void
|
||||
getAllKeys(std::function<void(const std::optional<DBStorage::Error> &error,
|
||||
const std::vector<std::string> &keys)> &&callback) noexcept
|
||||
{
|
||||
auto promise = DBStorage::CreatePromise(
|
||||
[callback](const std::vector<std::string> &keys) { callback(std::nullopt, keys); },
|
||||
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||
callback(errorManager.GetCombinedError(), {});
|
||||
});
|
||||
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||
[promise](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||
promise->ResolveOrReject(task.GetAllKeys(db));
|
||||
});
|
||||
}
|
||||
|
||||
REACT_METHOD(clear)
|
||||
void
|
||||
clear(std::function<void(const std::optional<DBStorage::Error> &error)> &&callback) noexcept
|
||||
{
|
||||
auto promise =
|
||||
DBStorage::CreatePromise([callback](bool /*value*/) { callback(std::nullopt); },
|
||||
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||
callback(errorManager.GetCombinedError());
|
||||
});
|
||||
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||
[promise](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||
promise->ResolveOrReject(task.RemoveAll(db));
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
DBStorage m_dbStorage;
|
||||
};
|
||||
} // namespace winrt::ReactNativeAsyncStorage::implementation
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue