/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule AsyncStorage * @flow-weak */ 'use strict'; var NativeModules = require('NativeModules'); var RCTAsyncLocalStorage = NativeModules.AsyncLocalStorage; var RCTAsyncRocksDBStorage = NativeModules.AsyncRocksDBStorage; // We use RocksDB if available. var RCTAsyncStorage = RCTAsyncRocksDBStorage || RCTAsyncLocalStorage; /** * AsyncStorage is a simple, asynchronous, persistent, global, key-value storage * system. It should be used instead of LocalStorage. * * It is recommended that you use an abstraction on top of AsyncStorage instead * of AsyncStorage directly for anything more than light usage since it * operates globally. * * This JS code is a simple facad over the native iOS implementation to provide * a clear JS API, real Error objects, and simple non-multi functions. Each * method returns a `Promise` object. */ var AsyncStorage = { /** * Fetches `key` and passes the result to `callback`, along with an `Error` if * there is any. Returns a `Promise` object. */ getItem: function( key: string, callback?: ?(error: ?Error, result: ?string) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiGet([key], function(errors, result) { // Unpack result to get value from [[key,value]] var value = (result && result[0] && result[0][1]) ? result[0][1] : null; callback && callback((errors && convertError(errors[0])) || null, value); if (errors) { reject(convertError(errors[0])); } else { resolve(value); } }); }); }, /** * Sets `value` for `key` and calls `callback` on completion, along with an * `Error` if there is any. Returns a `Promise` object. */ setItem: function( key: string, value: string, callback?: ?(error: ?Error) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiSet([[key,value]], function(errors) { callback && callback((errors && convertError(errors[0])) || null); if (errors) { reject(convertError(errors[0])); } else { resolve(null); } }); }); }, /** * Returns a `Promise` object. */ removeItem: function( key: string, callback?: ?(error: ?Error) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiRemove([key], function(errors) { callback && callback((errors && convertError(errors[0])) || null); if (errors) { reject(convertError(errors[0])); } else { resolve(null); } }); }); }, /** * Merges existing value with input value, assuming they are stringified json. Returns a `Promise` object. * * Not supported by all native implementations. */ mergeItem: function( key: string, value: string, callback?: ?(error: ?Error) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiMerge([[key,value]], function(errors) { callback && callback((errors && convertError(errors[0])) || null); if (errors) { reject(convertError(errors[0])); } else { resolve(null); } }); }); }, /** * Erases *all* AsyncStorage for all clients, libraries, etc. You probably * don't want to call this - use removeItem or multiRemove to clear only your * own keys instead. Returns a `Promise` object. */ clear: function(callback?: ?(error: ?Error) => void): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.clear(function(error) { callback && callback(convertError(error)); if (error && convertError(error)){ reject(convertError(error)); } else { resolve(null); } }); }); }, /** * Gets *all* keys known to the system, for all callers, libraries, etc. Returns a `Promise` object. */ getAllKeys: function(callback?: ?(error: ?Error, keys: ?Array) => void): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.getAllKeys(function(error, keys) { callback && callback(convertError(error), keys); if (error) { reject(convertError(error)); } else { resolve(keys); } }); }); }, /** * 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. */ /** * multiGet invokes callback with an array of key-value pair arrays that * matches the input format of multiSet. Returns a `Promise` object. * * multiGet(['k1', 'k2'], cb) -> cb([['k1', 'val1'], ['k2', 'val2']]) */ multiGet: function( keys: Array, callback?: ?(errors: ?Array, result: ?Array>) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiGet(keys, function(errors, result) { var error = (errors && errors.map((error) => convertError(error))) || null; callback && callback(error, result); if (errors) { reject(error); } else { resolve(result); } }); }); }, /** * multiSet and multiMerge take arrays of key-value array pairs that match * the output of multiGet, e.g. Returns a `Promise` object. * * multiSet([['k1', 'val1'], ['k2', 'val2']], cb); */ multiSet: function( keyValuePairs: Array>, callback?: ?(errors: ?Array) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiSet(keyValuePairs, function(errors) { var error = (errors && errors.map((error) => convertError(error))) || null; callback && callback(error); if (errors) { reject(error); } else { resolve(null); } }); }); }, /** * Delete all the keys in the `keys` array. Returns a `Promise` object. */ multiRemove: function( keys: Array, callback?: ?(errors: ?Array) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiRemove(keys, function(errors) { var error = (errors && errors.map((error) => convertError(error))) || null; callback && callback(error); if (errors) { reject(error); } else { resolve(null); } }); }); }, /** * Merges existing values with input values, assuming they are stringified * json. Returns a `Promise` object. * * Not supported by all native implementations. */ multiMerge: function( keyValuePairs: Array>, callback?: ?(errors: ?Array) => void ): Promise { return new Promise((resolve, reject) => { RCTAsyncStorage.multiMerge(keyValuePairs, function(errors) { var error = (errors && errors.map((error) => convertError(error))) || null; callback && callback(error); if (errors) { reject(error); } else { resolve(null); } }); }); }, }; // Not all native implementations support merge. if (!RCTAsyncStorage.multiMerge) { delete AsyncStorage.mergeItem; delete AsyncStorage.multiMerge; } function convertError(error) { if (!error) { return null; } var out = new Error(error.message); out.key = error.key; // flow doesn't like this :( return out; } module.exports = AsyncStorage;