Storage implment list() & listAll() (#2424)

Storage implment list() & listAll()
This commit is contained in:
Elliot Hesp
2019-07-31 12:02:44 +01:00
committed by GitHub
12 changed files with 743 additions and 41 deletions

View File

@@ -27,9 +27,12 @@ import android.webkit.MimeTypeMap;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.storage.ListResult;
import com.google.firebase.storage.StorageException;
import com.google.firebase.storage.StorageMetadata;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.StorageTask;
import java.util.Map;
@@ -194,6 +197,27 @@ class ReactNativeFirebaseStorageCommon {
return metadata;
}
static WritableMap getListResultAsMap(ListResult listResult) {
WritableMap map = Arguments.createMap();
map.putString("nextPageToken", listResult.getPageToken());
WritableArray items = Arguments.createArray();
WritableArray prefixes = Arguments.createArray();
for (StorageReference reference : listResult.getItems()) {
items.pushString(reference.getPath());
}
for (StorageReference reference : listResult.getPrefixes()) {
prefixes.pushString(reference.getPath());
}
map.putArray("items", items);
map.putArray("prefixes", prefixes);
return map;
}
static String[] getExceptionCodeAndMessage(@Nullable Exception exception) {
String code = STATUS_UNKNOWN;
String message = "An unknown error has occurred.";

View File

@@ -22,13 +22,17 @@ import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.ListResult;
import com.google.firebase.storage.StorageMetadata;
import com.google.firebase.storage.StorageReference;
@@ -41,6 +45,7 @@ import java.util.Objects;
import io.invertase.firebase.common.ReactNativeFirebaseModule;
import static io.invertase.firebase.storage.ReactNativeFirebaseStorageCommon.buildMetadataFromMap;
import static io.invertase.firebase.storage.ReactNativeFirebaseStorageCommon.getListResultAsMap;
import static io.invertase.firebase.storage.ReactNativeFirebaseStorageCommon.getMetadataAsMap;
import static io.invertase.firebase.storage.ReactNativeFirebaseStorageCommon.isExternalStorageWritable;
import static io.invertase.firebase.storage.ReactNativeFirebaseStorageCommon.promiseRejectStorageException;
@@ -113,6 +118,41 @@ public class ReactNativeFirebaseStorageModule extends ReactNativeFirebaseModule
});
}
@ReactMethod
public void list(String appName, String url, ReadableMap listOptions, Promise promise) {
StorageReference reference = getReferenceFromUrl(url, appName);
Task<ListResult> list;
int maxResults = listOptions.getInt("maxResults");
if (listOptions.hasKey("pageToken")) {
String pageToken = listOptions.getString("pageToken");
list = reference.list(maxResults, Objects.requireNonNull(pageToken));
} else {
list = reference.list(maxResults);
}
list.addOnCompleteListener(getExecutor(), task -> {
if (task.isSuccessful()) {
promise.resolve(getListResultAsMap(task.getResult()));
} else {
promiseRejectStorageException(promise, task.getException());
}
});
}
@ReactMethod
public void listAll(String appName, String url, Promise promise) {
StorageReference reference = getReferenceFromUrl(url, appName);
reference.listAll().addOnCompleteListener(getExecutor(), task -> {
if (task.isSuccessful()) {
promise.resolve(getListResultAsMap(task.getResult()));
} else {
promiseRejectStorageException(promise, task.getException());
}
});
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
*/

View File

@@ -221,6 +221,133 @@ describe('storage() -> StorageReference', () => {
});
});
describe('list', () => {
it('should return list results', async () => {
const storageReference = firebase.storage().ref('/list');
const result = await storageReference.list();
result.constructor.name.should.eql('StorageListResult');
result.should.have.property('nextPageToken');
result.items.should.be.Array();
result.items.length.should.be.greaterThan(0);
result.items[0].constructor.name.should.eql('StorageReference');
result.prefixes.should.be.Array();
result.prefixes.length.should.be.greaterThan(0);
result.prefixes[0].constructor.name.should.eql('StorageReference');
});
it('throws if options is not an object', () => {
try {
const storageReference = firebase.storage().ref('/');
storageReference.list(123);
return Promise.reject(new Error('Did not throw'));
} catch (error) {
error.message.should.containEql("'options' expected an object value");
return Promise.resolve();
}
});
describe('maxResults', () => {
it('should limit with maxResults are passed', async () => {
const storageReference = firebase.storage().ref('/list');
const result = await storageReference.list({
maxResults: 1,
});
result.nextPageToken.should.be.String();
result.items.should.be.Array();
result.items.length.should.eql(1);
result.items[0].constructor.name.should.eql('StorageReference');
result.prefixes.should.be.Array();
// todo length?
});
it('throws if maxResults is not a number', () => {
try {
const storageReference = firebase.storage().ref('/list');
storageReference.list({
maxResults: '123',
});
return Promise.reject(new Error('Did not throw'));
} catch (error) {
error.message.should.containEql("'options.maxResults' expected a number value");
return Promise.resolve();
}
});
it('throws if maxResults is not a valid number', () => {
try {
const storageReference = firebase.storage().ref('/list');
storageReference.list({
maxResults: 2000,
});
return Promise.reject(new Error('Did not throw'));
} catch (error) {
error.message.should.containEql(
"'options.maxResults' expected a number value between 1-1000",
);
return Promise.resolve();
}
});
});
describe('pageToken', () => {
it('throws if pageToken is not a string', () => {
try {
const storageReference = firebase.storage().ref('/list');
storageReference.list({
pageToken: 123,
});
return Promise.reject(new Error('Did not throw'));
} catch (error) {
error.message.should.containEql("'options.pageToken' expected a string value");
return Promise.resolve();
}
});
it('should return and use a page token', async () => {
const storageReference = firebase.storage().ref('/list');
const result1 = await storageReference.list({
maxResults: 1,
});
const item1 = result1.items[0].fullPath;
const result2 = await storageReference.list({
maxResults: 1,
pageToken: result1.nextPageToken,
});
const item2 = result2.items[0].fullPath;
if (item1 === item2) {
throw new Error('Expected item results to be different.');
}
});
});
});
describe('listAll', () => {
it('should return all results', async () => {
const storageReference = firebase.storage().ref('/list');
const result = await storageReference.listAll();
should.equal(result.nextPageToken, null);
result.items.should.be.Array();
result.items.length.should.be.greaterThan(0);
result.items[0].constructor.name.should.eql('StorageReference');
result.prefixes.should.be.Array();
result.prefixes.length.should.be.greaterThan(0);
result.prefixes[0].constructor.name.should.eql('StorageReference');
});
});
describe('updateMetadata', () => {
it('should return the updated metadata for a file', async () => {
const storageReference = firebase.storage().ref('/writeOnly.jpeg');

View File

@@ -25,6 +25,8 @@
+ (NSDictionary *)metadataToDict:(FIRStorageMetadata *)metadata;
+ (NSDictionary *)listResultToDict:(FIRStorageListResult *)listResult;
+ (PHAsset *)fetchAssetForPath:(NSString *)localFilePath;
+ (NSString *)utiToMimeType:(NSString *)dataUTI;

View File

@@ -299,6 +299,25 @@
return storageMetadata;
}
+ (NSDictionary *)listResultToDict:(FIRStorageListResult *)listResult {
NSMutableArray *items = [[NSMutableArray alloc]init];
NSMutableArray *prefixes = [[NSMutableArray alloc]init];
for (FIRStorageReference *ref in listResult.items) {
[items addObject:[ref fullPath]];
}
for (FIRStorageReference *ref in listResult.prefixes) {
[prefixes addObject:[ref fullPath]];
}
return @{
@"nextPageToken": [listResult pageToken] != nil ? (id) [listResult pageToken] : [NSNull null],
@"items": items,
@"prefixes": prefixes,
};
}
+ (NSMutableDictionary *)getUploadTaskAsDictionary:(FIRStorageTaskSnapshot *)task {
if (task == nil) {
return [@{

View File

@@ -153,6 +153,59 @@ RCT_EXPORT_METHOD(updateMetadata:
}];
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#list
*/
RCT_EXPORT_METHOD(list:
(FIRApp *) firebaseApp
: (NSString *) url
: (NSDictionary *) listOptions
: (RCTPromiseResolveBlock) resolve
: (RCTPromiseRejectBlock) reject
) {
FIRStorageReference *storageReference = [self getReferenceFromUrl:url app:firebaseApp];
long *maxResults = [listOptions[@"maxResults"] pointerValue];
id completionBlock = ^(FIRStorageListResult *result, NSError *error) {
if (error != nil) {
[self promiseRejectStorageException:reject error:error];
} else {
NSDictionary * dick = [RNFBStorageCommon listResultToDict:result];
// so we can see the result
resolve(dick);
}
};
if (listOptions[@"pageToken"]) {
NSString *pageToken = listOptions[@"pageToken"];
[storageReference listWithMaxResults:(int64_t) maxResults pageToken:pageToken completion:completionBlock];
} else {
[storageReference listWithMaxResults:(int64_t) maxResults completion:completionBlock];
}
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#listAll
*/
RCT_EXPORT_METHOD(listAll:
(FIRApp *) firebaseApp
: (NSString *) url
: (RCTPromiseResolveBlock) resolve
: (RCTPromiseRejectBlock) reject
) {
FIRStorageReference *storageReference = [self getReferenceFromUrl:url app:firebaseApp];
id completionBlock = ^(FIRStorageListResult *result, NSError *error) {
if (error != nil) {
[self promiseRejectStorageException:reject error:error];
} else {
resolve([RNFBStorageCommon listResultToDict:result]);
}
};
[storageReference listAllWithCompletion:completionBlock];
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
*/

View File

@@ -0,0 +1,43 @@
/* eslint-disable no-console */
/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// To avoid React Native require cycle warnings
let StorageReference = null;
export function provideStorageReferenceClass(storageReference) {
StorageReference = storageReference;
}
export default class StorageListResult {
constructor(storage, nativeData) {
this._nextPageToken = nativeData.nextPageToken || null;
this._items = nativeData.items.map(path => new StorageReference(storage, path));
this._prefixes = nativeData.prefixes.map(path => new StorageReference(storage, path));
}
get items() {
return this._items;
}
get nextPageToken() {
return this._nextPageToken;
}
get prefixes() {
return this._prefixes;
}
}

View File

@@ -25,11 +25,15 @@ import {
getDataUrlParts,
pathLastComponent,
ReferenceBase,
isObject,
hasOwnProperty,
isNumber,
} from '@react-native-firebase/common';
import { validateMetadata } from './utils';
import StorageStatics from './StorageStatics';
import StorageUploadTask from './StorageUploadTask';
import StorageDownloadTask from './StorageDownloadTask';
import StorageListResult, { provideStorageReferenceClass } from './StorageListResult';
export default class StorageReference extends ReferenceBase {
constructor(storage, path) {
@@ -112,6 +116,64 @@ export default class StorageReference extends ReferenceBase {
return this._storage.native.getMetadata(this.toString());
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#list
*/
list(options) {
if (!isUndefined(options) && !isObject(options)) {
throw new Error(
"firebase.storage.StorageReference.list(*) 'options' expected an object value.",
);
}
const listOptions = {
maxResults: 1000,
};
if (options) {
if (hasOwnProperty(options, 'maxResults')) {
if (!isNumber(options.maxResults)) {
throw new Error(
"firebase.storage.StorageReference.list(*) 'options.maxResults' expected a number value.",
);
}
// todo integer check
if (options.maxResults < 1 || options.maxResults > 1000) {
throw new Error(
"firebase.storage.StorageReference.list(*) 'options.maxResults' expected a number value between 1-1000.",
);
}
listOptions.maxResults = options.maxResults;
}
if (options.pageToken) {
if (!isString(options.pageToken)) {
throw new Error(
"firebase.storage.StorageReference.list(*) 'options.pageToken' expected a string value.",
);
}
listOptions.pageToken = options.pageToken;
}
}
return this._storage.native
.list(this.toString(), listOptions)
.then(data => new StorageListResult(this._storage, data));
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#listAll
*/
listAll() {
return this._storage.native
.listAll(this.toString())
.then(data => new StorageListResult(this._storage, data));
}
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#put
*/
@@ -182,7 +244,7 @@ export default class StorageReference extends ReferenceBase {
*/
toString() {
if (this.path.length <= 1) {
return this._storage._customUrlOrRegion;
return `${this._storage._customUrlOrRegion}`;
}
return `${this._storage._customUrlOrRegion}/${this.path}`;
@@ -244,3 +306,5 @@ export default class StorageReference extends ReferenceBase {
);
}
}
provideStorageReferenceClass(StorageReference);

View File

@@ -531,6 +531,49 @@ export namespace Storage {
*/
getMetadata(): Promise<FullMetadata>;
/**
* List items (files) and prefixes (folders) under this storage reference.
*
* List API is only available for Firebase Rules Version 2.
*
* GCS is a key-blob store. Firebase Storage imposes the semantic of '/' delimited folder structure.
* Refer to GCS's List API if you want to learn more.
*
* To adhere to Firebase Rules's Semantics, Firebase Storage does not support objects whose paths
* end with "/" or contain two consecutive "/"s. Firebase Storage List API will filter these unsupported objects.
* list() may fail if there are too many unsupported objects in the bucket.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('/');
* const results = await ref.list({
* maxResults: 30,
* });
* ```
*
* @param options An optional ListOptions interface.
*/
list(options?: ListOptions): Promise<ListResult>;
/**
* List all items (files) and prefixes (folders) under this storage reference.
*
* This is a helper method for calling list() repeatedly until there are no more results. The default pagination size is 1000.
*
* Note: The results may not be consistent if objects are changed while this operation is running.
*
* Warning: `listAll` may potentially consume too many resources if there are too many results.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('/');
* const results = await ref.listAll();
* ```
*/
listAll(): Promise<ListResult>;
/**
* Puts a file from local disk onto the storage bucket.
*
@@ -870,6 +913,43 @@ export namespace Storage {
error?: NativeFirebaseError;
}
/**
* The options `list()` accepts.
*/
export interface ListOptions {
/**
* If set, limits the total number of `prefixes` and `items` to return. The default and maximum maxResults is 1000.
*/
maxResults?: number;
/**
* The `nextPageToken` from a previous call to `list()`. If provided, listing is resumed from the previous position.
*/
pageToken?: string;
}
/**
* Result returned by `list()`.
*/
export interface ListResult {
/**
* Objects in this directory. You can call `getMetadate()` and `getDownloadUrl()` on them.
*/
items: Reference[];
/**
* If set, there might be more results for this list. Use this token to resume the list.
*/
nextPageToken: string | null;
/**
* References to prefixes (sub-folders). You can call `list()` on them to get its contents.
*
* Folders are implicit based on '/' in the object paths. For example, if a bucket has two objects '/a/b/1' and '/a/b/2', list('/a') will return '/a/b' as a prefix.
*/
prefixes: Reference[];
}
/**
* The Cloud Storage service is available for the default app, a given app or a specific storage bucket.
*

View File

@@ -507,36 +507,286 @@ export interface FullMetadata extends SettableMetadata {
updated: string;
}
// TODO(salakar) -----\/
/**
* The options `list()` accepts.
*/
export interface ListOptions {
/**
* If set, limits the total number of `prefixes` and `items` to return. The default and maximum maxResults is 1000.
*/
maxResults?: number;
/**
* The `nextPageToken` from a previous call to `list()`. If provided, listing is resumed from the previous position.
*/
pageToken?: string;
}
/**
* Result returned by `list()`.
*/
export interface ListResult {
/**
* Objects in this directory. You can call `getMetadate()` and `getDownloadUrl()` on them.
*/
items: Reference[];
/**
* If set, there might be more results for this list. Use this token to resume the list.
*/
nextPageToken: string | null;
/**
* References to prefixes (sub-folders). You can call `list()` on them to get its contents.
*
* Folders are implicit based on '/' in the object paths. For example, if a bucket has two objects '/a/b/1' and '/a/b/2', list('/a') will return '/a/b' as a prefix.
*/
prefixes: Reference[];
}
/**
* Represents a reference to a Google Cloud Storage object in React Native Firebase.
*
* A reference can be used to upload and download storage objects, get/set storage object metadata, retrieve storage object download urls and delete storage objects.
*
* #### Example 1
*
* Get a reference to a specific storage path.
*
* ```js
* const ref = firebase.storage().ref('invertase/logo.png');
* ```
*
* #### Example 2
*
* Get a reference to a specific storage path on another bucket in the same firebase project.
*
* ```js
* const ref = firebase.storage().refFromURL('gs://other-bucket/invertase/logo.png');
* ```
*/
export interface Reference {
/**
* The name of the bucket containing this reference's object.
*/
bucket: string;
/**
* A reference pointing to the parent location of this reference, or null if this reference is the root.
*/
parent: Reference | null;
/**
* The full path of this object.
*/
fullPath: string;
/**
* The short name of this object, which is the last component of the full path. For example,
* if fullPath is 'full/path/image.png', name is 'image.png'.
*/
name: string;
/**
* A reference to the root of this reference's bucket.
*/
root: Reference;
/**
* The storage service associated with this reference.
*/
storage: Module;
/**
* Returns a gs:// URL for this object in the form `gs://<bucket>/<path>/<to>/<object>`.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/logo.png');
* console.log('Full path: ', ref.toString()); // gs://invertase.io/invertase/logo.png
* ```
*/
toString(): string;
/**
* Returns a reference to a relative path from this reference.
*
* #### Example
*
* ```js
* const parent = firebase.storage().ref('invertase');
* const ref = parent.child('logo.png');
* ```
*
* @param path The relative path from this reference. Leading, trailing, and consecutive slashes are removed.
*/
child(path: string): Reference;
/**
* Deletes the object at this reference's location.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/logo.png');
* await ref.delete();
* ```
*/
delete(): Promise<void>;
/**
* Fetches a long lived download URL for this object.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/logo.png');
* const url = await ref.getDownloadURL();
* ```
*/
getDownloadURL(): Promise<string>;
/**
* Fetches metadata for the object at this location, if one exists.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/logo.png');
* const metadata = await ref.getMetadata();
* console.log('Cache control: ', metadata.cacheControl);
* ```
*/
getMetadata(): Promise<FullMetadata>;
/**
* List items (files) and prefixes (folders) under this storage reference.
*
* List API is only available for Firebase Rules Version 2.
*
* GCS is a key-blob store. Firebase Storage imposes the semantic of '/' delimited folder structure.
* Refer to GCS's List API if you want to learn more.
*
* To adhere to Firebase Rules's Semantics, Firebase Storage does not support objects whose paths
* end with "/" or contain two consecutive "/"s. Firebase Storage List API will filter these unsupported objects.
* list() may fail if there are too many unsupported objects in the bucket.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('/');
* const results = await ref.list({
* maxResults: 30,
* });
* ```
*
* @param options An optional ListOptions interface.
*/
list(options?: ListOptions): Promise<ListResult>;
/**
* List all items (files) and prefixes (folders) under this storage reference.
*
* This is a helper method for calling list() repeatedly until there are no more results. The default pagination size is 1000.
*
* Note: The results may not be consistent if objects are changed while this operation is running.
*
* Warning: `listAll` may potentially consume too many resources if there are too many results.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('/');
* const results = await ref.listAll();
* ```
*/
listAll(): Promise<ListResult>;
/**
* Puts a file from local disk onto the storage bucket.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/new-logo.png');
* const path = `${firebase.utils.FilePath.DOCUMENT_DIRECTORY}/new-logo.png`;
* const task = ref.putFile(path, {
* cacheControl: 'no-store', // disable caching
* });
* ```
*
* @param localFilePath The local file path to upload to the bucket at the reference location.
* @param metadata Any additional `SettableMetadata` for this task.
*/
putFile(localFilePath: string, metadata?: SettableMetadata): Task;
/**
* Downloads a file to the specified local file path on the device.
*
* #### Example
*
* Get a Download Storage task to download a file:
*
* ```js
* const downloadTo = `${firebase.utils.FilePath.DOCUMENT_DIRECTORY}/foobar.json`;
*
* const task = firebase.storage().ref('/foo/bar.json').writeToFile(downloadTo);
* ```
* @param localFilePath
*/
writeToFile(localFilePath: string): Task;
/**
* Puts data onto the storage bucket.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/new-logo.png');
* const task = ref.put(BLOB, {
* cacheControl: 'no-store', // disable caching
* });
* ```
*
* @param data The data to upload to the storage bucket at the reference location.
* @param metadata
*/
put(data: Blob | Uint8Array | ArrayBuffer, metadata?: SettableMetadata): Task;
putString(data: string, format?: StringFormat, metadata?: SettableMetadata): Task;
/**
* Puts a string on the storage bucket. Depending on the string type, set a {@link storage.StringFormat} type.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/new-logo.png');
* const task = ref.putString('PEZvbyBCYXI+', firebase.storage.StringFormat.BASE64, {
* cacheControl: 'no-store', // disable caching
* });
* ```
*
* @param data The string data, must match the format provided.
* @param format The format type of the string, e.g. a Base64 format string.
* @param metadata Any additional `SettableMetadata` for this task.
*/
putString(
data: string,
format?: 'raw' | 'base64' | 'base64url' | 'data_url',
metadata?: SettableMetadata,
): Task;
/**
* Updates the metadata for this reference object on the storage bucket.
*
* #### Example
*
* ```js
* const ref = firebase.storage().ref('invertase/nsfw-logo.png');
* const updatedMetadata = await ref.updateMetadata({
* customMetadata: {
* 'nsfw': 'true',
* }
* });
* ```
*
* @param metadata A `SettableMetadata` instance to update.
*/
updateMetadata(metadata: SettableMetadata): Promise<FullMetadata>;
}

View File

@@ -46,7 +46,7 @@ const PACKAGES = [
'utils',
'ml-natural-language',
'ml-vision',
// 'invites',
'invites',
'fiam',
'auth',
'database',

View File

@@ -312,62 +312,62 @@ PODS:
- React-RCTWebSocket (0.60.4):
- React-Core (= 0.60.4)
- React-fishhook (= 0.60.4)
- RNFBAnalytics (0.0.26):
- RNFBAnalytics (0.1.0):
- Firebase/Core (~> 6.5.0)
- React
- RNFBApp
- RNFBApp (0.0.26):
- RNFBApp (0.1.0):
- Firebase/Core (~> 6.5.0)
- React
- RNFBAuth (0.0.26):
- RNFBAuth (0.1.0):
- Firebase/Auth (~> 6.5.0)
- Firebase/Core (~> 6.5.0)
- React
- RNFBApp
- RNFBConfig (0.0.26):
- RNFBConfig (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/RemoteConfig (~> 6.5.0)
- React
- RNFBApp
- RNFBCrashlytics (0.0.26):
- RNFBCrashlytics (0.1.0):
- Crashlytics (~> 3.12.0)
- Fabric (~> 1.9.0)
- Firebase/Core (~> 6.5.0)
- React
- RNFBApp
- RNFBDatabase (0.0.26):
- RNFBDatabase (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/Database (~> 6.5.0)
- React
- RNFBApp
- RNFBDynamicLinks (0.0.26):
- RNFBDynamicLinks (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/DynamicLinks (~> 6.5.0)
- React
- RNFBApp
- RNFBFiam (0.0.26):
- RNFBFiam (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/InAppMessagingDisplay (~> 6.5.0)
- React
- RNFBApp
- RNFBFirestore (0.0.26):
- RNFBFirestore (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/Firestore (~> 6.5.0)
- React
- RNFBApp
- RNFBFunctions (0.0.26):
- RNFBFunctions (0.1.0):
- Firebase/Functions (~> 6.5.0)
- React
- RNFBApp
- RNFBIid (0.0.26):
- RNFBIid (0.1.0):
- Firebase/Core (~> 6.5.0)
- React
- RNFBApp
- RNFBMessaging (0.0.26):
- RNFBMessaging (0.1.0):
- Firebase/Messaging (~> 6.5.0)
- React
- RNFBApp
- RNFBMlNaturalLanguage (0.0.26):
- RNFBMlNaturalLanguage (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/MLCommon (~> 6.5.0)
- Firebase/MLNaturalLanguage (~> 6.5.0)
@@ -375,17 +375,17 @@ PODS:
- Firebase/MLNLSmartReply (~> 6.5.0)
- React
- RNFBApp
- RNFBPerf (0.0.26):
- RNFBPerf (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/Performance (~> 6.5.0)
- React
- RNFBApp
- RNFBStorage (0.0.26):
- RNFBStorage (0.1.0):
- Firebase/Core (~> 6.5.0)
- Firebase/Storage (~> 6.5.0)
- React
- RNFBApp
- RNFBUtils (0.0.26):
- RNFBUtils (0.1.0):
- Firebase/Core (~> 6.5.0)
- React
- RNFBApp
@@ -608,22 +608,22 @@ SPEC CHECKSUMS:
React-RCTText: e0f224898b13af9aa036ea7cb3d438daa68c1044
React-RCTVibration: 0bea40cd51bd089bd591a8f74c86e91fdf2666c5
React-RCTWebSocket: 163873f4cdd5f1058a9483443404fc3801581cb6
RNFBAnalytics: 71420d837ca8540a46cb2a864923ffd270be7d1d
RNFBApp: 6e1a89e541d640780af502473f3478113f3fcef0
RNFBAuth: 19478f2c21bb3088dedc46c66a60288d905ba3e0
RNFBConfig: 9a6e827781f8030dd0b9a9dd9a08551dafee4ff7
RNFBCrashlytics: 759d6774287ed596e248b0a288eaa495c52dbec7
RNFBDatabase: be81d1e368b2b748965fcf474a4a7f7e6e74df8d
RNFBDynamicLinks: c093448437b3e5dd50fde383af69a84225924c58
RNFBFiam: f500776891ab53a2380766a8dda331484989962b
RNFBFirestore: 10af675d54d88fd44bf6c6666d1e5d806ba72bcd
RNFBFunctions: 38ae5a1fb23af6ebacb5ab62d2d0004cb0e4a580
RNFBIid: 179e4d9b85cd267ec841d00f532bd16693d6fa75
RNFBMessaging: e8cd71ce0d54c378038a24cee3122e35ea596bec
RNFBMlNaturalLanguage: e61654420ab0020fd72f0144878d3a40e5ce2907
RNFBPerf: ed8f26e1ad933ab9e4b0657dd7f1d5a0025dd59f
RNFBStorage: f7fc03c0be9e6d8d3b4bc7a25be81841118bfa18
RNFBUtils: 9e8b7925e0a235ba08a81e1f3c877b3f2cb06723
RNFBAnalytics: 69a5aa7ab165b09e2c032b4240eda4df4162a6af
RNFBApp: 1517b1e22b8933c87d5d7eb232486b68afdcb308
RNFBAuth: b844d8e2e89b04b30ecae8dff2e2decc68ad488e
RNFBConfig: 28528ef2531045b081fe3166d51a69182389bb27
RNFBCrashlytics: 207f072a151242027ccc20549ac616a52bf25ef5
RNFBDatabase: cab5cef18b05d5ec4d190d68ddab0d495c254995
RNFBDynamicLinks: dbb1d425136f6cd6a3e4029662841be373e931f3
RNFBFiam: 083a9ed7b8cf5c3b2d0022f129a3bcd05edd9a58
RNFBFirestore: 6f4ab90c880906aaa0b2cc63d9fe0ce28cb1d3cd
RNFBFunctions: ca2cfc68ed63d0621c8ef46def3d93972120c293
RNFBIid: a068c7f9692b4c68ce3ff5d7b3c0cbb9c430eb94
RNFBMessaging: eda58ceb6650043d65ab28eccb1ca272291d0fcd
RNFBMlNaturalLanguage: bdc99e787ad3183d6a19e7222503d707e28eb4f6
RNFBPerf: bfb1f25cc79452b99272c9382b41cec20e9c058c
RNFBStorage: 195d5dd4ebc11f8d2e436f5d0eff2360285b8d31
RNFBUtils: 400e5a6c825ae8720bedb2fd876d3d02a4d51adc
yoga: c2c050f6ae6e222534760cc82f559b89214b67e2
PODFILE CHECKSUM: 0142c48fc8ad8e230e216fc15af267405c520250