merge master into omer_links

This commit is contained in:
Omer Levy
2017-10-05 20:07:24 +03:00
101 changed files with 6104 additions and 2033 deletions

View File

@@ -5,9 +5,17 @@
[![Package Quality](http://npm.packagequality.com/shield/react-native-firebase.svg?style=flat-square)](http://packagequality.com/#?package=react-native-firebase)
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
[![Twitter Follow](https://img.shields.io/twitter/follow/rnfirebase.svg?style=social&label=Follow)](https://twitter.com/rnfirebase)
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
---
We also support **both** databases: Realtime Database and Cloud Firestore!
---
<!---
[![License](https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square)](/LICENSE)
@@ -15,7 +23,7 @@
<hr>
> [Current Docs](http://invertase.link/docs) <b>|</b> [@next Docs](http://invertase.link/v3) <b>|</b> [iOS Install Guide](http://invertase.link/ios) <b>|</b> [Android Install Guide](http://invertase.link/android) <b>|</b> [FAQs](http://invertase.io/react-native-firebase/#/faqs) <b>|</b> [Feature Requests](http://invertase.link/requests)
> [Docs](http://invertase.link/v3-docs) <b>|</b> [iOS Install Guide](http://invertase.link/ios) <b>|</b> [Android Install Guide](http://invertase.link/android) <b>|</b> [FAQs](http://invertase.io/react-native-firebase/#/faqs) <b>|</b> [Feature Requests](http://invertase.link/requests)
<hr>
@@ -57,6 +65,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | ❌ | ❌ |
| **Firestore** | ❌ | ❌ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ | ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
@@ -68,18 +77,13 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
---
### Supported versions - React Native / Firebase
> The table below shows the supported version of `react-native-firebase` for different React Native versions
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
| | v0.36 - v0.39 | v0.40 - v0.46 | v0.47 +
| ------------------------------- | :---: | :---: | :---: |
| react-native-firebase | 1.X.X | 2.X.X | 2.1.X |
> The table below shows the minimum supported versions of the Firebase SDKs for each version of `react-native-firebase`
| | v1 | v2 | v3 |
| ---------------------- | :---: | :---: | :---: |
| Firebase Android SDK | 10.2.0+ | 11.0.0 + | 11.2.0 + |
| Firebase iOS SDK | 3.15.0+ | 4.0.0 + | 4.0.0 + |
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X |
|------------------------|-------------|-------------|-----------------|----------|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + |
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + |
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + |
---

View File

@@ -1,5 +1,5 @@
buildscript {
ext.firebaseVersion = '11.2.0'
ext.firebaseVersion = '11.4.2'
repositories {
jcenter()
}
@@ -33,6 +33,10 @@ android {
allprojects {
repositories {
jcenter()
mavenLocal()
maven {
url "https://maven.google.com"
}
}
}
@@ -85,5 +89,6 @@ dependencies {
compile "com.google.firebase:firebase-crash:$firebaseVersion"
compile "com.google.firebase:firebase-perf:$firebaseVersion"
compile "com.google.firebase:firebase-ads:$firebaseVersion"
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
compile "com.google.firebase:firebase-invites:$firebaseVersion"
}

View File

@@ -0,0 +1,27 @@
package io.invertase.firebase;
public class ErrorUtils {
/**
* Wrap a message string with the specified service name e.g. 'Database'
*
* @param message
* @param service
* @param fullCode
* @return
*/
public static String getMessageWithService(String message, String service, String fullCode) {
// Service: Error message (service/code).
return service + ": " + message + " (" + fullCode.toLowerCase() + ").";
}
/**
* Generate a service error code string e.g. 'DATABASE/PERMISSION-DENIED'
*
* @param service
* @param code
* @return
*/
public static String getCodeWithService(String service, String code) {
return service.toLowerCase() + "/" + code.toLowerCase();
}
}

View File

@@ -31,18 +31,6 @@ public class RNFirebasePackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -78,7 +78,6 @@ public class Utils {
/**
* @param dataSnapshot
* @param registration
* @param previousChildName
* @return
*/

View File

@@ -29,18 +29,6 @@ public class RNFirebaseAdMobPackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -33,18 +33,6 @@ public class RNFirebaseAnalyticsPackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -813,7 +813,7 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
TimeUnit.SECONDS,
activity,
callbacks
// ,PhoneAuthProvider.ForceResendingToken.zzboe() // TODO FORCE RESENDING
//, PhoneAuthProvider.ForceResendingToken.zzboe() // TODO FORCE RESENDING
);
}
}

View File

@@ -28,18 +28,6 @@ public class RNFirebaseAuthPackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -28,18 +28,6 @@ public class RNFirebaseRemoteConfigPackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -28,18 +28,6 @@ public class RNFirebaseCrashPackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -26,6 +26,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
@@ -522,30 +523,6 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
return existingRef;
}
/**
* Wrap a message string with the specified service name e.g. 'Database'
*
* @param message
* @param service
* @param fullCode
* @return
*/
private static String getMessageWithService(String message, String service, String fullCode) {
// Service: Error message (service/code).
return service + ": " + message + " (" + fullCode.toLowerCase() + ").";
}
/**
* Generate a service error code string e.g. 'DATABASE/PERMISSION-DENIED'
*
* @param service
* @param code
* @return
*/
private static String getCodeWithService(String service, String code) {
return service.toLowerCase() + "/" + code.toLowerCase();
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
@@ -564,56 +541,56 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
switch (nativeError.getCode()) {
case DatabaseError.DATA_STALE:
code = getCodeWithService(service, "data-stale");
message = getMessageWithService("The transaction needs to be run again with current data.", service, code);
code = ErrorUtils.getCodeWithService(service, "data-stale");
message = ErrorUtils.getMessageWithService("The transaction needs to be run again with current data.", service, code);
break;
case DatabaseError.OPERATION_FAILED:
code = getCodeWithService(service, "failure");
message = getMessageWithService("The server indicated that this operation failed.", service, code);
code = ErrorUtils.getCodeWithService(service, "failure");
message = ErrorUtils.getMessageWithService("The server indicated that this operation failed.", service, code);
break;
case DatabaseError.PERMISSION_DENIED:
code = getCodeWithService(service, "permission-denied");
message = getMessageWithService("Client doesn't have permission to access the desired data.", service, code);
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService("Client doesn't have permission to access the desired data.", service, code);
break;
case DatabaseError.DISCONNECTED:
code = getCodeWithService(service, "disconnected");
message = getMessageWithService("The operation had to be aborted due to a network disconnect.", service, code);
code = ErrorUtils.getCodeWithService(service, "disconnected");
message = ErrorUtils.getMessageWithService("The operation had to be aborted due to a network disconnect.", service, code);
break;
case DatabaseError.EXPIRED_TOKEN:
code = getCodeWithService(service, "expired-token");
message = getMessageWithService("The supplied auth token has expired.", service, code);
code = ErrorUtils.getCodeWithService(service, "expired-token");
message = ErrorUtils.getMessageWithService("The supplied auth token has expired.", service, code);
break;
case DatabaseError.INVALID_TOKEN:
code = getCodeWithService(service, "invalid-token");
message = getMessageWithService("The supplied auth token was invalid.", service, code);
code = ErrorUtils.getCodeWithService(service, "invalid-token");
message = ErrorUtils.getMessageWithService("The supplied auth token was invalid.", service, code);
break;
case DatabaseError.MAX_RETRIES:
code = getCodeWithService(service, "max-retries");
message = getMessageWithService("The transaction had too many retries.", service, code);
code = ErrorUtils.getCodeWithService(service, "max-retries");
message = ErrorUtils.getMessageWithService("The transaction had too many retries.", service, code);
break;
case DatabaseError.OVERRIDDEN_BY_SET:
code = getCodeWithService(service, "overridden-by-set");
message = getMessageWithService("The transaction was overridden by a subsequent set.", service, code);
code = ErrorUtils.getCodeWithService(service, "overridden-by-set");
message = ErrorUtils.getMessageWithService("The transaction was overridden by a subsequent set.", service, code);
break;
case DatabaseError.UNAVAILABLE:
code = getCodeWithService(service, "unavailable");
message = getMessageWithService("The service is unavailable.", service, code);
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService("The service is unavailable.", service, code);
break;
case DatabaseError.USER_CODE_EXCEPTION:
code = getCodeWithService(service, "user-code-exception");
message = getMessageWithService("User code called from the Firebase Database runloop threw an exception.", service, code);
code = ErrorUtils.getCodeWithService(service, "user-code-exception");
message = ErrorUtils.getMessageWithService("User code called from the Firebase Database runloop threw an exception.", service, code);
break;
case DatabaseError.NETWORK_ERROR:
code = getCodeWithService(service, "network-error");
message = getMessageWithService("The operation could not be performed due to a network error.", service, code);
code = ErrorUtils.getCodeWithService(service, "network-error");
message = ErrorUtils.getMessageWithService("The operation could not be performed due to a network error.", service, code);
break;
case DatabaseError.WRITE_CANCELED:
code = getCodeWithService(service, "write-cancelled");
message = getMessageWithService("The write was canceled by the user.", service, code);
code = ErrorUtils.getCodeWithService(service, "write-cancelled");
message = ErrorUtils.getMessageWithService("The write was canceled by the user.", service, code);
break;
default:
code = getCodeWithService(service, "unknown");
message = getMessageWithService("An unknown error occurred.", service, code);
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);

View File

@@ -28,18 +28,6 @@ public class RNFirebaseDatabasePackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -29,8 +29,8 @@ class RNFirebaseDatabaseReference {
private String appName;
private ReactContext reactContext;
private static final String TAG = "RNFirebaseDBReference";
private HashMap<String, ChildEventListener> childEventListeners;
private HashMap<String, ValueEventListener> valueEventListeners;
private HashMap<String, ChildEventListener> childEventListeners = new HashMap<>();
private HashMap<String, ValueEventListener> valueEventListeners = new HashMap<>();
/**
* RNFirebase wrapper around FirebaseDatabaseReference,
@@ -47,8 +47,6 @@ class RNFirebaseDatabaseReference {
query = null;
appName = app;
reactContext = context;
childEventListeners = new HashMap<>();
valueEventListeners = new HashMap<>();
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
}

View File

@@ -0,0 +1,210 @@
package io.invertase.firebase.firestore;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.firestore.DocumentChange;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class FirestoreSerialize {
private static final String KEY_CHANGES = "changes";
private static final String KEY_DATA = "data";
private static final String KEY_DOC_CHANGE_DOCUMENT = "document";
private static final String KEY_DOC_CHANGE_NEW_INDEX = "newIndex";
private static final String KEY_DOC_CHANGE_OLD_INDEX = "oldIndex";
private static final String KEY_DOC_CHANGE_TYPE = "type";
private static final String KEY_DOCUMENTS = "documents";
private static final String KEY_METADATA = "metadata";
private static final String KEY_PATH = "path";
/**
* Convert a DocumentSnapshot instance into a React Native WritableMap
*
* @param documentSnapshot DocumentSnapshot
* @return WritableMap
*/
static WritableMap snapshotToWritableMap(DocumentSnapshot documentSnapshot) {
WritableMap documentMap = Arguments.createMap();
documentMap.putString(KEY_PATH, documentSnapshot.getReference().getPath());
if (documentSnapshot.exists()) {
documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData()));
}
// metadata
if (documentSnapshot.getMetadata() != null) {
WritableMap metadata = Arguments.createMap();
metadata.putBoolean("fromCache", documentSnapshot.getMetadata().isFromCache());
metadata.putBoolean("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
documentMap.putMap(KEY_METADATA, metadata);
}
return documentMap;
}
public static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) {
WritableMap queryMap = Arguments.createMap();
List<DocumentChange> documentChanges = querySnapshot.getDocumentChanges();
queryMap.putArray(KEY_CHANGES, documentChangesToWritableArray(documentChanges));
// documents
WritableArray documents = Arguments.createArray();
List<DocumentSnapshot> documentSnapshots = querySnapshot.getDocuments();
for (DocumentSnapshot documentSnapshot : documentSnapshots) {
documents.pushMap(snapshotToWritableMap(documentSnapshot));
}
queryMap.putArray(KEY_DOCUMENTS, documents);
// metadata
if (querySnapshot.getMetadata() != null) {
WritableMap metadata = Arguments.createMap();
metadata.putBoolean("fromCache", querySnapshot.getMetadata().isFromCache());
metadata.putBoolean("hasPendingWrites", querySnapshot.getMetadata().hasPendingWrites());
queryMap.putMap(KEY_METADATA, metadata);
}
return queryMap;
}
/**
* Convert a List of DocumentChange instances into a React Native WritableArray
*
* @param documentChanges List<DocumentChange>
* @return WritableArray
*/
static WritableArray documentChangesToWritableArray(List<DocumentChange> documentChanges) {
WritableArray documentChangesWritable = Arguments.createArray();
for (DocumentChange documentChange : documentChanges) {
documentChangesWritable.pushMap(documentChangeToWritableMap(documentChange));
}
return documentChangesWritable;
}
/**
* Convert a DocumentChange instance into a React Native WritableMap
*
* @param documentChange DocumentChange
* @return WritableMap
*/
static WritableMap documentChangeToWritableMap(DocumentChange documentChange) {
WritableMap documentChangeMap = Arguments.createMap();
switch (documentChange.getType()) {
case ADDED:
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "added");
break;
case REMOVED:
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "removed");
break;
case MODIFIED:
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "modified");
}
documentChangeMap.putMap(KEY_DOC_CHANGE_DOCUMENT,
snapshotToWritableMap(documentChange.getDocument()));
documentChangeMap.putInt(KEY_DOC_CHANGE_NEW_INDEX, documentChange.getNewIndex());
documentChangeMap.putInt(KEY_DOC_CHANGE_OLD_INDEX, documentChange.getOldIndex());
return documentChangeMap;
}
/**
* Converts an Object Map into a React Native WritableMap.
*
* @param map Map<String, Object>
* @return WritableMap
*/
static WritableMap objectMapToWritable(Map<String, Object> map) {
WritableMap writableMap = Arguments.createMap();
for (Map.Entry<String, Object> entry : map.entrySet()) {
putValue(writableMap, entry.getKey(), entry.getValue());
}
return writableMap;
}
/**
* Converts an Object array into a React Native WritableArray.
*
* @param array Object[]
* @return WritableArray
*/
static WritableArray objectArrayToWritable(Object[] array) {
WritableArray writableArray = Arguments.createArray();
for (Object item : array) {
if (item == null) {
writableArray.pushNull();
continue;
}
Class itemClass = item.getClass();
if (itemClass == Boolean.class) {
writableArray.pushBoolean((Boolean) item);
} else if (itemClass == Integer.class) {
writableArray.pushDouble(((Integer) item).doubleValue());
} else if (itemClass == Double.class) {
writableArray.pushDouble((Double) item);
} else if (itemClass == Float.class) {
writableArray.pushDouble(((Float) item).doubleValue());
} else if (itemClass == String.class) {
writableArray.pushString(item.toString());
} else if (itemClass == Map.class) {
writableArray.pushMap((objectMapToWritable((Map<String, Object>) item)));
} else if (itemClass == Arrays.class) {
writableArray.pushArray(objectArrayToWritable((Object[]) item));
} else if (itemClass == List.class || itemClass == ArrayList.class) {
List<Object> list = (List<Object>) item;
Object[] listAsArray = list.toArray(new Object[list.size()]);
writableArray.pushArray(objectArrayToWritable(listAsArray));
} else {
throw new RuntimeException("Cannot convert object of type " + item);
}
}
return writableArray;
}
/**
* Detects an objects type and calls the relevant WritableMap setter method to add the value.
*
* @param map WritableMap
* @param key String
* @param value Object
*/
static void putValue(WritableMap map, String key, Object value) {
if (value == null) {
map.putNull(key);
} else {
Class valueClass = value.getClass();
if (valueClass == Boolean.class) {
map.putBoolean(key, (Boolean) value);
} else if (valueClass == Integer.class) {
map.putDouble(key, ((Integer) value).doubleValue());
} else if (valueClass == Double.class) {
map.putDouble(key, (Double) value);
} else if (valueClass == Float.class) {
map.putDouble(key, ((Float) value).doubleValue());
} else if (valueClass == String.class) {
map.putString(key, value.toString());
} else if (valueClass == Map.class) {
map.putMap(key, (objectMapToWritable((Map<String, Object>) value)));
} else if (valueClass == Arrays.class) {
map.putArray(key, objectArrayToWritable((Object[]) value));
} else if (valueClass == List.class || valueClass == ArrayList.class) {
List<Object> list = (List<Object>) value;
Object[] array = list.toArray(new Object[list.size()]);
map.putArray(key, objectArrayToWritable(array));
} else {
throw new RuntimeException("Cannot convert object of type " + value);
}
}
}
}

View File

@@ -0,0 +1,340 @@
package io.invertase.firebase.firestore;
import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
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.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FieldValue;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.SetOptions;
import com.google.firebase.firestore.WriteBatch;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseFirestore";
// private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseFirestore(ReactApplicationContext reactContext) {
super(reactContext);
}
/*
* REACT NATIVE METHODS
*/
@ReactMethod
public void collectionGet(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, final Promise promise) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
ref.get(promise);
}
@ReactMethod
public void collectionOffSnapshot(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId) {
RNFirebaseFirestoreCollectionReference.offSnapshot(listenerId);
}
@ReactMethod
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
ref.onSnapshot(listenerId);
}
@ReactMethod
public void documentBatch(final String appName, final ReadableArray writes,
final Promise promise) {
FirebaseFirestore firestore = getFirestoreForApp(appName);
WriteBatch batch = firestore.batch();
final List<Object> writesArray = Utils.recursivelyDeconstructReadableArray(writes);
for (Object w : writesArray) {
Map<String, Object> write = (Map) w;
String type = (String) write.get("type");
String path = (String) write.get("path");
Map<String, Object> data = (Map) write.get("data");
DocumentReference ref = firestore.document(path);
switch (type) {
case "DELETE":
batch = batch.delete(ref);
break;
case "SET":
Map<String, Object> options = (Map) write.get("options");
if (options != null && options.containsKey("merge") && (boolean)options.get("merge")) {
batch = batch.set(ref, data, SetOptions.merge());
} else {
batch = batch.set(ref, data);
}
break;
case "UPDATE":
batch = batch.update(ref, data);
break;
}
}
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "documentBatch:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "documentBatch:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
}
});
}
@ReactMethod
public void documentCollections(String appName, String path, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.collections(promise);
}
@ReactMethod
public void documentCreate(String appName, String path, ReadableMap data, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.create(data, promise);
}
@ReactMethod
public void documentDelete(String appName, String path, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.delete(promise);
}
@ReactMethod
public void documentGet(String appName, String path, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.get(promise);
}
@ReactMethod
public void documentGetAll(String appName, ReadableArray documents, final Promise promise) {
// Not supported on Android out of the box
}
@ReactMethod
public void documentOffSnapshot(String appName, String path, String listenerId) {
RNFirebaseFirestoreDocumentReference.offSnapshot(listenerId);
}
@ReactMethod
public void documentOnSnapshot(String appName, String path, String listenerId) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.onSnapshot(listenerId);
}
@ReactMethod
public void documentSet(String appName, String path, ReadableMap data, ReadableMap options, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.set(data, options, promise);
}
@ReactMethod
public void documentUpdate(String appName, String path, ReadableMap data, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.update(data, promise);
}
/*
* INTERNALS/UTILS
*/
/**
* Generates a js-like error from an exception and rejects the provided promise with it.
*
* @param exception Exception Exception normally from a task result.
* @param promise Promise react native promise
*/
static void promiseRejectException(Promise promise, FirebaseFirestoreException exception) {
WritableMap jsError = getJSError(exception);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
exception
);
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName
* @return
*/
static FirebaseFirestore getFirestoreForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
return FirebaseFirestore.getInstance(firebaseApp);
}
/**
* Get a collection reference for a specific app and path
*
* @param appName
* @param filters
* @param orders
* @param options
* @param path @return
*/
private RNFirebaseFirestoreCollectionReference getCollectionForAppPath(String appName, String path,
ReadableArray filters,
ReadableArray orders,
ReadableMap options) {
return new RNFirebaseFirestoreCollectionReference(this.getReactApplicationContext(), appName, path, filters, orders, options);
}
/**
* Get a document reference for a specific app and path
*
* @param appName
* @param path
* @return
*/
private RNFirebaseFirestoreDocumentReference getDocumentForAppPath(String appName, String path) {
return new RNFirebaseFirestoreDocumentReference(this.getReactApplicationContext(), appName, path);
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
*
* @param nativeException
* @return
*/
static WritableMap getJSError(FirebaseFirestoreException nativeException) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt("nativeErrorCode", nativeException.getCode().value());
errorMap.putString("nativeErrorMessage", nativeException.getMessage());
String code;
String message;
String service = "Firestore";
// TODO: Proper error mappings
switch (nativeException.getCode()) {
case OK:
code = ErrorUtils.getCodeWithService(service, "ok");
message = ErrorUtils.getMessageWithService("Ok.", service, code);
break;
case CANCELLED:
code = ErrorUtils.getCodeWithService(service, "cancelled");
message = ErrorUtils.getMessageWithService("The operation was cancelled.", service, code);
break;
case UNKNOWN:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("Unknown error or an error from a different error domain.", service, code);
break;
case INVALID_ARGUMENT:
code = ErrorUtils.getCodeWithService(service, "invalid-argument");
message = ErrorUtils.getMessageWithService("Client specified an invalid argument.", service, code);
break;
case DEADLINE_EXCEEDED:
code = ErrorUtils.getCodeWithService(service, "deadline-exceeded");
message = ErrorUtils.getMessageWithService("Deadline expired before operation could complete.", service, code);
break;
case NOT_FOUND:
code = ErrorUtils.getCodeWithService(service, "not-found");
message = ErrorUtils.getMessageWithService("Some requested document was not found.", service, code);
break;
case ALREADY_EXISTS:
code = ErrorUtils.getCodeWithService(service, "already-exists");
message = ErrorUtils.getMessageWithService("Some document that we attempted to create already exists.", service, code);
break;
case PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService("The caller does not have permission to execute the specified operation.", service, code);
break;
case RESOURCE_EXHAUSTED:
code = ErrorUtils.getCodeWithService(service, "resource-exhausted");
message = ErrorUtils.getMessageWithService("Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.", service, code);
break;
case FAILED_PRECONDITION:
code = ErrorUtils.getCodeWithService(service, "failed-precondition");
message = ErrorUtils.getMessageWithService("Operation was rejected because the system is not in a state required for the operation`s execution.", service, code);
break;
case ABORTED:
code = ErrorUtils.getCodeWithService(service, "aborted");
message = ErrorUtils.getMessageWithService("The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.", service, code);
break;
case OUT_OF_RANGE:
code = ErrorUtils.getCodeWithService(service, "out-of-range");
message = ErrorUtils.getMessageWithService("Operation was attempted past the valid range.", service, code);
break;
case UNIMPLEMENTED:
code = ErrorUtils.getCodeWithService(service, "unimplemented");
message = ErrorUtils.getMessageWithService("Operation is not implemented or not supported/enabled.", service, code);
break;
case INTERNAL:
code = ErrorUtils.getCodeWithService(service, "internal");
message = ErrorUtils.getMessageWithService("Internal errors.", service, code);
break;
case UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService("The service is currently unavailable.", service, code);
break;
case DATA_LOSS:
code = ErrorUtils.getCodeWithService(service, "data-loss");
message = ErrorUtils.getMessageWithService("Unrecoverable data loss or corruption.", service, code);
break;
case UNAUTHENTICATED:
code = ErrorUtils.getCodeWithService(service, "unauthenticated");
message = ErrorUtils.getMessageWithService("The request does not have valid authentication credentials for the operation.", service, code);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
}
/**
* React Method - returns this module name
*
* @return
*/
@Override
public String getName() {
return "RNFirebaseFirestore";
}
/**
* React Native constants for RNFirebaseFirestore
*
* @return
*/
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("deleteFieldValue", FieldValue.delete().toString());
constants.put("serverTimestampFieldValue", FieldValue.serverTimestamp().toString());
return constants;
}
}

View File

@@ -0,0 +1,218 @@
package io.invertase.firebase.firestore;
import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.ListenerRegistration;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreCollectionReference {
private static final String TAG = "RNFSCollectionReference";
private static Map<String, ListenerRegistration> collectionSnapshotListeners = new HashMap<>();
private final String appName;
private final String path;
private final ReadableArray filters;
private final ReadableArray orders;
private final ReadableMap options;
private final Query query;
private ReactContext reactContext;
RNFirebaseFirestoreCollectionReference(ReactContext reactContext, String appName, String path,
ReadableArray filters, ReadableArray orders,
ReadableMap options) {
this.appName = appName;
this.path = path;
this.filters = filters;
this.orders = orders;
this.options = options;
this.query = buildQuery();
this.reactContext = reactContext;
}
void get(final Promise promise) {
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult());
promise.resolve(data);
} else {
Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
}
});
}
public static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId) {
if (!collectionSnapshotListeners.containsKey(listenerId)) {
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
@Override
public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException exception) {
if (exception == null) {
handleQuerySnapshotEvent(listenerId, querySnapshot);
} else {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
handleQuerySnapshotError(listenerId, exception);
}
}
};
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(listener);
collectionSnapshotListeners.put(listenerId, listenerRegistration);
}
}
/*
* INTERNALS/UTILS
*/
boolean hasListeners() {
return !collectionSnapshotListeners.isEmpty();
}
private Query buildQuery() {
Query query = RNFirebaseFirestore.getFirestoreForApp(appName).collection(path);
query = applyFilters(query);
query = applyOrders(query);
query = applyOptions(query);
return query;
}
private Query applyFilters(Query query) {
List<Object> filtersList = Utils.recursivelyDeconstructReadableArray(filters);
for (Object f : filtersList) {
Map<String, Object> filter = (Map) f;
String fieldPath = (String) filter.get("fieldPath");
String operator = (String) filter.get("operator");
Object value = filter.get("value");
switch (operator) {
case "EQUAL":
query = query.whereEqualTo(fieldPath, value);
break;
case "GREATER_THAN":
query = query.whereGreaterThan(fieldPath, value);
break;
case "GREATER_THAN_OR_EQUAL":
query = query.whereGreaterThanOrEqualTo(fieldPath, value);
break;
case "LESS_THAN":
query = query.whereLessThan(fieldPath, value);
break;
case "LESS_THAN_OR_EQUAL":
query = query.whereLessThanOrEqualTo(fieldPath, value);
break;
}
}
return query;
}
private Query applyOrders(Query query) {
List<Object> ordersList = Utils.recursivelyDeconstructReadableArray(orders);
for (Object o : ordersList) {
Map<String, Object> order = (Map) o;
String direction = (String) order.get("direction");
String fieldPath = (String) order.get("fieldPath");
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
}
return query;
}
private Query applyOptions(Query query) {
if (options.hasKey("endAt")) {
ReadableArray endAtArray = options.getArray("endAt");
query = query.endAt(Utils.recursivelyDeconstructReadableArray(endAtArray));
}
if (options.hasKey("endBefore")) {
ReadableArray endBeforeArray = options.getArray("endBefore");
query = query.endBefore(Utils.recursivelyDeconstructReadableArray(endBeforeArray));
}
if (options.hasKey("limit")) {
int limit = options.getInt("limit");
query = query.limit(limit);
}
if (options.hasKey("offset")) {
// Android doesn't support offset
}
if (options.hasKey("selectFields")) {
// Android doesn't support selectFields
}
if (options.hasKey("startAfter")) {
ReadableArray startAfterArray = options.getArray("startAfter");
query = query.startAfter(Utils.recursivelyDeconstructReadableArray(startAfterArray));
}
if (options.hasKey("startAt")) {
ReadableArray startAtArray = options.getArray("startAt");
query = query.startAt(Utils.recursivelyDeconstructReadableArray(startAtArray));
}
return query;
}
/**
* Handles documentSnapshot events.
*
* @param listenerId
* @param querySnapshot
*/
private void handleQuerySnapshotEvent(String listenerId, QuerySnapshot querySnapshot) {
WritableMap event = Arguments.createMap();
WritableMap data = FirestoreSerialize.snapshotToWritableMap(querySnapshot);
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("querySnapshot", data);
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
}
/**
* Handles a documentSnapshot error event
*
* @param listenerId
* @param exception
*/
private void handleQuerySnapshotError(String listenerId, FirebaseFirestoreException exception) {
WritableMap event = Arguments.createMap();
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("error", RNFirebaseFirestore.getJSError(exception));
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
}
}

View File

@@ -0,0 +1,190 @@
package io.invertase.firebase.firestore;
import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.ListenerRegistration;
import com.google.firebase.firestore.SetOptions;
import java.util.HashMap;
import java.util.Map;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreDocumentReference {
private static final String TAG = "RNFBFSDocumentReference";
private static Map<String, ListenerRegistration> documentSnapshotListeners = new HashMap<>();
private final String appName;
private final String path;
private ReactContext reactContext;
private final DocumentReference ref;
RNFirebaseFirestoreDocumentReference(ReactContext reactContext, String appName, String path) {
this.appName = appName;
this.path = path;
this.reactContext = reactContext;
this.ref = RNFirebaseFirestore.getFirestoreForApp(appName).document(path);
}
public void collections(Promise promise) {
// Not supported on Android
}
public void create(ReadableMap data, Promise promise) {
// Not supported on Android out of the box
}
public void delete(final Promise promise) {
this.ref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "delete:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "delete:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
}
});
}
void get(final Promise promise) {
this.ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult());
promise.resolve(data);
} else {
Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
}
});
}
public static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId) {
if (!documentSnapshotListeners.containsKey(listenerId)) {
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
@Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException exception) {
if (exception == null) {
handleDocumentSnapshotEvent(listenerId, documentSnapshot);
} else {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
handleDocumentSnapshotError(listenerId, exception);
}
}
};
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(listener);
documentSnapshotListeners.put(listenerId, listenerRegistration);
}
}
public void set(final ReadableMap data, final ReadableMap options, final Promise promise) {
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(data);
Task<Void> task;
SetOptions setOptions = null;
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
task = this.ref.set(map, SetOptions.merge());
} else {
task = this.ref.set(map);
}
task.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "set:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "set:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
}
});
}
public void update(final ReadableMap data, final Promise promise) {
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(data);
this.ref.update(map).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "update:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "update:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
}
});
}
/*
* INTERNALS/UTILS
*/
boolean hasListeners() {
return !documentSnapshotListeners.isEmpty();
}
/**
* Handles documentSnapshot events.
*
* @param listenerId
* @param documentSnapshot
*/
private void handleDocumentSnapshotEvent(String listenerId, DocumentSnapshot documentSnapshot) {
WritableMap event = Arguments.createMap();
WritableMap data = FirestoreSerialize.snapshotToWritableMap(documentSnapshot);
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("documentSnapshot", data);
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
}
/**
* Handles a documentSnapshot error event
*
* @param listenerId
* @param exception
*/
private void handleDocumentSnapshotError(String listenerId, FirebaseFirestoreException exception) {
WritableMap event = Arguments.createMap();
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("error", RNFirebaseFirestore.getJSError(exception));
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
}
}

View File

@@ -0,0 +1,39 @@
package io.invertase.firebase.firestore;
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.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("unused")
public class RNFirebaseFirestorePackage implements ReactPackage {
public RNFirebaseFirestorePackage() {
}
/**
* @param reactContext react application context that can be used to create modules
* @return list of native modules to register with the newly created catalyst instance
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new RNFirebaseFirestore(reactContext));
return modules;
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -28,18 +28,6 @@ public class RNFirebaseMessagingPackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -28,18 +28,6 @@ public class RNFirebasePerformancePackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatability
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -33,18 +33,6 @@ public class RNFirebaseStoragePackage implements ReactPackage {
return modules;
}
/**
* @return list of JS modules to register with the newly created catalyst instance.
* <p/>
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
// TODO: Removed in 0.47.0. Here for backwards compatibility
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}

View File

@@ -1,8 +1,3 @@
---
!> These docs are for the [v3 pre-release](https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0) version, for the currently published release version see the [v2 docs](/v2/)
---
<div style="text-align: center;">
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg?style=flat-square)](https://www.npmjs.com/package/react-native-firebase)
@@ -11,6 +6,7 @@
[![License](https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square)](/LICENSE)
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
[![Twitter Follow](https://img.shields.io/twitter/follow/rnfirebase.svg?style=social&label=Follow)](https://twitter.com/rnfirebase)
</div>
---
@@ -46,6 +42,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ❌ | ❌ | ❌ |
| **Firestore** | ❌ | ❌ | ✅ | ❌ |
| **Invites** | ❌ | ❌ | ❌ | ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
@@ -57,15 +54,10 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
---
### Supported versions - React Native / Firebase
> The table below shows the supported version of `react-native-firebase` for different React Native versions
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
| | v0.36 - v0.39 | v0.40 - v0.46 | v0.47 +
| ------------------------------- | :---: | :---: | :---: |
| react-native-firebase | 1.X.X | 2.X.X | 2.1.X |
> The table below shows the minimum supported versions of the Firebase SDKs for each version of `react-native-firebase`
| | v1 | v2 | v3 |
| ---------------------- | :---: | :---: | :---: |
| Firebase Android SDK | 10.2.0+ | 11.0.0 + | 11.2.0 + |
| Firebase iOS SDK | 3.15.0+ | 4.0.0 + | 4.0.0 + |
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X |
|------------------------|-------------|-------------|-----------------|----------|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + |
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + |
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + |

View File

@@ -10,8 +10,9 @@
---
- Core
- [App](/core/app)
- [Configure default app instance](/core/config-default-app)
- [Apps](/core/app)
- [Default App](/core/default-app)
- [Dynamically Initialize Apps](/core/initialize-apps)
- [Firebase](/core/firebase)
- [Configure RNFirebase](/core/config-rnfirebase)
@@ -24,6 +25,7 @@
- [Cloud Messaging](/modules/cloud-messaging)
- [Crash Reporting](/modules/crash)
- [Database](/modules/database)
- [Firestore (Beta)](/modules/firestore)
- [Remote Config](/modules/config)
- [Storage](/modules/storage)
- [Transactions](/modules/transactions)

View File

@@ -1,21 +1,21 @@
# App - firebase.app(): FirebaseApp
## Apps
RNFirebase supports both initializing apps natively and also via js code over the RN bridge.
?> For the **default** app see: [Default App](/core/default-app)
Apps initialized natively are available immediately at app runtime, there is no need to call `initializeApp` for them.
?> For initializing **additional apps** natively and also via js code over the RN bridge see: [Dynamically Initializing Apps](/core/initialize-apps)
For example, to access the default app initialized via the `Google-Services` `plist` or `json` file:
### Reading App Options
!> `<app>.options.clientId` is not available on the **Android Firebase SDK** so will return null for Android, see the issue: [firebase/firebase-ios-sdk#140 (comment)](https://github.com/firebase/firebase-ios-sdk/issues/140#issuecomment-315953708)
Just like the Firebase web sdk you can view options used to initialize an app instance in the same way, for example:
```js
import firebase from 'react-native-firebase';
const defaultApp = firebase.app();
defaultApp.database().ref('foobar').once('value', (snapshot) => {
// snapshot from default app
});
// get the default app name/options that were initialized natively
// get the default app name/options that the app initialized with
console.log("name", defaultApp.name);
console.log("apiKey", defaultApp.options.apiKey);
console.log("applicationId", defaultApp.options.applicationId);
@@ -26,9 +26,3 @@ console.log("storageBucket", defaultApp.options.projectId);
```
<!-- TODO api ref docs: -->
<!-- - name: String -->
<!-- - options: Object -->
<!-- - delete(): Promise -->

View File

@@ -1,4 +0,0 @@
<!-- TODO -->
### Enable Database Persistence

View File

@@ -1,5 +1,8 @@
<!-- TODO -->
### Log Options
TODO - PR's welcome
### Debug Options
TODO - PR's welcome

27
docs/core/default-app.md Normal file
View File

@@ -0,0 +1,27 @@
### Default Firebase App
After following the iOS & Android install guides and correctly setting up your google services plist/json files; the default app is automatically initialized and available for use in react-native-firebase.
There's no need to call `initializeApp(opt)` in your JS code for the default app, import RNFirebase and use the default app straight away:
```javascript
import firebase from 'react-native-firebase';
console.log(firebase.database().app.name); // '[DEFAULT]'
```
!> Calling `initializeApp()` for the default app will throw an 'app already initialized' error in a later release.
### Enable Database Persistence
Enabling database persistence (setPersistence) via JS for the default app is no longer supported. This breaking change was added in v3 to prevent several race condition issues around.
You can still however easily enable this natively for the default app instance:
#### Android
Add `FirebaseDatabase.getInstance().setPersistenceEnabled(true);` inside your `MainActivity.java` files `onCreate()` method.
#### iOS
Add `[FIRDatabase database].persistenceEnabled = YES;` after the `[FIRApp configure];` line inside your `AppDelegate.m` files `didFinishLaunchingWithOptions` method.

View File

@@ -1,9 +1,10 @@
# Firebase
<!-- TODO api ref docs -->
<!-- TODO - apps(): Array<FirebaseApp> -->
<!-- TODO - app(): FirebaseApp -->
<!-- TODO - initializeApp(): FirebaseApp -->
<!-- TODO - setLogLevel() -->
<!-- TODO - SDK_VERSION: String -->
<!-- TODO - googleApiAvailability: Object -->
TODO Reference Docs:
- apps(): Array<FirebaseApp>
- app(): FirebaseApp
- initializeApp(): FirebaseApp
- setLogLevel()
- SDK_VERSION: String
- googleApiAvailability: Object

View File

@@ -0,0 +1,93 @@
## Initializing Apps
!> The **default** firebase app instance can **not** be initialized via JS, please setup your google services plist/json files in your android studio / xcode projects. See [Default App](/core/default-app) for more information.
App initialization in RNFirebase is for the most part the same as the web sdk, with only a few minor differences.
### Supported Modules
Only 4 modules on the official firebase native SDK's support multiple apps, they are as follows:
- Authentication
- Database
- Firestore
- Storage
### Initialize via JavaScript
#### Cross Platform Example
```javascript
import { Platform } from 'react-native';
import firebase from 'react-native-firebase';
// pluck values from your `GoogleService-Info.plist` you created on the firebase console
const iosConfig = {
clientId: 'x',
appId: 'x',
apiKey: 'x',
databaseURL: 'x',
storageBucket: 'x',
messagingSenderId: 'x',
projectId: 'x',
// enable persistence by adding the below flag
persistence: true,
};
// pluck values from your `google-services.json` file you created on the firebase console
const androidConfig = {
clientId: 'x',
appId: 'x',
apiKey: 'x',
databaseURL: 'x',
storageBucket: 'x',
messagingSenderId: 'x',
projectId: 'x',
// enable persistence by adding the below flag
persistence: true,
};
const kittensApp = firebase.initializeApp(
// use platform specific firebase config
Platform.OS === 'ios' ? iosConfig : androidConfig,
// name of this app
'kittens',
);
// dynamically created apps aren't available immediately due to the
// asynchronous nature of react native bridging, therefore you must
// wait for an `onReady` state before calling any modules/methods
// otherwise you will most likely run into `app not initialized` exceptions
kittensApp.onReady().then((app) => {
// --- ready ---
// use `app` arg, kittensApp var or `app('kittens')` to access modules
// and their methods. e.g:
firebase.app('kittens').auth().signInAnonymously().then((user) => {
console.log('kittensApp user ->', user.toJSON());
});
});
```
### Initialize via Android/iOS native code
If you're familiar with native code you can create apps natively also (or if you are already initializing additional apps natively on app boot) - these apps automatically become available for use inside RNFirebase.
For example, if you created an app natively called `dogs` then the following would work:
```javascript
import firebase from 'react-native-firebase';
const dogsApp = firebase.app('dogs');
dogsApp.auth().signInAnonymously().then((user) => {
console.log('dogsApp user ->', user.toJSON());
});
```
### Deleting an app instance
Currently it's not possible to provide cross platform 'delete app' functionality as the Firebase Android SDK is missing the app delete method, this has been flagged with firebase ([firebase/firebase-ios-sdk#140 (comment)](https://github.com/firebase/firebase-ios-sdk/issues/140#issuecomment-315953708)).

View File

@@ -45,11 +45,11 @@
],
}
</script>
<script>
if (typeof navigator.serviceWorker !== 'undefined') {
navigator.serviceWorker.register('sw.js')
}
</script>
<!--<script>-->
<!--if (typeof navigator.serviceWorker !== 'undefined') {-->
<!--navigator.serviceWorker.register('sw.js')-->
<!--}-->
<!--</script>-->
<link rel="stylesheet" href="//unpkg.com/docsify/themes/vue.css">
<style>
.markdown-section pre>code {

View File

@@ -12,7 +12,7 @@ buildscript {
// ...
dependencies {
// ...
classpath 'com.google.gms:google-services:3.0.0'
classpath 'com.google.gms:google-services:3.1.1'
}
}
```
@@ -42,31 +42,40 @@ dependencies {
compile(project(':react-native-firebase')) {
transitive = false
}
compile "com.google.firebase:firebase-core:11.2.0"
compile "com.google.firebase:firebase-core:11.4.2"
// If you are receiving Google Play API availability issues, add the following dependency
compile "com.google.android.gms:play-services-base:11.2.0"
compile "com.google.android.gms:play-services-base:11.4.2"
// RNFirebase optional dependencies
compile "com.google.firebase:firebase-ads:11.2.0"
compile "com.google.firebase:firebase-auth:11.2.0"
compile "com.google.firebase:firebase-config:11.2.0"
compile "com.google.firebase:firebase-crash:11.2.0"
compile "com.google.firebase:firebase-database:11.2.0"
compile "com.google.firebase:firebase-messaging:11.2.0"
compile "com.google.firebase:firebase-perf:11.2.0"
compile "com.google.firebase:firebase-storage:11.2.0"
compile "com.google.firebase:firebase-ads:11.4.2"
compile "com.google.firebase:firebase-auth:11.4.2"
compile "com.google.firebase:firebase-config:11.4.2"
compile "com.google.firebase:firebase-crash:11.4.2"
compile "com.google.firebase:firebase-database:11.4.2"
compile "com.google.firebase:firebase-firestore:11.4.2"
compile "com.google.firebase:firebase-messaging:11.4.2"
compile "com.google.firebase:firebase-perf:11.4.2"
compile "com.google.firebase:firebase-storage:11.4.2"
}
```
Google Play services from 11.2.0 onwards require their dependencies to be downloaded from Google's Maven respository so add the
Google Play services from 11.4.2 onwards require their dependencies to be downloaded from Google's Maven respository so add the
required reference to the repositories section of the *project* level build.gradle
`android/build.gradle`
```groovy
allprojects {
repositories {
// ...
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
// -------------------------------------------------
// Add this below the existing maven property above
// -------------------------------------------------
maven {
url 'https://maven.google.com'
}
@@ -88,6 +97,7 @@ import io.invertase.firebase.auth.RNFirebaseAuthPackage; // Firebase Auth
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage; // Firebase Remote Config
import io.invertase.firebase.crash.RNFirebaseCrashPackage; // Firebase Crash Reporting
import io.invertase.firebase.database.RNFirebaseDatabasePackage; // Firebase Realtime Database
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage; // Firebase Firestore
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; // Firebase Cloud Messaging
import io.invertase.firebase.perf.RNFirebasePerformancePackage; // Firebase Performance
import io.invertase.firebase.storage.RNFirebaseStoragePackage; // Firebase Storage
@@ -107,6 +117,7 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseRemoteConfigPackage(),
new RNFirebaseCrashPackage(),
new RNFirebaseDatabasePackage(),
new RNFirebaseFirestorePackage(),
new RNFirebaseMessagingPackage(),
new RNFirebasePerformancePackage(),
new RNFirebaseStoragePackage()
@@ -193,6 +204,6 @@ In the same file, add the `firebase-perf` module to your dependencies:
```groovy
dependencies {
// ...
compile "com.google.firebase:firebase-perf:11.2.0"
compile "com.google.firebase:firebase-perf:11.4.2"
}
```

View File

@@ -69,6 +69,7 @@ pod 'Firebase/Auth'
pod 'Firebase/Crash'
pod 'Firebase/Database'
pod 'Firebase/DynamicLinks'
pod 'Firebase/Firestore'
pod 'Firebase/Messaging'
pod 'Firebase/RemoteConfig'
pod 'Firebase/Storage'

View File

@@ -2,7 +2,98 @@
## From v2 to v3
<!-- TODO -->
The below is a quick summary of steps to take when migrating from v2 to v3 of RNFirebase. Please see the [v3 change log](https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0) for detailed changes.
** Please note, we're now using `Apache License 2.0` to license this library. **
##### 1) Install the latest version of RNFirebase:
> `npm i react-native-firebase@latest --save`
##### 2) Upgrade react-native version (only if you're currently lower than v0.48):
- Follow the instructions [here](https://facebook.github.io/react-native/docs/upgrading.html)
##### 3) Update your code to reflect deprecations/breaking changes if needed:
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] enabling database persistence (setPersistence) via JS is no longer supported - this is to prevent several race conditions. See sub points on how to enable these natively.
- [android] add `FirebaseDatabase.getInstance().setPersistenceEnabled(true);` to your `MainActivity` `onCreate` method.
- [ios] add `[FIRDatabase database].persistenceEnabled = YES;` after the `[FIRApp configure];` line inside your `AppDelegate` `didFinishLaunchingWithOptions` method.
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [app] `new RNFirebase()` is no longer supported. See below for information about app initialisation.
- ![#f03c15](https://placehold.it/15/fdfd96/000000?text=+) **[deprecated]** [app] `initializeApp()` for apps that are already initialised natively (i.e. the default app initialised via google-services plist/json) will now log a deprecation warning.
- As these apps are already initialised natively there's no need to call `initializeApp` in your JS code. For now, calling it will just return the app that's already internally initialised - in a future version this will throw an `already initialized` exception.
- Accessing apps can now be done the same way as the web sdk, simply call `firebase.app()` to get the default app, or with the name of specific app as the first arg, e.g. `const meow = firebase.app('catsApp');` to get a specific app.
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [auth] Third party providers now user `providerId` rather than `provider` as per the Web SDK. If you are manually creating your credentials, you will need to update the field name.
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] Error messages and codes internally re-written to match the web sdk
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] `ref.isEqual` now checks the query modifiers as well as the ref path (was just path before). With the release of multi apps/core support this check now also includes whether the refs are for the same app.
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] on/off behaviour changes. Previous `off` behaviour was incorrect. A `SyncTree/Repo` implementation was added to provide the correct behaviour you'd expect in the web sdk. Whilst this is a breaking change it shouldn't be much of an issue if you've previously setup your on/off handling correctly. See #160 for specifics of this change.
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [storage] UploadTaskSnapshot -> `downloadUrl` renamed to `downloadURL` to match web sdk
##### 4) Android - Update `android/build.gradle`:
- Check you are using google-services 3.1.0 or greater:
- You must add `maven { url 'https://maven.google.com' }` to your `android/build.gradle` as follows:
```groovy
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.google.gms:google-services:3.1.0' // CHECK VERSION HERE
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
// -------------------------------------------------
// Add this below the existing maven property above
// -------------------------------------------------
maven {
url 'https://maven.google.com'
}
}
}
```
##### 5) Android - Update `app/build.gradle`:
- You must update all your Firebase & play services dependencies to 11.4.2.
##### 6) iOS - Update podfile:
- You need to check that you're running at least version 4.3.0 of the Firebase Pods
- Run `pod outdated`
- Run `pod update`
Add the `Firebase/Firestore` if you plan on using firestore.
## From v1 to v2

View File

@@ -1,6 +1,6 @@
# AdMob
The admob allows you to display adverts in your app, using your account from [AdMob by Google](https://www.google.co.uk/admob/). RNFirebase allows you to display Banners, Interstitials, NativeExpress Ads & Rewarded Videos.
The admob module allows you to display adverts in your app, using your account from [AdMob by Google](https://www.google.co.uk/admob/). RNFirebase allows you to display Banners, Interstitials, NativeExpress Ads & Rewarded Videos.
## Initialize

190
docs/modules/firestore.md Normal file
View File

@@ -0,0 +1,190 @@
# Firestore (Beta)
RNFirebase mimics the [Firestore Web SDK](https://firebase.google.com/docs/reference/js/firebase.firestore) by providing a bridge to the native Android and iOS Firestore SDK's.
All Firestore operations are accessed via `firebase.firestore()`. For detailed documentation of the supported methods demonstrated below please use the official firestore web sdk guides.
?> Please note that Persistence (offline support) is enabled by default with Firestore on iOS and Android.
## Add and Manage Data
### Collections
Read information about a collection example:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.collection('posts')
.get()
.then(querySnapshot => {
// Access all the documents in the collection
const docs = querySnapshot.docs;
// Access the list of document changes for the collection
const changes = querySnapshot.docChanges;
// Loop through the documents
querySnapshot.forEach((doc) => {
const value = doc.data();
})
})
```
Add to a collection example (generated ID):
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.collection('posts')
.add({
title: 'Amazing post',
})
.then(() => {
// Document added to collection and ID generated
// Will have path: `posts/{generatedId}`
})
```
Add to a collection example (manual ID):
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.collection('posts')
.doc('post1')
.set({
title: 'My awesome post',
content: 'Some awesome content',
})
.then(() => {
// Document added to collection with path: `posts/post1`
})
```
### Documents
There are multiple ways to read a document. The following are equivalent examples:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.doc('posts/posts1')
.get((documentSnapshot) => {
const value = documentSnapshot.data();
});
firebase.firestore()
.collection('posts')
.doc('posts1')
.get((documentSnapshot) => {
const value = documentSnapshot.data();
});
```
Create a document example:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.doc('posts/posts1')
.set({
title: 'My awesome post',
content: 'Some awesome content',
})
.then(() => {
// Document created
});
```
Updating a document example:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.doc('posts/posts1')
.update({
title: 'My awesome post',
})
.then(() => {
// Document created
});
```
Deleting a document example:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.doc('posts/posts1')
.delete()
.then(() => {
// Document deleted
});
```
### Batching document updates
Writes, updates and deletes to documents can be batched and committed atomically as follows:
```javascript
import firebase from 'react-native-firebase';
const ayRef = firebase.firestore().doc('places/AY');
const lRef = firebase.firestore().doc('places/LON');
const nycRef = firebase.firestore().doc('places/NYC');
const sfRef = firebase.firestore().doc('places/SF');
firebase.firestore()
.batch()
.set(ayRef, { name: 'Aylesbury' })
.set(lRef, { name: 'London' })
.set(nycRef, { name: 'New York City' })
.set(sfRef, { name: 'San Francisco' })
.update(nycRef, { population: 1000000 })
.update(sfRef, { name: 'San Fran' })
.set(lRef, { population: 3000000 }, { merge: true })
.delete(ayRef)
.commit()
.then(() => {
// Would end up with three documents in the collection: London, New York City and San Francisco
});
```
### Transactions
Coming soon
## Realtime Updates
### Collections
Listen to collection updates example:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.collection('cities')
.where('state', '==', 'CA')
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
// DocumentSnapshot available
})
})
```
The snapshot handler will receive a new query snapshot every time the query results change (that is, when a document is added, removed, or modified).
### Documents
Listen to document updates example:
```javascript
import firebase from 'react-native-firebase';
firebase.firestore()
.doc('posts/post1')
.onSnapshot((documentSnapshot) => {
// DocumentSnapshot available
})
```
The snapshot handler will receive the current contents of the document, and any subsequent changes to the document.

View File

@@ -1,7 +1,5 @@
# Performance Monitoring
!> Performance monitoring requires react-native-firebase version 1.2.0.
?> Android: If you plan on using this module in your own application, please ensure the optional setup instructions for
[Android](http://invertase.io/react-native-firebase/#/installation-android?id=_4-performance-monitoring-optional) have been followed.

View File

@@ -3,6 +3,126 @@
Although RNFirebase usage requires a React Native environment, it isn't tightly coupled which allows for full flexibility
when it comes to integrating with other modules such a [`react-redux`](https://github.com/reactjs/react-redux).
## Standalone Integration
Although the following example works for a basic redux setup, it may differ when integrating with other redux middleware.
Imagine a simple TODO app, with redux we're able to abstract the Firebase logic out of components which allows for greater
testability and maintainability.
?> We use [`redux-thunk`](https://github.com/gaearon/redux-thunk) to provide async actions.
### Action Creators
```js
// Actions
export const subscribe = () => {
return (dispatch) => {
firebase.database().ref('todos').on('value', (snapshot) => {
const todos = [];
snapshot.forEach((childSnapshot) => {
todos.push({
id: childSnapshot.key,
...(childSnapshot.val()),
})
})
dispatch({
type: 'TODO_UPDATE',
todos,
})
})
}
}
// Methods
export const addTodo = text => {
firebase.database().ref('todos').push({
text,
visible: true,
})
}
export const completeTodo = id => {
firebase.database().ref(`todos/${id}`).update({
visible: false,
})
}
```
Instead of creating multiple actions which the reducers handle, we instead subscribe to the database ref and on any changes,
send a single action for the reducers to handle with the data which is constantly updating.
### Reducers
Our reducer now becomes really simple, as we're able to simply update the reducers state with whatever data has been returned
from our Firebase subscription.
```js
const todos = (state = {}, action) => {
switch (action.type) {
case 'TODO_UPDATE':
return { ...action.todos };
}
return state;
}
export default todos;
```
### Component
We can now easily subscribe to the todos in redux state and get live updates when Firebase updates.
```js
import React from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import { subscribe, addTodo, completeTodo } from '../actions/TodoActions.js';
...
class Todos extends React.Component {
componentDidMount() {
this.props.dispatch(
subscribe()
);
}
onComplete = (id) => {
this.props.dispatch(
completeTodo(id)
);
};
onAdd = (text) => {
this.props.dispatch(
addTodo(text)
);
};
render() {
return (
<FlatList
data={this.props.todos}
...
/>
);
}
}
function mapStateToProps(state) {
return {
todos: state.todos,
};
}
export default connect(mapStateToProps)(Todos);
```
## React Redux Firebase
[`react-redux-firebase`](http://docs.react-redux-firebase.com/history/v2.0.0) provides simplified and standardized common redux/firebase logic.
@@ -212,121 +332,3 @@ For more details, please visit [`react-redux-firebase`'s react-native section](h
);
```
## Standalone Integration
Although the following example works for a basic redux setup, it may differ when integrating with other redux middleware.
Imagine a simple TODO app, with redux we're able to abstract the Firebase logic out of components which allows for greater
testability and maintainability.
?> We use [`redux-thunk`](https://github.com/gaearon/redux-thunk) to provide async actions.
### Action Creators
```js
// Actions
export const subscribe = () => {
return (dispatch) => {
firebase.database().ref('todos').on('value', (snapshot) => {
const todos = [];
snapshot.forEach((childSnapshot) => {
todos.push({
id: childSnapshot.key,
...(childSnapshot.val()),
})
})
dispatch({
type: 'TODO_UPDATE',
todos,
})
})
}
}
// Methods
export const addTodo = text => {
firebase.database().ref('todos').push({
text,
visible: true,
})
}
export const completeTodo = id => {
firebase.database().ref(`todos/${id}`).update({
visible: false,
})
}
```
Instead of creating multiple actions which the reducers handle, we instead subscribe to the database ref and on any changes,
send a single action for the reducers to handle with the data which is constantly updating.
### Reducers
Our reducer now becomes really simple, as we're able to simply update the reducers state with whatever data has been returned
from our Firebase subscription.
```js
const todos = (state = {}, action) => {
switch (action.type) {
case 'TODO_UPDATE':
return { ...action.todos };
}
return state;
}
export default todos;
```
### Component
We can now easily subscribe to the todos in redux state and get live updates when Firebase updates.
```js
import React from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import { subscribe, addTodo, completeTodo } from '../actions/TodoActions.js';
...
class Todos extends React.Component {
componentDidMount() {
this.props.dispatch(
subscribe()
);
}
onComplete = (id) => {
this.props.dispatch(
completeTodo(id)
);
};
onAdd = (text) => {
this.props.dispatch(
addTodo(text)
);
};
render() {
return (
<FlatList
data={this.props.todos}
...
/>
);
}
}
function mapStateToProps(state) {
return {
todos: state.todos,
};
}
export default connect(mapStateToProps)(Todos);
```

View File

@@ -1,3 +1,5 @@
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Viewing old RNFirebase version ![#f03c15](https://placehold.it/15/f03c15/000000?text=+)
- Getting started
- [Initial setup](/v2/initial-setup)
- [Installation - iOS](/v2/installation-ios)

View File

@@ -13,6 +13,9 @@
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; };
8323CF081F6FBD870071420B /* RNFirebaseAdMobBannerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */; };
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */; };
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */; };
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */; };
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */; };
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; };
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; };
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; };
@@ -53,6 +56,12 @@
8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobBannerManager.m; sourceTree = "<group>"; };
8323CF041F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobNativeExpressManager.h; sourceTree = "<group>"; };
8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobNativeExpressManager.m; sourceTree = "<group>"; };
8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreDocumentReference.m; sourceTree = "<group>"; };
8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestore.h; sourceTree = "<group>"; };
8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestore.m; sourceTree = "<group>"; };
8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreCollectionReference.m; sourceTree = "<group>"; };
8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreDocumentReference.h; sourceTree = "<group>"; };
8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreCollectionReference.h; sourceTree = "<group>"; };
839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = "<group>"; };
839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = "<group>"; };
839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
@@ -119,6 +128,7 @@
839D915A1EF3E20A0077C7C8 /* config */,
839D915D1EF3E20A0077C7C8 /* crash */,
839D91601EF3E20A0077C7C8 /* database */,
8376F70D1F7C141500D45A85 /* firestore */,
839D91631EF3E20A0077C7C8 /* messaging */,
839D91661EF3E20A0077C7C8 /* perf */,
134814211AA4EA7D00B7C361 /* Products */,
@@ -129,6 +139,20 @@
);
sourceTree = "<group>";
};
8376F70D1F7C141500D45A85 /* firestore */ = {
isa = PBXGroup;
children = (
8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */,
8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */,
8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */,
8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */,
8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */,
8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */,
);
name = firestore;
path = RNFirebase/firestore;
sourceTree = "<group>";
};
839D914D1EF3E20A0077C7C8 /* admob */ = {
isa = PBXGroup;
children = (
@@ -292,9 +316,12 @@
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */,
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */,
839D91761EF3E20B0077C7C8 /* RNFirebaseStorage.m in Sources */,
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */,
839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */,
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */,
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */,
839D916F1EF3E20B0077C7C8 /* RNFirebaseAnalytics.m in Sources */,
839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */,
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */,

View File

@@ -79,7 +79,7 @@ RCT_EXPORT_METHOD(deleteApp:
/**
* React native constant exports - exports native firebase apps mainly
* @return
* @return NSDictionary
*/
- (NSDictionary *)constantsToExport {
NSMutableDictionary *constants = [NSMutableDictionary new];

View File

@@ -5,6 +5,7 @@
static NSString *const AUTH_CHANGED_EVENT = @"auth_state_changed";
static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed";
static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_changed";
// Database
static NSString *const DATABASE_SYNC_EVENT = @"database_sync_event";
@@ -16,6 +17,10 @@ static NSString *const DATABASE_CHILD_MODIFIED_EVENT = @"child_changed";
static NSString *const DATABASE_CHILD_REMOVED_EVENT = @"child_removed";
static NSString *const DATABASE_CHILD_MOVED_EVENT = @"child_moved";
// Firestore
static NSString *const FIRESTORE_COLLECTION_SYNC_EVENT = @"firestore_collection_sync_event";
static NSString *const FIRESTORE_DOCUMENT_SYNC_EVENT = @"firestore_document_sync_event";
// Storage
static NSString *const STORAGE_EVENT = @"storage_event";
static NSString *const STORAGE_ERROR = @"storage_error";

View File

@@ -558,6 +558,9 @@ RCT_EXPORT_METHOD(checkActionCode:
case FIRActionCodeOperationUnknown:
actionType = @"UNKNOWN";
break;
case FIRActionCodeOperationRecoverEmail:
actionType = @"RECOVER_EMAIL";
break;
}
NSDictionary *result = @{@"data": @{@"email": [info dataForKey:FIRActionCodeEmailKey], @"fromEmail": [info dataForKey:FIRActionCodeFromEmailKey],}, @"actionType": actionType,};
@@ -654,7 +657,7 @@ RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
@@ -667,6 +670,41 @@ RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
}];
}
/**
verifyPhoneNumber
@param string phoneNumber
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
phoneNumber:(NSString *) phoneNumber
requestKey:(NSString *) requestKey) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
NSDictionary * jsError = [self getJSError:(error)];
NSMutableDictionary * props = [@{
@"type": @"onVerificationFailed",
@"requestKey":requestKey,
@"state": @{@"error": jsError},
} mutableCopy];
[self sendJSEventWithAppName:appName title:PHONE_AUTH_STATE_CHANGED_EVENT props: props];
} else {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:verificationID forKey:@"authVerificationID"];
NSMutableDictionary * props = [@{
@"type": @"onCodeSent",
@"requestKey":requestKey,
@"state": @{@"verificationId": verificationID},
} mutableCopy];
[self sendJSEventWithAppName:appName title:PHONE_AUTH_STATE_CHANGED_EVENT props: props];
}
}];
}
RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appName
verificationCode:(NSString *) verificationCode
resolver:(RCTPromiseResolveBlock) resolve
@@ -843,9 +881,9 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
/**
getCredentialForProvider
@param provider
@param authToken
@param authTokenSecret
@param provider string
@param authToken string
@param authTokenSecret string
@return FIRAuthCredential
*/
- (FIRAuthCredential *)getCredentialForProvider:(NSString *)provider token:(NSString *)authToken secret:(NSString *)authTokenSecret {
@@ -893,9 +931,20 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
@param error NSError
*/
- (void)promiseRejectAuthException:(RCTPromiseRejectBlock)reject error:(NSError *)error {
NSDictionary * jsError = [self getJSError:(error)];
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
}
/**
Reject a promise with an auth exception
@param error NSError
*/
- (NSDictionary *)getJSError:(NSError *)error {
NSString *code = @"auth/unknown";
NSString *message = [error localizedDescription];
NSString *nativeErrorMessage = [error localizedDescription];
switch (error.code) {
case FIRAuthErrorCodeInvalidCustomToken:
code = @"auth/invalid-custom-token";
@@ -969,7 +1018,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
code = @"auth/internal-error";
message = @"An internal error has occurred, please try again.";
break;
// unsure of the below codes so leaving them as the default error message
case FIRAuthErrorCodeTooManyRequests:
code = @"auth/too-many-requests";
@@ -1004,10 +1053,15 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
default:
break;
}
reject(code, message, error);
return @{
@"code": code,
@"message": message,
@"nativeErrorMessage": nativeErrorMessage,
};
}
/**
Resolve or reject a promise based on FIRUser value existance
@@ -1109,7 +1163,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
}
- (NSArray<NSString *> *)supportedEvents {
return @[AUTH_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT];
return @[AUTH_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT, PHONE_AUTH_STATE_CHANGED_EVENT];
}
@end

View File

@@ -7,15 +7,15 @@
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(log:(NSString *)message) {
FIRCrashLog(message);
FIRCrashLog(@"%@", message);
}
RCT_EXPORT_METHOD(logcat:(nonnull NSNumber *) level tag:(NSString *) tag message:(NSString *) message) {
FIRCrashLog(message);
FIRCrashLog(@"%@", message);
}
RCT_EXPORT_METHOD(report:(NSString *) message) {
FIRCrashLog(message);
FIRCrashLog(@"%@", message);
assert(NO);
}

View File

@@ -0,0 +1,26 @@
#ifndef RNFirebaseFirestore_h
#define RNFirebaseFirestore_h
#import <Foundation/Foundation.h>
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
#import <FirebaseFirestore/FirebaseFirestore.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RNFirebaseFirestore : RCTEventEmitter <RCTBridgeModule> {}
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName;
+ (NSDictionary *)getJSError:(NSError *)nativeError;
@end
#else
@interface RNFirebaseFirestore : NSObject
@end
#endif
#endif

View File

@@ -0,0 +1,282 @@
#import "RNFirebaseFirestore.h"
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
#import <Firebase.h>
#import "RNFirebaseEvents.h"
#import "RNFirebaseFirestoreCollectionReference.h"
#import "RNFirebaseFirestoreDocumentReference.h"
@implementation RNFirebaseFirestore
RCT_EXPORT_MODULE();
- (id)init {
self = [super init];
if (self != nil) {
}
return self;
}
RCT_EXPORT_METHOD(collectionGet:(NSString *) appName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
listenerId:(nonnull NSString *) listenerId) {
[RNFirebaseFirestoreCollectionReference offSnapshot:listenerId];
}
RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
listenerId:(nonnull NSString *) listenerId) {
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
[ref onSnapshot:listenerId];
}
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
writes:(NSArray *) writes
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appName];
FIRWriteBatch *batch = [firestore batch];
for (NSDictionary *write in writes) {
NSString *type = write[@"type"];
NSString *path = write[@"path"];
NSDictionary *data = write[@"data"];
FIRDocumentReference *ref = [firestore documentWithPath:path];
if ([type isEqualToString:@"DELETE"]) {
batch = [batch deleteDocument:ref];
} else if ([type isEqualToString:@"SET"]) {
NSDictionary *options = write[@"options"];
if (options && options[@"merge"]) {
batch = [batch setData:data forDocument:ref options:[FIRSetOptions merge]];
} else {
batch = [batch setData:data forDocument:ref];
}
} else if ([type isEqualToString:@"UPDATE"]) {
batch = [batch updateData:data forDocument:ref];
}
}
[batch commitWithCompletion:^(NSError * _Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
resolve(nil);
}
}];
}
RCT_EXPORT_METHOD(documentCollections:(NSString *) appName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentCreate:(NSString *) appName
path:(NSString *) path
data:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] create:data resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentDelete:(NSString *) appName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] delete:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentGet:(NSString *) appName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentGetAll:(NSString *) appName
documents:(NSString *) documents
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
// Not supported on iOS out of the box
}
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
path:(NSString *) path
listenerId:(nonnull NSString *) listenerId) {
[RNFirebaseFirestoreDocumentReference offSnapshot:listenerId];
}
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
path:(NSString *) path
listenerId:(nonnull NSString *) listenerId) {
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
[ref onSnapshot:listenerId];
}
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
path:(NSString *) path
data:(NSDictionary *) data
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] set:data options:options resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
path:(NSString *) path
data:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] update:data resolver:resolve rejecter:reject];
}
/*
* INTERNALS/UTILS
*/
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error {
NSDictionary *jsError = [RNFirebaseFirestore getJSError:error];
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
}
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName {
FIRApp *app = [FIRApp appNamed:appName];
return [FIRFirestore firestoreForApp:app];
}
- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options {
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:self app:appName path:path filters:filters orders:orders options:options];
}
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appName path:(NSString *)path {
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self app:appName path:path];
}
// TODO: Move to error util for use in other modules
+ (NSString *)getMessageWithService:(NSString *)message service:(NSString *)service fullCode:(NSString *)fullCode {
return [NSString stringWithFormat:@"%@: %@ (%@).", service, message, [fullCode lowercaseString]];
}
+ (NSString *)getCodeWithService:(NSString *)service code:(NSString *)code {
return [NSString stringWithFormat:@"%@/%@", [service lowercaseString], [code lowercaseString]];
}
+ (NSDictionary *)getJSError:(NSError *)nativeError {
NSMutableDictionary *errorMap = [[NSMutableDictionary alloc] init];
[errorMap setValue:@(nativeError.code) forKey:@"nativeErrorCode"];
[errorMap setValue:[nativeError localizedDescription] forKey:@"nativeErrorMessage"];
NSString *code;
NSString *message;
NSString *service = @"Firestore";
switch (nativeError.code) {
case FIRFirestoreErrorCodeOK:
code = [RNFirebaseFirestore getCodeWithService:service code:@"ok"];
message = [RNFirebaseFirestore getMessageWithService:@"Ok." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeCancelled:
code = [RNFirebaseFirestore getCodeWithService:service code:@"cancelled"];
message = [RNFirebaseFirestore getMessageWithService:@"The operation was cancelled." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeUnknown:
code = [RNFirebaseFirestore getCodeWithService:service code:@"unknown"];
message = [RNFirebaseFirestore getMessageWithService:@"Unknown error or an error from a different error domain." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeInvalidArgument:
code = [RNFirebaseFirestore getCodeWithService:service code:@"invalid-argument"];
message = [RNFirebaseFirestore getMessageWithService:@"Client specified an invalid argument." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeDeadlineExceeded:
code = [RNFirebaseFirestore getCodeWithService:service code:@"deadline-exceeded"];
message = [RNFirebaseFirestore getMessageWithService:@"Deadline expired before operation could complete." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeNotFound:
code = [RNFirebaseFirestore getCodeWithService:service code:@"not-found"];
message = [RNFirebaseFirestore getMessageWithService:@"Some requested document was not found." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeAlreadyExists:
code = [RNFirebaseFirestore getCodeWithService:service code:@"already-exists"];
message = [RNFirebaseFirestore getMessageWithService:@"Some document that we attempted to create already exists." service:service fullCode:code];
break;
case FIRFirestoreErrorCodePermissionDenied:
code = [RNFirebaseFirestore getCodeWithService:service code:@"permission-denied"];
message = [RNFirebaseFirestore getMessageWithService:@"The caller does not have permission to execute the specified operation." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeResourceExhausted:
code = [RNFirebaseFirestore getCodeWithService:service code:@"resource-exhausted"];
message = [RNFirebaseFirestore getMessageWithService:@"Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeFailedPrecondition:
code = [RNFirebaseFirestore getCodeWithService:service code:@"failed-precondition"];
message = [RNFirebaseFirestore getMessageWithService:@"Operation was rejected because the system is not in a state required for the operation`s execution." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeAborted:
code = [RNFirebaseFirestore getCodeWithService:service code:@"aborted"];
message = [RNFirebaseFirestore getMessageWithService:@"The operation was aborted, typically due to a concurrency issue like transaction aborts, etc." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeOutOfRange:
code = [RNFirebaseFirestore getCodeWithService:service code:@"out-of-range"];
message = [RNFirebaseFirestore getMessageWithService:@"Operation was attempted past the valid range." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeUnimplemented:
code = [RNFirebaseFirestore getCodeWithService:service code:@"unimplemented"];
message = [RNFirebaseFirestore getMessageWithService:@"Operation is not implemented or not supported/enabled." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeInternal:
code = [RNFirebaseFirestore getCodeWithService:service code:@"internal"];
message = [RNFirebaseFirestore getMessageWithService:@"Internal errors." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeUnavailable:
code = [RNFirebaseFirestore getCodeWithService:service code:@"unavailable"];
message = [RNFirebaseFirestore getMessageWithService:@"The service is currently unavailable." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeDataLoss:
code = [RNFirebaseFirestore getCodeWithService:service code:@"data-loss"];
message = [RNFirebaseFirestore getMessageWithService:@"Unrecoverable data loss or corruption." service:service fullCode:code];
break;
case FIRFirestoreErrorCodeUnauthenticated:
code = [RNFirebaseFirestore getCodeWithService:service code:@"unauthenticated"];
message = [RNFirebaseFirestore getMessageWithService:@"The request does not have valid authentication credentials for the operation." service:service fullCode:code];
break;
default:
code = [RNFirebaseFirestore getCodeWithService:service code:@"unknown"];
message = [RNFirebaseFirestore getMessageWithService:@"An unknown error occurred." service:service fullCode:code];
break;
}
[errorMap setValue:code forKey:@"code"];
[errorMap setValue:message forKey:@"message"];
return errorMap;
}
- (NSArray<NSString *> *)supportedEvents {
return @[FIRESTORE_COLLECTION_SYNC_EVENT, FIRESTORE_DOCUMENT_SYNC_EVENT];
}
@end
#else
@implementation RNFirebaseFirestore
@end
#endif

View File

@@ -0,0 +1,35 @@
#ifndef RNFirebaseFirestoreCollectionReference_h
#define RNFirebaseFirestoreCollectionReference_h
#import <Foundation/Foundation.h>
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
#import <FirebaseFirestore/FirebaseFirestore.h>
#import <React/RCTEventEmitter.h>
#import "RNFirebaseEvents.h"
#import "RNFirebaseFirestore.h"
#import "RNFirebaseFirestoreDocumentReference.h"
@interface RNFirebaseFirestoreCollectionReference : NSObject
@property RCTEventEmitter *emitter;
@property NSString *app;
@property NSString *path;
@property NSArray *filters;
@property NSArray *orders;
@property NSDictionary *options;
@property FIRQuery *query;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId;
- (void)onSnapshot:(NSString *)listenerId;
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot;
@end
#else
@interface RNFirebaseFirestoreCollectionReference : NSObject
@end
#endif
#endif

View File

@@ -0,0 +1,207 @@
#import "RNFirebaseFirestoreCollectionReference.h"
@implementation RNFirebaseFirestoreCollectionReference
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
static NSMutableDictionary *_listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
app:(NSString *) app
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options {
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_path = path;
_filters = filters;
_orders = orders;
_options = options;
_query = [self buildQuery];
}
// Initialise the static listeners object if required
if (!_listeners) {
_listeners = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)get:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
[_query getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
NSDictionary *data = [RNFirebaseFirestoreCollectionReference snapshotToDictionary:snapshot];
resolve(data);
}
}];
}
+ (void)offSnapshot:(NSString *) listenerId {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
}
- (void)onSnapshot:(NSString *) listenerId {
if (_listeners[listenerId] == nil) {
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
[self handleQuerySnapshotError:listenerId error:error];
} else {
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
}
};
id<FIRListenerRegistration> listener = [_query addSnapshotListener:listenerBlock];
_listeners[listenerId] = listener;
}
}
- (FIRQuery *)buildQuery {
FIRQuery *query = (FIRQuery*)[[RNFirebaseFirestore getFirestoreForApp:_app] collectionWithPath:_path];
query = [self applyFilters:query];
query = [self applyOrders:query];
query = [self applyOptions:query];
return query;
}
- (FIRQuery *)applyFilters:(FIRQuery *) query {
for (NSDictionary *filter in _filters) {
NSString *fieldPath = filter[@"fieldPath"];
NSString *operator = filter[@"operator"];
// TODO: Validate this works
id value = filter[@"value"];
if ([operator isEqualToString:@"EQUAL"]) {
query = [query queryWhereField:fieldPath isEqualTo:value];
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
query = [query queryWhereField:fieldPath isGreaterThan:value];
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
} else if ([operator isEqualToString:@"LESS_THAN"]) {
query = [query queryWhereField:fieldPath isLessThan:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
}
}
return query;
}
- (FIRQuery *)applyOrders:(FIRQuery *) query {
for (NSDictionary *order in _orders) {
NSString *direction = order[@"direction"];
NSString *fieldPath = order[@"fieldPath"];
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
}
return query;
}
- (FIRQuery *)applyOptions:(FIRQuery *) query {
if (_options[@"endAt"]) {
query = [query queryEndingAtValues:_options[@"endAt"]];
}
if (_options[@"endBefore"]) {
query = [query queryEndingBeforeValues:_options[@"endBefore"]];
}
if (_options[@"offset"]) {
// iOS doesn't support offset
}
if (_options[@"selectFields"]) {
// iOS doesn't support selectFields
}
if (_options[@"startAfter"]) {
query = [query queryStartingAfterValues:_options[@"startAfter"]];
}
if (_options[@"startAt"]) {
query = [query queryStartingAtValues:_options[@"startAt"]];
}
return query;
}
- (void)handleQuerySnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
[_emitter sendEventWithName:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
}
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
querySnapshot:(FIRQuerySnapshot *)querySnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];
[_emitter sendEventWithName:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
}
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot {
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
[snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"];
[snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"];
if (querySnapshot.metadata) {
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
[metadata setValue:@(querySnapshot.metadata.fromCache) forKey:@"fromCache"];
[metadata setValue:@(querySnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
[snapshot setValue:metadata forKey:@"metadata"];
}
return snapshot;
}
+ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *) documentChanges {
NSMutableArray *changes = [[NSMutableArray alloc] init];
for (FIRDocumentChange *change in documentChanges) {
[changes addObject:[self documentChangeToDictionary:change]];
}
return changes;
}
+ (NSDictionary *)documentChangeToDictionary:(FIRDocumentChange *)documentChange {
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
[change setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentChange.document] forKey:@"document"];
[change setValue:@(documentChange.newIndex) forKey:@"newIndex"];
[change setValue:@(documentChange.oldIndex) forKey:@"oldIndex"];
if (documentChange.type == FIRDocumentChangeTypeAdded) {
[change setValue:@"added" forKey:@"type"];
} else if (documentChange.type == FIRDocumentChangeTypeRemoved) {
[change setValue:@"removed" forKey:@"type"];
} else if (documentChange.type == FIRDocumentChangeTypeModified) {
[change setValue:@"modified" forKey:@"type"];
}
return change;
}
+ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *) documentSnapshots {
NSMutableArray *snapshots = [[NSMutableArray alloc] init];
for (FIRDocumentSnapshot *snapshot in documentSnapshots) {
[snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]];
}
return snapshots;
}
#endif
@end

View File

@@ -0,0 +1,38 @@
#ifndef RNFirebaseFirestoreDocumentReference_h
#define RNFirebaseFirestoreDocumentReference_h
#import <Foundation/Foundation.h>
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
#import <FirebaseFirestore/FirebaseFirestore.h>
#import <React/RCTEventEmitter.h>
#import "RNFirebaseEvents.h"
#import "RNFirebaseFirestore.h"
@interface RNFirebaseFirestoreDocumentReference : NSObject
@property RCTEventEmitter *emitter;
@property NSString *app;
@property NSString *path;
@property FIRDocumentReference *ref;
- (id)initWithPath:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path;
- (void)collections:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)create:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId;
- (void)onSnapshot:(NSString *)listenerId;
- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (BOOL)hasListeners;
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot;
@end
#else
@interface RNFirebaseFirestoreDocumentReference : NSObject
@end
#endif
#endif

View File

@@ -0,0 +1,160 @@
#import "RNFirebaseFirestoreDocumentReference.h"
@implementation RNFirebaseFirestoreDocumentReference
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
static NSMutableDictionary *_listeners;
- (id)initWithPath:(RCTEventEmitter *)emitter
app:(NSString *) app
path:(NSString *) path {
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_path = path;
_ref = [[RNFirebaseFirestore getFirestoreForApp:_app] documentWithPath:_path];
}
// Initialise the static listeners object if required
if (!_listeners) {
_listeners = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)collections:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
// Not supported on iOS
}
- (void)create:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
// Not supported on iOS out of the box
}
- (void)delete:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
[_ref deleteDocumentWithCompletion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
- (void)get:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
[_ref getDocumentWithCompletion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
NSDictionary *data = [RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot];
resolve(data);
}
}];
}
+ (void)offSnapshot:(NSString *) listenerId {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
}
- (void)onSnapshot:(NSString *) listenerId {
if (_listeners[listenerId] == nil) {
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
[self handleDocumentSnapshotError:listenerId error:error];
} else {
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
}
};
id<FIRListenerRegistration> listener = [_ref addSnapshotListener:listenerBlock];
_listeners[listenerId] = listener;
}
}
- (void)set:(NSDictionary *) data
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
if (options && options[@"merge"]) {
[_ref setData:data options:[FIRSetOptions merge] completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
} else {
[_ref setData:data completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
}
- (void)update:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
[_ref updateData:data completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
- (BOOL)hasListeners {
return [[_listeners allKeys] count] > 0;
}
+ (void)handleWriteResponse:(NSError *) error
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
resolve(nil);
}
}
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
[snapshot setValue:documentSnapshot.reference.path forKey:@"path"];
if (documentSnapshot.exists) {
[snapshot setValue:documentSnapshot.data forKey:@"data"];
}
if (documentSnapshot.metadata) {
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
[metadata setValue:@(documentSnapshot.metadata.fromCache) forKey:@"fromCache"];
[metadata setValue:@(documentSnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
[snapshot setValue:metadata forKey:@"metadata"];
}
return snapshot;
}
- (void)handleDocumentSnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
}
- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
}
#endif
@end

View File

@@ -4,6 +4,7 @@
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
#import "RNFirebaseEvents.h"
#import <FirebaseMessaging/FirebaseMessaging.h>
#import <FirebaseInstanceID/FIRInstanceID.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTConvert.h>
@@ -258,7 +259,7 @@ RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseR
}
RCT_EXPORT_METHOD(deleteInstanceId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) {
[[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) {
if (!error) {
resolve(nil);
} else {

View File

@@ -12,6 +12,7 @@ import RemoteConfig from './modules/config';
import Storage, { statics as StorageStatics } from './modules/storage';
import Database, { statics as DatabaseStatics } from './modules/database';
import Messaging, { statics as MessagingStatics } from './modules/messaging';
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
import Links, { statics as LinksStatics } from './modules/links';
const FirebaseCoreModule = NativeModules.RNFirebase;
@@ -33,6 +34,7 @@ export default class FirebaseApp {
this.config = this._staticsOrModuleInstance({}, RemoteConfig);
this.crash = this._staticsOrModuleInstance({}, Crash);
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
this.perf = this._staticsOrModuleInstance({}, Performance);
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
@@ -111,13 +113,15 @@ export default class FirebaseApp {
* @return {Promise}
*/
delete() {
if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME && this._nativeInitialized) {
return Promise.reject(
new Error('Unable to delete the default native firebase app instance.'),
);
}
return FirebaseCoreModule.deleteApp(this._name);
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete'));
// TODO only the ios sdk currently supports delete, add back in when android also supports it
// if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME && this._nativeInitialized) {
// return Promise.reject(
// new Error('Unable to delete the default native firebase app instance.'),
// );
// }
//
// return FirebaseCoreModule.deleteApp(this._name);
}

View File

@@ -21,6 +21,7 @@ import RemoteConfig from './modules/config';
import Storage, { statics as StorageStatics } from './modules/storage';
import Database, { statics as DatabaseStatics } from './modules/database';
import Messaging, { statics as MessagingStatics } from './modules/messaging';
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
const FirebaseCoreModule = NativeModules.RNFirebase;
@@ -48,6 +49,7 @@ class FirebaseCore {
this.config = this._appNamespaceOrStatics({}, RemoteConfig);
this.crash = this._appNamespaceOrStatics({}, Crash);
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);

View File

@@ -66,11 +66,20 @@ export default class PhoneAuthListener {
this._subscribeToEvents();
// start verification flow natively
this._auth._native.verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._timeout,
);
if (isAndroid) {
this._auth._native.verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._timeout,
);
}
if (isIOS) {
this._auth._native.verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
);
}
}
/**

View File

@@ -23,8 +23,17 @@ export default class Database extends ModuleBase {
this._native.setPersistence(this._options.persistence);
}
// todo serverTimeOffset event/listener - make ref natively and switch to events
this._serverTimeOffset = 0; // TODO ----^
// server time listener
// setTimeout used to avoid setPersistence race conditions
// todo move this and persistence to native side, create a db configure() method natively perhaps?
// todo and then native can call setPersistence and then emit offset events
setTimeout(() => {
this._serverTimeOffset = 0;
this._offsetRef = this.ref('.info/serverTimeOffset');
this._offsetRef.on('value', (snapshot) => {
this._serverTimeOffset = snapshot.val() || this._serverTimeOffset;
});
}, 1);
}
/**
@@ -32,7 +41,7 @@ export default class Database extends ModuleBase {
* @return {number}
*/
getServerTime() {
return new Date().getTime() + this._serverTimeOffset;
return new Date(Date.now() + this._serverTimeOffset);
}
/**

View File

@@ -250,10 +250,10 @@ export default class Reference extends ReferenceBase {
*/
push(value: any, onComplete?: Function): Reference | Promise {
if (value === null || value === undefined) {
return new Reference(this._database, `${this.path}/${generatePushID(this._database.serverTimeOffset)}`);
return new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
}
const newRef = new Reference(this._database, `${this.path}/${generatePushID(this._database.serverTimeOffset)}`);
const newRef = new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
const promise = newRef.set(value);
// if callback provided then internally call the set promise with value

View File

@@ -0,0 +1,105 @@
/**
* @flow
* CollectionReference representation wrapper
*/
import DocumentReference from './DocumentReference';
import Path from './Path';
import Query from './Query';
import QuerySnapshot from './QuerySnapshot';
import { firestoreAutoId } from '../../utils';
import type { Direction, Operator } from './Query';
/**
* @class CollectionReference
*/
export default class CollectionReference {
_collectionPath: Path;
_firestore: Object;
_query: Query;
constructor(firestore: Object, collectionPath: Path) {
this._collectionPath = collectionPath;
this._firestore = firestore;
this._query = new Query(firestore, collectionPath);
}
get firestore(): Object {
return this._firestore;
}
get id(): string | null {
return this._collectionPath.id;
}
get parent(): DocumentReference | null {
const parentPath = this._collectionPath.parent();
return parentPath ? new DocumentReference(this._firestore, parentPath) : null;
}
add(data: Object): Promise<DocumentReference> {
const documentRef = this.doc();
return documentRef.set(data)
.then(() => Promise.resolve(documentRef));
}
doc(documentPath?: string): DocumentReference {
const newPath = documentPath || firestoreAutoId();
const path = this._collectionPath.child(newPath);
if (!path.isDocument) {
throw new Error('Argument "documentPath" must point to a document.');
}
return new DocumentReference(this._firestore, path);
}
// From Query
endAt(fieldValues: any): Query {
return this._query.endAt(fieldValues);
}
endBefore(fieldValues: any): Query {
return this._query.endBefore(fieldValues);
}
get(): Promise<QuerySnapshot> {
return this._query.get();
}
limit(n: number): Query {
return this._query.limit(n);
}
offset(n: number): Query {
return this._query.offset(n);
}
onSnapshot(onNext: () => any, onError?: () => any): () => void {
return this._query.onSnapshot(onNext, onError);
}
orderBy(fieldPath: string, directionStr?: Direction): Query {
return this._query.orderBy(fieldPath, directionStr);
}
select(varArgs: string[]): Query {
return this._query.select(varArgs);
}
startAfter(fieldValues: any): Query {
return this._query.startAfter(fieldValues);
}
startAt(fieldValues: any): Query {
return this._query.startAt(fieldValues);
}
stream(): Stream<DocumentSnapshot> {
return this._query.stream();
}
where(fieldPath: string, opStr: Operator, value: any): Query {
return this._query.where(fieldPath, opStr, value);
}
}

View File

@@ -0,0 +1,46 @@
/**
* @flow
* DocumentChange representation wrapper
*/
import DocumentSnapshot from './DocumentSnapshot';
export type DocumentChangeNativeData = {
document: DocumentSnapshot,
newIndex: number,
oldIndex: number,
type: string,
}
/**
* @class DocumentChange
*/
export default class DocumentChange {
_document: DocumentSnapshot;
_newIndex: number;
_oldIndex: number;
_type: string;
constructor(nativeData: DocumentChangeNativeData) {
this._document = nativeData.document;
this._newIndex = nativeData.newIndex;
this._oldIndex = nativeData.oldIndex;
this._type = nativeData.type;
}
get doc(): DocumentSnapshot {
return this._document;
}
get newIndex(): number {
return this._newIndex;
}
get oldIndex(): number {
return this._oldIndex;
}
get type(): string {
return this._type;
}
}

View File

@@ -0,0 +1,115 @@
/**
* @flow
* DocumentReference representation wrapper
*/
import CollectionReference from './CollectionReference';
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import { firestoreAutoId } from '../../utils';
export type WriteOptions = {
merge?: boolean,
}
/**
* @class DocumentReference
*/
export default class DocumentReference {
_documentPath: Path;
_firestore: Object;
constructor(firestore: Object, documentPath: Path) {
this._documentPath = documentPath;
this._firestore = firestore;
}
get firestore(): Object {
return this._firestore;
}
get id(): string | null {
return this._documentPath.id;
}
get parent(): CollectionReference {
const parentPath = this._documentPath.parent();
return new CollectionReference(this._firestore, parentPath);
}
get path(): string {
return this._documentPath.relativeName;
}
collection(collectionPath: string): CollectionReference {
const path = this._documentPath.child(collectionPath);
if (!path.isCollection) {
throw new Error('Argument "collectionPath" must point to a collection.');
}
return new CollectionReference(this._firestore, path);
}
delete(): Promise<void> {
return this._firestore._native
.documentDelete(this.path);
}
get(): Promise<DocumentSnapshot> {
return this._firestore._native
.documentGet(this.path)
.then(result => new DocumentSnapshot(this._firestore, result));
}
onSnapshot(onNext: Function, onError?: Function): () => void {
// TODO: Validation
const listenerId = firestoreAutoId();
const listener = (nativeDocumentSnapshot) => {
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
onNext(documentSnapshot);
};
// Listen to snapshot events
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`),
listener,
);
// Listen for snapshot error events
if (onError) {
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
onError,
);
}
// Add the native listener
this._firestore._native
.documentOnSnapshot(this.path, listenerId);
// Return an unsubscribe method
return this._offDocumentSnapshot.bind(this, listenerId, listener);
}
set(data: Object, writeOptions?: WriteOptions): Promise<void> {
return this._firestore._native
.documentSet(this.path, data, writeOptions);
}
update(data: Object): Promise<void> {
return this._firestore._native
.documentUpdate(this.path, data);
}
/**
* Remove document snapshot listener
* @param listener
*/
_offDocumentSnapshot(listenerId: number, listener: Function) {
this._firestore.log.info('Removing onDocumentSnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`), listener);
this._firestore._native
.documentOffSnapshot(this.path, listenerId);
}
}

View File

@@ -0,0 +1,56 @@
/**
* @flow
* DocumentSnapshot representation wrapper
*/
import DocumentReference from './DocumentReference';
import Path from './Path';
export type SnapshotMetadata = {
fromCache: boolean,
hasPendingWrites: boolean,
}
export type DocumentSnapshotNativeData = {
data: Object,
metadata: SnapshotMetadata,
path: string,
}
/**
* @class DocumentSnapshot
*/
export default class DocumentSnapshot {
_data: Object;
_metadata: SnapshotMetadata;
_ref: DocumentReference;
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
this._data = nativeData.data;
this._metadata = nativeData.metadata;
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
}
get exists(): boolean {
return this._data !== undefined;
}
get id(): string | null {
return this._ref.id;
}
get metadata(): SnapshotMetadata {
return _metadata;
}
get ref(): DocumentReference {
return this._ref;
}
data(): Object {
return this._data;
}
get(fieldPath: string): any {
return this._data[fieldPath];
}
}

View File

@@ -0,0 +1,29 @@
/**
* @flow
* GeoPoint representation wrapper
*/
/**
* @class GeoPoint
*/
export default class GeoPoint {
_latitude: number;
_longitude: number;
constructor(latitude: number, longitude: number) {
// TODO: Validation
// validate.isNumber('latitude', latitude);
// validate.isNumber('longitude', longitude);
this._latitude = latitude;
this._longitude = longitude;
}
get latitude() {
return this._latitude;
}
get longitude() {
return this._longitude;
}
}

View File

@@ -0,0 +1,59 @@
/**
* @flow
* Path representation wrapper
*/
/**
* @class Path
*/
export default class Path {
_parts: string[];
constructor(pathComponents: string[]) {
this._parts = pathComponents;
}
get id(): string | null {
if (this._parts.length > 0) {
return this._parts[this._parts.length - 1];
}
return null;
}
get isDocument(): boolean {
return this._parts.length > 0 && this._parts.length % 2 === 0;
}
get isCollection(): boolean {
return this._parts.length % 2 === 1;
}
get relativeName(): string {
return this._parts.join('/');
}
child(relativePath: string): Path {
return new Path(this._parts.concat(relativePath.split('/')));
}
parent(): Path | null {
if (this._parts.length === 0) {
return null;
}
return new Path(this._parts.slice(0, this._parts.length - 1));
}
/**
*
* @package
*/
static fromName(name): Path {
const parts = name.split('/');
if (parts.length === 0) {
return new Path([]);
}
return new Path(parts);
}
}

View File

@@ -0,0 +1,230 @@
/**
* @flow
* Query representation wrapper
*/
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import QuerySnapshot from './QuerySnapshot';
import INTERNALS from '../../internals';
import { firestoreAutoId } from '../../utils';
const DIRECTIONS = {
ASC: 'ASCENDING',
asc: 'ASCENDING',
DESC: 'DESCENDING',
desc: 'DESCENDING',
};
const OPERATORS = {
'=': 'EQUAL',
'==': 'EQUAL',
'>': 'GREATER_THAN',
'>=': 'GREATER_THAN_OR_EQUAL',
'<': 'LESS_THAN',
'<=': 'LESS_THAN_OR_EQUAL',
};
export type Direction = 'DESC' | 'desc' | 'ASC' | 'asc';
type FieldFilter = {
fieldPath: string,
operator: string,
value: any,
}
type FieldOrder = {
direction: string,
fieldPath: string,
}
type QueryOptions = {
endAt?: any[],
endBefore?: any[],
limit?: number,
offset?: number,
selectFields?: string[],
startAfter?: any[],
startAt?: any[],
}
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
/**
* @class Query
*/
export default class Query {
_fieldFilters: FieldFilter[];
_fieldOrders: FieldOrder[];
_firestore: Object;
_iid: number;
_queryOptions: QueryOptions;
_referencePath: Path;
constructor(firestore: Object, path: Path, fieldFilters?: FieldFilter[],
fieldOrders?: FieldOrder[], queryOptions?: QueryOptions) {
this._fieldFilters = fieldFilters || [];
this._fieldOrders = fieldOrders || [];
this._firestore = firestore;
this._queryOptions = queryOptions || {};
this._referencePath = path;
}
get firestore(): Object {
return this._firestore;
}
endAt(fieldValues: any): Query {
fieldValues = [].slice.call(arguments);
// TODO: Validation
const options = {
...this._queryOptions,
endAt: fieldValues,
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
}
endBefore(fieldValues: any): Query {
fieldValues = [].slice.call(arguments);
// TODO: Validation
const options = {
...this._queryOptions,
endBefore: fieldValues,
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
}
get(): Promise<QuerySnapshot> {
return this._firestore._native
.collectionGet(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
)
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
}
limit(limit: number): Query {
// TODO: Validation
// validate.isInteger('n', n);
const options = {
...this._queryOptions,
limit,
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
}
onSnapshot(onNext: () => any, onError?: () => any): () => void {
// TODO: Validation
const listenerId = firestoreAutoId();
const listener = (nativeQuerySnapshot) => {
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
onNext(querySnapshot);
};
// Listen to snapshot events
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`),
listener,
);
// Listen for snapshot error events
if (onError) {
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
onError,
);
}
// Add the native listener
this._firestore._native
.collectionOnSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId
);
// Return an unsubscribe method
return this._offCollectionSnapshot.bind(this, listenerId, listener);
}
orderBy(fieldPath: string, directionStr?: Direction = 'asc'): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isOptionalFieldOrder('directionStr', directionStr);
if (this._queryOptions.startAt || this._queryOptions.endAt) {
throw new Error('Cannot specify an orderBy() constraint after calling ' +
'startAt(), startAfter(), endBefore() or endAt().');
}
const newOrder = {
direction: DIRECTIONS[directionStr],
fieldPath,
};
const combinedOrders = this._fieldOrders.concat(newOrder);
return new Query(this.firestore, this._referencePath, this._fieldFilters,
combinedOrders, this._queryOptions);
}
startAfter(fieldValues: any): Query {
fieldValues = [].slice.call(arguments);
// TODO: Validation
const options = {
...this._queryOptions,
startAfter: fieldValues,
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
}
startAt(fieldValues: any): Query {
fieldValues = [].slice.call(arguments);
// TODO: Validation
const options = {
...this._queryOptions,
startAt: fieldValues,
};
return new Query(this.firestore, this._referencePath, this._fieldFilters,
this._fieldOrders, options);
}
where(fieldPath: string, opStr: Operator, value: any): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isFieldFilter('fieldFilter', opStr, value);
const newFilter = {
fieldPath,
operator: OPERATORS[opStr],
value,
};
const combinedFilters = this._fieldFilters.concat(newFilter);
return new Query(this.firestore, this._referencePath, combinedFilters,
this._fieldOrders, this._queryOptions);
}
/**
* Remove query snapshot listener
* @param listener
*/
_offCollectionSnapshot(listenerId: number, listener: Function) {
this._firestore.log.info('Removing onQuerySnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener);
this._firestore._native
.collectionOffSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId
);
}
}

View File

@@ -0,0 +1,66 @@
/**
* @flow
* QuerySnapshot representation wrapper
*/
import DocumentChange from './DocumentChange';
import DocumentSnapshot from './DocumentSnapshot';
import Query from './Query';
import type { DocumentChangeNativeData } from './DocumentChange';
import type { DocumentSnapshotNativeData, SnapshotMetadata } from './DocumentSnapshot';
type QuerySnapshotNativeData = {
changes: DocumentChangeNativeData[],
documents: DocumentSnapshotNativeData[],
metadata: SnapshotMetadata,
}
/**
* @class QuerySnapshot
*/
export default class QuerySnapshot {
_changes: DocumentChange[];
_docs: DocumentSnapshot[];
_metadata: SnapshotMetadata;
_query: Query;
constructor(firestore: Object, query: Query, nativeData: QuerySnapshotNativeData) {
this._changes = nativeData.changes.map(change => new DocumentChange(change));
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
this._metadata = nativeData.metadata;
this._query = query;
}
get docChanges(): DocumentChange[] {
return this._changes;
}
get docs(): DocumentSnapshot[] {
return this._docs;
}
get empty(): boolean {
return this._docs.length === 0;
}
get query(): Query {
return this._query;
}
get metadata(): SnapshotMetadata {
return this._metadata;
}
get size(): number {
return this._docs.length;
}
forEach(callback: DocumentSnapshot => any) {
// TODO: Validation
// validate.isFunction('callback', callback);
for (const doc of this._docs) {
callback(doc);
}
}
}

View File

@@ -0,0 +1,75 @@
/**
* @flow
* WriteBatch representation wrapper
*/
import DocumentReference from './DocumentReference';
import type { WriteOptions } from './DocumentReference';
type DocumentWrite = {
data?: Object,
options?: Object,
path: string,
type: 'DELETE' | 'SET' | 'UPDATE',
}
/**
* @class WriteBatch
*/
export default class WriteBatch {
_firestore: Object;
_writes: DocumentWrite[];
constructor(firestore: Object) {
this._firestore = firestore;
this._writes = [];
}
commit(): Promise<void> {
return this._firestore._native
.documentBatch(this._writes);
}
delete(docRef: DocumentReference): WriteBatch {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isOptionalPrecondition('deleteOptions', deleteOptions);
this._writes.push({
path: docRef.path,
type: 'DELETE',
});
return this;
}
set(docRef: DocumentReference, data: Object, writeOptions?: WriteOptions) {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data);
// validate.isOptionalPrecondition('writeOptions', writeOptions);
this._writes.push({
data,
options: writeOptions,
path: docRef.path,
type: 'SET',
});
return this;
}
// TODO: Update to new method signature
update(docRef: DocumentReference, data: Object): WriteBatch {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data, true);
this._writes.push({
data,
path: docRef.path,
type: 'UPDATE',
});
return this;
}
}

View File

@@ -0,0 +1,145 @@
/**
* @flow
* Firestore representation wrapper
*/
import { NativeModules } from 'react-native';
import ModuleBase from './../../utils/ModuleBase';
import CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference';
import DocumentSnapshot from './DocumentSnapshot';
import GeoPoint from './GeoPoint';
import Path from './Path';
import WriteBatch from './WriteBatch';
import INTERNALS from './../../internals';
type CollectionSyncEvent = {
appName: string,
querySnapshot?: QuerySnapshot,
error?: Object,
listenerId: string,
path: string,
}
type DocumentSyncEvent = {
appName: string,
documentSnapshot?: DocumentSnapshot,
error?: Object,
listenerId: string,
path: string,
}
/**
* @class Firestore
*/
export default class Firestore extends ModuleBase {
static _NAMESPACE = 'firestore';
static _NATIVE_MODULE = 'RNFirebaseFirestore';
_referencePath: Path;
constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, true);
this._referencePath = new Path([]);
this.addListener(
// sub to internal native event - this fans out to
// public event name: onCollectionSnapshot
this._getAppEventName('firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this),
);
this.addListener(
// sub to internal native event - this fans out to
// public event name: onDocumentSnapshot
this._getAppEventName('firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this),
);
}
batch(): WriteBatch {
return new WriteBatch(this);
}
/**
*
* @param collectionPath
* @returns {CollectionReference}
*/
collection(collectionPath: string): CollectionReference {
const path = this._referencePath.child(collectionPath);
if (!path.isCollection) {
throw new Error('Argument "collectionPath" must point to a collection.');
}
return new CollectionReference(this, path);
}
/**
*
* @param documentPath
* @returns {DocumentReference}
*/
doc(documentPath: string): DocumentReference {
const path = this._referencePath.child(documentPath);
if (!path.isDocument) {
throw new Error('Argument "documentPath" must point to a document.');
}
return new DocumentReference(this, path);
}
enablePersistence(): Promise<void> {
throw new Error('Persistence is enabled by default on the Firestore SDKs');
}
runTransaction(updateFunction): Promise<any> {
throw new Error('firebase.firestore().runTransaction() coming soon');
}
setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel'));
}
settings(settings: Object): void {
throw new Error('firebase.firestore().settings() coming soon');
}
/**
* INTERNALS
*/
/**
* Internal collection sync listener
* @param event
* @private
*/
_onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) {
this.emit(this._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error);
} else {
this.emit(this._getAppEventName(`onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
}
}
/**
* Internal document sync listener
* @param event
* @private
*/
_onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) {
this.emit(this._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error);
} else {
this.emit(this._getAppEventName(`onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
}
}
}
export const statics = {
FieldValue: {
delete: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.deleteFieldValue || {},
serverTimestamp: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.serverTimestampFieldValue || {}
},
GeoPoint,
};

View File

@@ -1,4 +1,4 @@
import { Platform } from 'react-native';
import { Platform, NativeModules } from 'react-native';
import ModuleBase from './../../utils/ModuleBase';
import RemoteMessage from './RemoteMessage';
@@ -25,6 +25,8 @@ const WILL_PRESENT_RESULT = {
None: 'UNNotificationPresentationOptionNone',
};
const FirebaseMessaging = NativeModules.FirebaseMessaging;
/**
* IOS only finish function
* @param data

View File

@@ -15,6 +15,7 @@ const logs = {};
const MULTI_APP_MODULES = [
'auth',
'database',
'firestore',
'storage',
];
@@ -31,6 +32,10 @@ const NATIVE_MODULE_EVENTS = {
'database_transaction_event',
// 'database_server_offset', // TODO
],
Firestore: [
'firestore_collection_sync_event',
'firestore_document_sync_event',
],
};
const DEFAULTS = {

View File

@@ -5,10 +5,19 @@ import { Platform } from 'react-native';
// modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const hasOwnProperty = Object.hasOwnProperty;
// const DEFAULT_CHUNK_SIZE = 50;
// Source: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical
const REGEXP_FIELD_NAME = new RegExp(
`^(?:\\.?((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))$`
);
const REGEXP_FIELD_PATH = new RegExp(
`^((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+)(?:\\.((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))*$`
);
/**
* Deep get a value from an object.
* @website https://github.com/Salakar/deeps
@@ -88,6 +97,16 @@ export function isString(value: any): boolean {
return typeof value === 'string';
}
/**
* Firestore field name/path validator.
* @param field
* @param paths
* @return {boolean}
*/
export function isValidFirestoreField(field, paths) {
return (paths ? REGEXP_FIELD_PATH : REGEXP_FIELD_NAME).test(field);
}
// platform checks
export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';
@@ -374,3 +393,16 @@ export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Functi
return Promise.reject(error);
});
}
/**
* Generate a firestore auto id for use with collection/document .add()
* @return {string}
*/
export function firestoreAutoId(): string {
let autoId = '';
for (let i = 0; i < 20; i++) {
autoId += AUTO_ID_CHARS.charAt(Math.floor(Math.random() * AUTO_ID_CHARS.length));
}
return autoId;
}

6
package-lock.json generated
View File

@@ -2624,9 +2624,9 @@
}
},
"flow-bin": {
"version": "0.46.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.46.0.tgz",
"integrity": "sha1-Bq1/4Z3dsQQiZEOAZKKjL+4SuHI=",
"version": "0.55.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.55.0.tgz",
"integrity": "sha1-kIPakye9jKtrQHbWPYXyJHp+rhs=",
"dev": true
},
"for-in": {

View File

@@ -1,8 +1,8 @@
{
"name": "react-native-firebase",
"version": "3.0.0-alpha.5",
"version": "3.0.0",
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Auth, Database, Messaging (FCM), Remote Config, Storage, Admob, Analytics, Crash Reporting, and Performance.",
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Messaging (FCM), Remote Config, Storage and Performance.",
"main": "index",
"scripts": {
"flow": "flow",
@@ -38,12 +38,20 @@
"admob",
"auth",
"config",
"digits",
"phone-auth",
"sms",
"firestore",
"cloud-firestore",
"datastore",
"remote-config",
"transactions",
"react-native",
"react-native-firebase",
"firebase",
"fcm",
"apn",
"gcm",
"analytics",
"messaging",
"database",
@@ -51,11 +59,12 @@
"ios",
"crash",
"firestack",
"performance"
"performance",
"firestore"
],
"peerDependencies": {
"react": "*",
"react-native": ">= 0.40.0",
"react-native": ">= 0.48.0",
"fbjs": "*"
},
"devDependencies": {
@@ -70,22 +79,25 @@
"eslint-plugin-import": "^2.0.1",
"eslint-plugin-jsx-a11y": "^2.2.3",
"eslint-plugin-react": "^6.4.1",
"flow-bin": "^0.46.0",
"react": "^15.3.0",
"react-dom": "^15.3.0",
"react-native": "^0.44.0",
"flow-bin": "^0.55.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-native": "^0.48.0",
"shelljs": "^0.7.8",
"wml": "0.0.82"
},
"dependencies": {
"bows": "^1.6.0",
"prop-types": "^15.5.10"
"prop-types": "^15.6.0"
},
"rnpm": {
"android": {
"buildPatch": " compile(project(':react-native-firebase')) {\n transitive = false\n }\n",
"packageImportPath": "import io.invertase.firebase.RNFirebasePackage;\nimport io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;",
"packageInstance": "new RNFirebasePackage(),\n new RNFirebaseAnalyticsPackage()"
"packageImportPath": "import io.invertase.firebase.RNFirebasePackage;",
"packageInstance": "new RNFirebasePackage()"
},
"commands": {
"postlink": "node node_modules/react-native-firebase/scripts/rnpm-postlink"
}
}
}

26
scripts/rnpm-postlink.js Normal file
View File

@@ -0,0 +1,26 @@
const fs = require('fs');
const path = require('path');
const appBuildGradlePath = path.join('android', 'app', 'build.gradle');
const defaultCompileStatement = "compile project(':react-native-firebase')";
const requiredCompileStatement = "compile(project(':react-native-firebase')) {\n transitive = false\n }";
// android/build.gradle
// 1) TODO: Add Google Play maven repository
// 2) TODO: Add google-services dependency if required
// android/app/build.gradle
// 0) Load the file
let buildGradleContents = fs.readFileSync(appBuildGradlePath, 'utf8');
// 1) Check that react-native-firebase compile statement is the correct format
buildGradleContents = buildGradleContents.replace(defaultCompileStatement, requiredCompileStatement);
// 2) TODO: Add firebase-core and play-services-base dependencies
// 3) TODO: Add google-services plugin
// 4) Write file
fs.writeFileSync(appBuildGradlePath, buildGradleContents);

View File

@@ -41,6 +41,7 @@ android {
ndk {
abiFilters "armeabi-v7a", "x86"
}
multiDexEnabled true
}
splits {
abi {
@@ -71,7 +72,7 @@ android {
}
}
project.ext.firebaseVersion = '11.2.0'
project.ext.firebaseVersion = '11.4.2'
dependencies {
// compile(project(':react-native-firebase')) {
@@ -82,7 +83,6 @@ dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.google.android.gms:play-services-base:$firebaseVersion"
compile "com.google.firebase:firebase-ads:$firebaseVersion"
compile "com.google.firebase:firebase-ads:$firebaseVersion"
compile "com.google.firebase:firebase-auth:$firebaseVersion"
compile "com.google.firebase:firebase-config:$firebaseVersion"
compile "com.google.firebase:firebase-core:$firebaseVersion"
@@ -91,6 +91,7 @@ dependencies {
compile "com.google.firebase:firebase-messaging:$firebaseVersion"
compile "com.google.firebase:firebase-perf:$firebaseVersion"
compile "com.google.firebase:firebase-storage:$firebaseVersion"
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
compile "com.google.firebase:firebase-invites:$firebaseVersion"
compile "com.android.support:appcompat-v7:26.0.1"
compile "com.facebook.react:react-native:+" // From node_modules

Binary file not shown.

View File

@@ -10,9 +10,9 @@ import io.invertase.firebase.auth.RNFirebaseAuthPackage;
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage;
import io.invertase.firebase.crash.RNFirebaseCrashPackage;
import io.invertase.firebase.database.RNFirebaseDatabasePackage;
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
import io.invertase.firebase.links.RNFirebaseLinksPackage;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
import io.invertase.firebase.storage.RNFirebaseStoragePackage;
import com.oblador.vectoricons.VectorIconsPackage;
import com.facebook.react.ReactNativeHost;
@@ -43,9 +43,10 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseRemoteConfigPackage(),
new RNFirebaseCrashPackage(),
new RNFirebaseDatabasePackage(),
new RNFirebaseFirestorePackage(),
new RNFirebaseLinksPackage(),
new RNFirebaseMessagingPackage(),
new RNFirebasePerformancePackage(),
// new RNFirebasePerformancePackage(),
new RNFirebaseStoragePackage()
);
}

View File

@@ -6,8 +6,8 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.google.gms:google-services:3.1.0'
classpath 'com.google.firebase:firebase-plugins:1.1.0'
classpath 'com.google.gms:google-services:3.1.1'
classpath 'com.google.firebase:firebase-plugins:1.1.1'
}
}
@@ -18,6 +18,7 @@ allprojects {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
mavenLocal()
google()
}
}

View File

@@ -37,4 +37,4 @@
<key>DATABASE_URL</key>
<string>https://rnfirebase-b9ad4.firebaseio.com</string>
</dict>
</plist>
</plist>

View File

@@ -8,10 +8,16 @@ target 'ReactNativeFirebaseDemo' do
# use_frameworks!
react_native_path = "../node_modules/react-native"
pod "Yoga", :path => "#{react_native_path}/ReactCommon/yoga"
pod "yoga", :path => "#{react_native_path}/ReactCommon/yoga"
# Pods for ReactNativeFirebaseDemo
pod 'React', :path => '../node_modules/react-native'
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'BatchedBridge',
'RCTText',
'RCTNetwork',
'RCTWebSocket',
]
pod 'Firebase/AdMob'
pod 'Firebase/Auth'
@@ -19,6 +25,7 @@ target 'ReactNativeFirebaseDemo' do
pod 'Firebase/Crash'
pod 'Firebase/Database'
pod 'Firebase/DynamicLinks'
pod 'Firebase/Firestore'
pod 'Firebase/Messaging'
pod 'Firebase/RemoteConfig'
pod 'Firebase/Storage'
@@ -26,4 +33,12 @@ target 'ReactNativeFirebaseDemo' do
pod 'RNFirebase', :path => './../../'
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "React"
target.remove_from_project
end
end
end
end

View File

@@ -1,83 +1,98 @@
PODS:
- Firebase/AdMob (4.1.0):
- BoringSSL (9.0):
- BoringSSL/Implementation (= 9.0)
- BoringSSL/Interface (= 9.0)
- BoringSSL/Implementation (9.0):
- BoringSSL/Interface (= 9.0)
- BoringSSL/Interface (9.0)
- Firebase/AdMob (4.3.0):
- Firebase/Core
- Google-Mobile-Ads-SDK (= 7.22.0)
- Firebase/Auth (4.1.0):
- Google-Mobile-Ads-SDK (= 7.24.1)
- Firebase/Auth (4.3.0):
- Firebase/Core
- FirebaseAuth (= 4.1.0)
- Firebase/Core (4.1.0):
- FirebaseAnalytics (= 4.0.3)
- FirebaseCore (= 4.0.5)
- Firebase/Crash (4.1.0):
- FirebaseAuth (= 4.2.1)
- Firebase/Core (4.3.0):
- FirebaseAnalytics (= 4.0.4)
- FirebaseCore (= 4.0.8)
- Firebase/Crash (4.3.0):
- Firebase/Core
- FirebaseCrash (= 2.0.1)
- Firebase/Database (4.1.0):
- FirebaseCrash (= 2.0.2)
- Firebase/Database (4.3.0):
- Firebase/Core
- FirebaseDatabase (= 4.0.1)
- Firebase/DynamicLinks (4.1.0):
- FirebaseDatabase (= 4.1.0)
- Firebase/DynamicLinks (4.3.0):
- Firebase/Core
- FirebaseDynamicLinks (= 2.1.0)
- Firebase/Messaging (4.1.0):
- Firebase/Firestore (4.3.0):
- Firebase/Core
- FirebaseMessaging (= 2.0.1)
- Firebase/Performance (4.1.0):
- FirebaseFirestore (= 0.8.0)
- Firebase/Messaging (4.3.0):
- Firebase/Core
- FirebasePerformance (= 1.0.3)
- Firebase/RemoteConfig (4.1.0):
- FirebaseMessaging (= 2.0.4)
- Firebase/Performance (4.3.0):
- Firebase/Core
- FirebaseRemoteConfig (= 2.0.2)
- Firebase/Storage (4.1.0):
- FirebasePerformance (= 1.0.6)
- Firebase/RemoteConfig (4.3.0):
- Firebase/Core
- FirebaseStorage (= 2.0.1)
- FirebaseAnalytics (4.0.3):
- FirebaseRemoteConfig (= 2.0.3)
- Firebase/Storage (4.3.0):
- Firebase/Core
- FirebaseStorage (= 2.0.2)
- FirebaseAnalytics (4.0.4):
- FirebaseCore (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- nanopb (~> 0.3)
- FirebaseAuth (4.1.0):
- FirebaseAuth (4.2.1):
- FirebaseAnalytics (~> 4.0)
- GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)
- GTMSessionFetcher/Core (~> 1.1)
- FirebaseCore (4.0.5):
- FirebaseCore (4.0.8):
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- nanopb (~> 0.3)
- FirebaseCrash (2.0.1):
- FirebaseCrash (2.0.2):
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- Protobuf (~> 3.1)
- FirebaseDatabase (4.0.1):
- FirebaseDatabase (4.1.0):
- FirebaseAnalytics (~> 4.0)
- FirebaseCore (~> 4.0)
- leveldb-library (~> 1.18)
- FirebaseDynamicLinks (2.1.0):
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (2.0.1):
- FirebaseFirestore (0.8.0):
- FirebaseAnalytics (~> 4.0)
- FirebaseAuth (~> 4.2)
- FirebaseCore (~> 4.0)
- FirebaseMessaging (2.0.1):
- gRPC-ProtoRPC (~> 1.0)
- leveldb-library (~> 1.18)
- Protobuf (~> 3.1)
- FirebaseInstanceID (2.0.4)
- FirebaseMessaging (2.0.4):
- FirebaseAnalytics (~> 4.0)
- FirebaseCore (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- Protobuf (~> 3.1)
- FirebasePerformance (1.0.3):
- FirebasePerformance (1.0.6):
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- GTMSessionFetcher/Core (~> 1.1)
- Protobuf (~> 3.1)
- FirebaseRemoteConfig (2.0.2):
- FirebaseRemoteConfig (2.0.3):
- FirebaseAnalytics (~> 4.0)
- FirebaseInstanceID (~> 2.0)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- Protobuf (~> 3.1)
- FirebaseStorage (2.0.1):
- FirebaseStorage (2.0.2):
- FirebaseAnalytics (~> 4.0)
- FirebaseCore (~> 4.0)
- GTMSessionFetcher/Core (~> 1.1)
- Google-Mobile-Ads-SDK (7.22.0)
- Google-Mobile-Ads-SDK (7.24.1)
- GoogleToolboxForMac/DebugUtils (2.1.1):
- GoogleToolboxForMac/Defines (= 2.1.1)
- GoogleToolboxForMac/Defines (2.1.1)
@@ -90,7 +105,23 @@ PODS:
- GoogleToolboxForMac/Defines (= 2.1.1)
- GoogleToolboxForMac/NSString+URLArguments (= 2.1.1)
- GoogleToolboxForMac/NSString+URLArguments (2.1.1)
- GTMSessionFetcher/Core (1.1.11)
- gRPC (1.6.0):
- gRPC-Core (= 1.6.0)
- gRPC-RxLibrary (= 1.6.0)
- gRPC-Core (1.6.0):
- gRPC-Core/Implementation (= 1.6.0)
- gRPC-Core/Interface (= 1.6.0)
- gRPC-Core/Implementation (1.6.0):
- BoringSSL (~> 9.0)
- gRPC-Core/Interface (= 1.6.0)
- nanopb (~> 0.3)
- gRPC-Core/Interface (1.6.0)
- gRPC-ProtoRPC (1.6.0):
- gRPC (= 1.6.0)
- gRPC-RxLibrary (= 1.6.0)
- Protobuf (~> 3.0)
- gRPC-RxLibrary (1.6.0)
- GTMSessionFetcher/Core (1.1.12)
- leveldb-library (1.18.3)
- nanopb (0.3.8):
- nanopb/decode (= 0.3.8)
@@ -98,17 +129,30 @@ PODS:
- nanopb/decode (0.3.8)
- nanopb/encode (0.3.8)
- Protobuf (3.4.0)
- React (0.44.3):
- React/Core (= 0.44.3)
- React/Core (0.44.3):
- React/cxxreact
- Yoga (= 0.44.3.React)
- React/cxxreact (0.44.3):
- React/jschelpers
- React/jschelpers (0.44.3)
- RNFirebase (3.0.0-alpha.5):
- React (0.49.0-rc.6):
- React/Core (= 0.49.0-rc.6)
- React/BatchedBridge (0.49.0-rc.6):
- React/Core
- React/cxxreact_legacy
- React/Core (0.49.0-rc.6):
- yoga (= 0.49.0-rc.6.React)
- React/cxxreact_legacy (0.49.0-rc.6):
- React/jschelpers_legacy
- React/fishhook (0.49.0-rc.6)
- React/jschelpers_legacy (0.49.0-rc.6)
- React/RCTBlob (0.49.0-rc.6):
- React/Core
- React/RCTNetwork (0.49.0-rc.6):
- React/Core
- React/RCTText (0.49.0-rc.6):
- React/Core
- React/RCTWebSocket (0.49.0-rc.6):
- React/Core
- React/fishhook
- React/RCTBlob
- RNFirebase (3.0.0):
- React
- Yoga (0.44.3.React)
- yoga (0.49.0-rc.6.React)
DEPENDENCIES:
- Firebase/AdMob
@@ -117,45 +161,56 @@ DEPENDENCIES:
- Firebase/Crash
- Firebase/Database
- Firebase/DynamicLinks
- Firebase/Firestore
- Firebase/Messaging
- Firebase/Performance
- Firebase/RemoteConfig
- Firebase/Storage
- React (from `../node_modules/react-native`)
- React/BatchedBridge (from `../node_modules/react-native`)
- React/Core (from `../node_modules/react-native`)
- React/RCTNetwork (from `../node_modules/react-native`)
- React/RCTText (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- RNFirebase (from `./../../`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
EXTERNAL SOURCES:
React:
:path: "../node_modules/react-native"
RNFirebase:
:path: "./../../"
Yoga:
yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
Firebase: ebebf41db7f10e0c7668b6eaaa857fbe599aa478
FirebaseAnalytics: 76f754d37ca5b04f36856729b6af3ca0152d1069
FirebaseAuth: 8d1d2389cf82f891048d6d50d27d044f55ae09a6
FirebaseCore: 7d876ea97a830cbe62ba7fbbe7670c833a324ba0
FirebaseCrash: bce9fbfb7dd6cc850a41fe39740a2292e6b61f2e
FirebaseDatabase: 94c38c783d23dc6679441050772d42e801d06e9e
BoringSSL: 19083b821ef3ae0f758fae15482e183003b1e265
Firebase: 83283761a1ef6dc9846e03d08059f51421afbd65
FirebaseAnalytics: 722b53c7b32bfc7806b06e0093a2f5180d4f2c5a
FirebaseAuth: d7f047fbeab98062b98ea933b8d934e0fb1190e2
FirebaseCore: 69b1a5ac5f857ba6d5fd9d5fe794f4786dd5e579
FirebaseCrash: cded0fc566c03651aea606a101bc156085f333ca
FirebaseDatabase: 607284a103e961d7f5863ee603cab5e85f443bd6
FirebaseDynamicLinks: ed4cb6c42705aaa5e841ed2d76e3a4bddbec10c1
FirebaseInstanceID: 0500e3cb54a1a4e01a8cffcc09323b8bb8fc7e1e
FirebaseMessaging: b45ff9ef5d932600d3f78ff43168e7c5707aa7dc
FirebasePerformance: 36bdb0500213b459ae991766801d5dc5399ff231
FirebaseRemoteConfig: 5b3e3301ef2f237b1b588e8ef3211b5a22e9e15d
FirebaseStorage: 661fc1f8d4131891d256b62e82a45ace8b3f0c3b
Google-Mobile-Ads-SDK: 1bdf1a4244d0553b1840239874c209c01aef055f
FirebaseFirestore: 8e2fd99a621ae6fc6acfac3bdea824fe9d9c128d
FirebaseInstanceID: 70c2b877e9338971b2429ea5a4293df6961aa44e
FirebaseMessaging: 3dd86bfda2acb680b05c97f3f8ac566e9bb87b2a
FirebasePerformance: fa032c27e229eb8c1a8638918793fe2e47465205
FirebaseRemoteConfig: 1c982f73af48ec048c8fa8621d5178cfdffac9aa
FirebaseStorage: 0cca42d9b889a0227c3a50121f45a4469fc9eb27
Google-Mobile-Ads-SDK: ed8004a7265b424568dc84f3d2bbe3ea3fff958f
GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0
GTMSessionFetcher: 5ad62e8200fa00ed011fe5e08d27fef72c5b1429
gRPC: '07788969b862af21491908f82b83d17ac08c94cd'
gRPC-Core: f707ade59c559fe718e27713189607d03b15f571
gRPC-ProtoRPC: de7505e493a9d1b6b96c8ea8f976c73100fdf53f
gRPC-RxLibrary: 17b9699beb0a838b95b57832244f9ead18e66777
GTMSessionFetcher: ebaa1f79a5366922c1735f1566901f50beba23b7
leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
React: 6361345ebeb769a929e10a06baf0c868d6d03ad5
RNFirebase: 60be8c01b94551a12e7be5431189e8ee8cefcdd3
Yoga: c90474ca3ec1edba44c97b6c381f03e222a9e287
React: e6ef6a41ec6dd1b7941417d60ca582bf5e9c739d
RNFirebase: 2ceda3aef595ea1379bf5bfcc1cd9ee7d6c05d3b
yoga: f9485d2ebf0ca773db2d727ea71b1aa8c9f3e075
PODFILE CHECKSUM: 46b6a553f3c9fd264b449806b373d33b4af518b5
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45
COCOAPODS: 1.2.1

View File

@@ -25,21 +25,9 @@
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
1852BC611C674A2E905975BC /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5F6AF99C2A6F4545B0F74832 /* Foundation.ttf */; };
211A77BD015F4D728A9B6E72 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7DD9301D866B4D52A7F83996 /* FontAwesome.ttf */; };
2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; };
2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; };
2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; };
2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; };
2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; };
2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; };
2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; };
2DCD954D1E0B4F2C00145EB5 /* ReactNativeFirebaseDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeFirebaseDemoTests.m */; };
42A0E8F428A74B23B4C7D95A /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FD3DFC8253C74B6298AFD3B7 /* SimpleLineIcons.ttf */; };
58EAF3FA628941C98A02BAE4 /* Feather.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 528FC2DCD44F4567A3FE9F59 /* Feather.ttf */; };
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
644044EA167D431D96D0E6C1 /* libLRDRCTSimpleToast.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBEE706291534F5F948A3805 /* libLRDRCTSimpleToast.a */; };
6F307BC16AD047C8BD921730 /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A6F0C1C198B4A6FB4AB907D /* MaterialCommunityIcons.ttf */; };
82FE196A2ADC4D29BB2CB4BF /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 48961CCCEE8947FB9EE3658F /* Ionicons.ttf */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
@@ -116,13 +104,6 @@
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;
remoteInfo = "ReactNativeFirebaseDemo-tvOS";
};
3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
@@ -242,6 +223,48 @@
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
8E7D4A501F83AF5200699FE4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
remoteInfo = fishhook;
};
8E7D4A521F83AF5200699FE4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32;
remoteInfo = "fishhook-tvOS";
};
8EC7AC341F8397E50041B0FE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7;
remoteInfo = "third-party";
};
8EC7AC361F8397E50041B0FE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D383D3C1EBD27B6005632C8;
remoteInfo = "third-party-tvOS";
};
8EC7AC381F8397E50041B0FE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 139D7E881E25C6D100323FB7;
remoteInfo = "double-conversion";
};
8EC7AC3A1F8397E50041B0FE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D383D621EBD27B9005632C8;
remoteInfo = "double-conversion-tvOS";
};
997890041E69DE2900F6820C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C7C48FEEC8E34DE4BB5DE211 /* RNFirebase.xcodeproj */;
@@ -256,13 +279,6 @@
remoteGlobalIDString = 5DBEB1501B18CEA900B34395;
remoteInfo = RNVectorIcons;
};
997890191E6D86E700F6820C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C4D9D5FCAA43A384C23C35 /* LRDRCTSimpleToast.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 15209BEF1D250F63000D0F44;
remoteInfo = LRDRCTSimpleToast;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@@ -288,17 +304,15 @@
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeFirebaseDemo/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
182271FFECD74C3B92960E1D /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = "<group>"; };
2D02E47B1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ReactNativeFirebaseDemo-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
2D02E4901E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReactNativeFirebaseDemo-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
42C7056622ACE9D1E626AD4C /* libPods-ReactNativeFirebaseDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeFirebaseDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
48961CCCEE8947FB9EE3658F /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; };
4935BEDE99B9436581953E77 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = "<group>"; };
528FC2DCD44F4567A3FE9F59 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
5F6AF99C2A6F4545B0F74832 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
739DB016E64944BE99C338C4 /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = "<group>"; };
7791402B6343C1906BE4D13A /* libPods-ReactNativeFirebaseDemoTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeFirebaseDemoTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
78C4D9D5FCAA43A384C23C35 /* LRDRCTSimpleToast.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = LRDRCTSimpleToast.xcodeproj; path = "../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast.xcodeproj"; sourceTree = "<group>"; };
7A6F0C1C198B4A6FB4AB907D /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; };
7DD9301D866B4D52A7F83996 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
@@ -337,33 +351,10 @@
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
E51DA6317685417F97A59475 /* libRNVectorIcons.a in Frameworks */,
644044EA167D431D96D0E6C1 /* libLRDRCTSimpleToast.a in Frameworks */,
076B30A1D09C6AEEAE912DEB /* libPods-ReactNativeFirebaseDemo.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E4781E0B4A5D006451C7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */,
2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */,
2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */,
2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */,
2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */,
2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */,
2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */,
2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E48D1E0B4A5D006451C7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -449,6 +440,8 @@
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */,
8E7D4A511F83AF5200699FE4 /* libfishhook.a */,
8E7D4A531F83AF5200699FE4 /* libfishhook-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@@ -479,6 +472,10 @@
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,
8EC7AC351F8397E50041B0FE /* libthird-party.a */,
8EC7AC371F8397E50041B0FE /* libthird-party.a */,
8EC7AC391F8397E50041B0FE /* libdouble-conversion.a */,
8EC7AC3B1F8397E50041B0FE /* libdouble-conversion.a */,
);
name = Products;
sourceTree = "<group>";
@@ -526,7 +523,6 @@
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
C7C48FEEC8E34DE4BB5DE211 /* RNFirebase.xcodeproj */,
8BE028F5B04B4DC3BEF0305F /* RNVectorIcons.xcodeproj */,
78C4D9D5FCAA43A384C23C35 /* LRDRCTSimpleToast.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@@ -570,21 +566,10 @@
children = (
13B07F961A680F5B00A75B9A /* ReactNativeFirebaseDemo.app */,
00E356EE1AD99517003FC87E /* ReactNativeFirebaseDemoTests.xctest */,
2D02E47B1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOS.app */,
2D02E4901E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOSTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
83F533111F76A95B0008C1A5 /* Recovered References */ = {
isa = PBXGroup;
children = (
0C76E33ACF004369AEB318B1 /* libRNVectorIcons.a */,
BBEE706291534F5F948A3805 /* libLRDRCTSimpleToast.a */,
);
name = "Recovered References";
sourceTree = "<group>";
};
99788FE61E69DE2900F6820C /* Products */ = {
isa = PBXGroup;
children = (
@@ -601,14 +586,6 @@
name = Products;
sourceTree = "<group>";
};
997890161E6D86E700F6820C /* Products */ = {
isa = PBXGroup;
children = (
9978901A1E6D86E700F6820C /* libLRDRCTSimpleToast.a */,
);
name = Products;
sourceTree = "<group>";
};
F50F62EA16044AFF8BD7CF63 /* Resources */ = {
isa = PBXGroup;
children = (
@@ -622,6 +599,7 @@
CC24EB30F0484352BD65FFC1 /* Octicons.ttf */,
FD3DFC8253C74B6298AFD3B7 /* SimpleLineIcons.ttf */,
182271FFECD74C3B92960E1D /* Zocial.ttf */,
528FC2DCD44F4567A3FE9F59 /* Feather.ttf */,
);
name = Resources;
sourceTree = "<group>";
@@ -668,42 +646,6 @@
productReference = 13B07F961A680F5B00A75B9A /* ReactNativeFirebaseDemo.app */;
productType = "com.apple.product-type.application";
};
2D02E47A1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "ReactNativeFirebaseDemo-tvOS" */;
buildPhases = (
2D02E4771E0B4A5D006451C7 /* Sources */,
2D02E4781E0B4A5D006451C7 /* Frameworks */,
2D02E4791E0B4A5D006451C7 /* Resources */,
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */,
);
buildRules = (
);
dependencies = (
);
name = "ReactNativeFirebaseDemo-tvOS";
productName = "ReactNativeFirebaseDemo-tvOS";
productReference = 2D02E47B1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOS.app */;
productType = "com.apple.product-type.application";
};
2D02E48F1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOSTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "ReactNativeFirebaseDemo-tvOSTests" */;
buildPhases = (
2D02E48C1E0B4A5D006451C7 /* Sources */,
2D02E48D1E0B4A5D006451C7 /* Frameworks */,
2D02E48E1E0B4A5D006451C7 /* Resources */,
);
buildRules = (
);
dependencies = (
2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */,
);
name = "ReactNativeFirebaseDemo-tvOSTests";
productName = "ReactNativeFirebaseDemo-tvOSTests";
productReference = 2D02E4901E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOSTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -715,16 +657,11 @@
TargetAttributes = {
00E356ED1AD99517003FC87E = {
CreatedOnToolsVersion = 6.2;
DevelopmentTeam = YMA4Y8JWM2;
TestTargetID = 13B07F861A680F5B00A75B9A;
};
2D02E47A1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1;
ProvisioningStyle = Automatic;
};
2D02E48F1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1;
ProvisioningStyle = Automatic;
TestTargetID = 2D02E47A1E0B4A5D006451C7;
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = YMA4Y8JWM2;
};
};
};
@@ -740,10 +677,6 @@
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 997890161E6D86E700F6820C /* Products */;
ProjectRef = 78C4D9D5FCAA43A384C23C35 /* LRDRCTSimpleToast.xcodeproj */;
},
{
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
@@ -801,8 +734,6 @@
targets = (
13B07F861A680F5B00A75B9A /* ReactNativeFirebaseDemo */,
00E356ED1AD99517003FC87E /* ReactNativeFirebaseDemoTests */,
2D02E47A1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOS */,
2D02E48F1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOSTests */,
);
};
/* End PBXProject section */
@@ -983,6 +914,48 @@
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8E7D4A511F83AF5200699FE4 /* libfishhook.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libfishhook.a;
remoteRef = 8E7D4A501F83AF5200699FE4 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8E7D4A531F83AF5200699FE4 /* libfishhook-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libfishhook-tvOS.a";
remoteRef = 8E7D4A521F83AF5200699FE4 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8EC7AC351F8397E50041B0FE /* libthird-party.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libthird-party.a";
remoteRef = 8EC7AC341F8397E50041B0FE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8EC7AC371F8397E50041B0FE /* libthird-party.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libthird-party.a";
remoteRef = 8EC7AC361F8397E50041B0FE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8EC7AC391F8397E50041B0FE /* libdouble-conversion.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libdouble-conversion.a";
remoteRef = 8EC7AC381F8397E50041B0FE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8EC7AC3B1F8397E50041B0FE /* libdouble-conversion.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libdouble-conversion.a";
remoteRef = 8EC7AC3A1F8397E50041B0FE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
997890051E69DE2900F6820C /* RNFirebase.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
@@ -997,13 +970,6 @@
remoteRef = 997890071E69DE2900F6820C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
9978901A1E6D86E700F6820C /* libLRDRCTSimpleToast.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libLRDRCTSimpleToast.a;
remoteRef = 997890191E6D86E700F6820C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@@ -1031,21 +997,7 @@
885057F5D1FA461AAAE0B487 /* Octicons.ttf in Resources */,
42A0E8F428A74B23B4C7D95A /* SimpleLineIcons.ttf in Resources */,
D46EBD0604CE40EFB18F8A35 /* Zocial.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E4791E0B4A5D006451C7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E48E1E0B4A5D006451C7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
58EAF3FA628941C98A02BAE4 /* Feather.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1064,7 +1016,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh";
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
26F9B43687A3EAE646F8970D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
@@ -1084,20 +1036,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native Code And Images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh";
};
6AE1012F46FF8A4D1D818A12 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1148,23 +1086,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E4771E0B4A5D006451C7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */,
2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E48C1E0B4A5D006451C7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2DCD954D1E0B4F2C00145EB5 /* ReactNativeFirebaseDemoTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -1173,11 +1094,6 @@
target = 13B07F861A680F5B00A75B9A /* ReactNativeFirebaseDemo */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 2D02E47A1E0B4A5D006451C7 /* ReactNativeFirebaseDemo-tvOS */;
targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -1197,6 +1113,7 @@
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = YMA4Y8JWM2;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -1205,7 +1122,6 @@
"$(inherited)",
"$(SRCROOT)/../node_modules/react-native-firebase/Firebase.framework/Headers/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast",
);
INFOPLIST_FILE = ReactNativeFirebaseDemoTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1229,11 +1145,11 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = YMA4Y8JWM2;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/react-native-firebase/Firebase.framework/Headers/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast",
);
INFOPLIST_FILE = ReactNativeFirebaseDemoTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1259,11 +1175,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = YMA4Y8JWM2;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/react-native-firebase/Firebase.framework/Headers/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast",
);
INFOPLIST_FILE = ReactNativeFirebaseDemo/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1272,7 +1188,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.invertase.ReactNativeFirebaseDemo;
PRODUCT_BUNDLE_IDENTIFIER = com.invertase.RNFirebaseTests;
PRODUCT_NAME = ReactNativeFirebaseDemo;
VERSIONING_SYSTEM = "apple-generic";
};
@@ -1284,11 +1200,11 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = YMA4Y8JWM2;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/react-native-firebase/Firebase.framework/Headers/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast",
);
INFOPLIST_FILE = ReactNativeFirebaseDemo/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1297,138 +1213,12 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.invertase.ReactNativeFirebaseDemo;
PRODUCT_BUNDLE_IDENTIFIER = com.invertase.RNFirebaseTests;
PRODUCT_NAME = ReactNativeFirebaseDemo;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
2D02E4971E0B4A5E006451C7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/react-native-firebase/Firebase.framework/Headers/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast",
);
INFOPLIST_FILE = "ReactNativeFirebaseDemo-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.ReactNativeFirebaseDemo-tvOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Debug;
};
2D02E4981E0B4A5E006451C7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/react-native-firebase/Firebase.framework/Headers/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-simple-toast/ios/LRDRCTSimpleToast",
);
INFOPLIST_FILE = "ReactNativeFirebaseDemo-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.ReactNativeFirebaseDemo-tvOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Release;
};
2D02E4991E0B4A5E006451C7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "ReactNativeFirebaseDemo-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.ReactNativeFirebaseDemo-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactNativeFirebaseDemo-tvOS.app/ReactNativeFirebaseDemo-tvOS";
TVOS_DEPLOYMENT_TARGET = 10.1;
};
name = Debug;
};
2D02E49A1E0B4A5E006451C7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "ReactNativeFirebaseDemo-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.ReactNativeFirebaseDemo-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReactNativeFirebaseDemo-tvOS.app/ReactNativeFirebaseDemo-tvOS";
TVOS_DEPLOYMENT_TARGET = 10.1;
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1448,6 +1238,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = YMA4Y8JWM2;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@@ -1489,6 +1280,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
DEVELOPMENT_TEAM = YMA4Y8JWM2;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -1526,24 +1318,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "ReactNativeFirebaseDemo-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D02E4971E0B4A5E006451C7 /* Debug */,
2D02E4981E0B4A5E006451C7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "ReactNativeFirebaseDemo-tvOSTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D02E4991E0B4A5E006451C7 /* Debug */,
2D02E49A1E0B4A5E006451C7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ReactNativeFirebaseDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -54,6 +54,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -83,6 +84,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

View File

@@ -65,6 +65,11 @@
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {

View File

@@ -47,6 +47,7 @@
<string>Octicons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Zocial.ttf</string>
<string>Feather.ttf</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>

2324
tests/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -32,13 +32,13 @@
"js-beautify": "^1.6.11",
"lodash.groupby": "^4.6.0",
"lodash.some": "^4.6.0",
"react": "16.0.0-alpha.12",
"react-native": "^0.48.4",
"react-test-renderer": "16.0.0-alpha.12",
"react-native-simple-toast": "0.0.5",
"prop-types": "^15.6.0",
"react": "^16.0.0",
"react-native": "^0.49.1",
"react-native-vector-icons": "^4.0.0",
"react-navigation": "^1.0.0-beta.9",
"react-redux": "^5.0.3",
"react-test-renderer": "16.0.0-alpha.12",
"redux": "^3.6.0",
"redux-logger": "^2.8.2",
"redux-persist": "^4.4.2",

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import some from 'lodash.some';
import { connect } from 'react-redux';
import Toast from 'react-native-simple-toast';
// import Toast from 'react-native-simple-toast';
import { runTests } from '../tests/index';
import RunStatus from '../../lib/RunStatus';
@@ -26,7 +26,7 @@ class OverviewControlButton extends Component {
handleOnPress() {
const { focusedTestIds, pendingTestIds, tests } = this.props;
runTests(tests, { focusedTestIds, pendingTestIds });
Toast.show('Running all suite tests.');
// Toast.show('Running all suite tests.');
}
render() {

View File

@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Toast from 'react-native-simple-toast';
// import Toast from 'react-native-simple-toast';
import RunStatus from '../../lib/RunStatus';
import { runTest } from '../tests/index';
@@ -24,7 +24,7 @@ class TestControlButton extends Component {
const { test: { id, description } } = this.props;
runTest(id);
Toast.show(`Running ${description}.`);
// Toast.show(`Running ${description}.`);
}
render() {

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Toast from 'react-native-simple-toast';
// import Toast from 'react-native-simple-toast';
import RunStatus from '../../lib/RunStatus';
import { runTests } from '../tests/index';
@@ -28,7 +28,7 @@ class TestSuiteControlButton extends Component {
runTests(testSuiteTests, { focusedTestIds, pendingTestIds });
Toast.show(`Running ${name} tests.`);
// Toast.show(`Running ${name} tests.`);
}
toggleOnlyShowFailingTests() {

View File

@@ -31,7 +31,7 @@ class CoreContainer extends React.Component {
NetInfo.isConnected.fetch().then((isConnected) => {
this.handleAppStateChange('active'); // Force connect (react debugger issue)
this.props.dispatch(setNetworkState(isConnected));
NetInfo.isConnected.addEventListener('change', this.handleNetworkChange);
NetInfo.isConnected.addEventListener('connectionChange', this.handleNetworkChange);
});
}
@@ -40,7 +40,7 @@ class CoreContainer extends React.Component {
*/
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppStateChange);
NetInfo.isConnected.removeEventListener('change', this.handleNetworkChange);
NetInfo.isConnected.removeEventListener('connectionChange', this.handleNetworkChange);
}
props: Props;

249
tests/src/phone-auth.js Normal file
View File

@@ -0,0 +1,249 @@
import React, { Component } from 'react';
import { View, Button, Text, TextInput, Image, ActivityIndicator, Platform } from 'react-native';
import fb from './firebase';
const firebase = fb.native;
const imageUrl = 'https://www.shareicon.net/data/512x512/2016/07/19/798524_sms_512x512.png';
export default class PhoneAuth extends Component {
static getDefaultState() {
return {
message: '',
error: '',
codeInput: '',
phoneNumber: '+44',
auto: Platform.OS === 'android',
autoVerifyCountDown: 0,
sent: false,
started: false,
user: null,
};
}
constructor(props) {
super(props);
this.timeout = 20;
this._autoVerifyInterval = null;
this.state = PhoneAuth.getDefaultState();
}
_tick() {
this.setState({
autoVerifyCountDown: this.state.autoVerifyCountDown - 1,
});
}
/**
* Called when confirm code is pressed - we should have the code and verificationId now in state.
*/
afterVerify = () => {
const { codeInput, verificationId } = this.state;
const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, codeInput);
// TODO do something with credential for example:
firebase.auth()
.signInWithCredential(credential)
.then((user) => {
console.log('PHONE AUTH USER ->>>>>', user.toJSON());
this.setState({ user: user.toJSON() });
}).catch(console.error);
};
signIn = () => {
const { phoneNumber } = this.state;
this.setState({ message: 'Sending code ...', error: '', started: true, autoVerifyCountDown: this.timeout }, () => {
firebase.auth()
.verifyPhoneNumber(phoneNumber)
.on('state_changed', (phoneAuthSnapshot) => {
console.log(phoneAuthSnapshot);
switch (phoneAuthSnapshot.state) {
case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent'
// update state with code sent and if android start a interval timer
// for auto verify - to provide visual feedback
this.setState({
sent: true,
message: 'Code Sent!',
verificationId: phoneAuthSnapshot.verificationId,
autoVerifyCountDown: this.timeout,
}, () => {
if (this.state.auto) {
this._autoVerifyInterval = setInterval(this._tick.bind(this), 1000);
}
});
break;
case firebase.auth.PhoneAuthState.ERROR: // or 'error'
// restart the phone flow again on error
clearInterval(this._autoVerifyInterval);
this.setState({
...PhoneAuth.getDefaultState(),
error: phoneAuthSnapshot.error.message,
});
break;
// ---------------------
// ANDROID ONLY EVENTS
// ---------------------
case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout'
clearInterval(this._autoVerifyInterval);
this.setState({
sent: true,
auto: false,
verificationId: phoneAuthSnapshot.verificationId,
});
break;
case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified'
clearInterval(this._autoVerifyInterval);
this.setState({
sent: true,
codeInput: phoneAuthSnapshot.code,
verificationId: phoneAuthSnapshot.verificationId,
});
break;
default:
// will never get here - just for linting
}
});
});
};
renderInputPhoneNumber() {
const { phoneNumber } = this.state;
return (
<View style={{ flex: 1 }}>
<Text>Enter phone number:</Text>
<TextInput
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ phoneNumber: value })}
placeholder={'Phone number ... '}
value={phoneNumber}
/>
<Button title="Begin Verification" color="green" onPress={this.signIn} />
</View>
);
}
renderSendingCode() {
const { phoneNumber } = this.state;
return (
<View style={{ paddingBottom: 15 }}>
<Text
style={{ paddingBottom: 25 }}
>
{`Sending verification code to '${phoneNumber}'.`}
</Text>
<ActivityIndicator animating style={{ padding: 50 }} size={'large'} />
</View>
);
}
renderAutoVerifyProgress() {
const { autoVerifyCountDown, started, error, sent, phoneNumber } = this.state;
if (!sent && started && !error.length) {
return this.renderSendingCode();
}
return (
<View style={{ padding: 0 }}>
<Text
style={{ paddingBottom: 25 }}
>
{`Verification code has been successfully sent to '${phoneNumber}'.`}
</Text>
<Text
style={{ marginBottom: 25 }}
>
{`We'll now attempt to automatically verify the code for you. This will timeout in ${autoVerifyCountDown} seconds.`}
</Text>
<Button
style={{ paddingTop: 25 }} title="I have a code already" color="green"
onPress={() => this.setState({ auto: false })}
/>
</View>
);
}
renderError() {
const { error } = this.state;
return (
<View style={{ padding: 10, borderRadius: 5, margin: 10, backgroundColor: 'rgb(255,0,0)' }}>
<Text
style={{ color: '#fff' }}
>
{error}
</Text>
</View>
);
}
render() {
const { started, error, codeInput, sent, auto, user } = this.state;
return (
<View style={{ flex: 1, backgroundColor: user ? 'rgb(0, 200, 0)' : '#fff' }}>
<View
style={{
padding: 5,
justifyContent: 'center',
alignItems: 'center',
flex: 1,
}}
>
<Image source={{ uri: imageUrl }} style={{ width: 128, height: 128, marginTop: 25, marginBottom: 15 }} />
<Text style={{ fontSize: 25, marginBottom: 20 }}>Phone Auth Example</Text>
{error && error.length ? this.renderError() : null}
{!started && !sent ? this.renderInputPhoneNumber() : null}
{started && auto && !codeInput.length ? this.renderAutoVerifyProgress() : null}
{!user && started && sent && (codeInput.length || !auto) ? (
<View style={{ marginTop: 15 }}>
<Text>Enter verification code below:</Text>
<TextInput
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ codeInput: value })}
placeholder={'Code ... '}
value={codeInput}
/>
<Button title="Confirm Code" color="#841584" onPress={this.afterVerify} />
</View>
) : null}
{user ? (
<View style={{ marginTop: 15 }}>
<Text>{`Signed in with new user id: '${user.uid}'`}</Text>
</View>
) : null}
</View>
</View>
);
}
}
/*
{ user ? (
<View
style={{
padding: 15,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#77dd77',
flex: 1,
}}
>
<Image source={{ uri: successImageUri }} style={{ width: 100, height: 100, marginBottom: 25 }} />
<Text style={{ fontSize: 25 }}>Signed In!</Text>
<Text>{JSON.stringify(user)}</Text>
</View>
) : null}
*/
// Example usage if handling here and not in optionalCompleteCb:
// const { verificationId, code } = phoneAuthSnapshot;
// const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, code);
// Do something with your new credential, e.g.:
// firebase.auth().signInWithCredential(credential);
// firebase.auth().linkWithCredential(credential);
// etc ...

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, Text, ListView, TouchableHighlight } from 'react-native';
import { connect } from 'react-redux';
import some from 'lodash.some';

View File

@@ -1,4 +1,6 @@
import React, { PropTypes } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, Text, ListView, TouchableHighlight } from 'react-native';
import { connect } from 'react-redux';

View File

@@ -1,4 +1,6 @@
import React, { PropTypes } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, Text, ScrollView } from 'react-native';
import { connect } from 'react-redux';
import { js_beautify as beautify } from 'js-beautify';

View File

@@ -58,16 +58,17 @@ function coreTests({ describe, it }) {
return Promise.resolve();
});
it('it should initialize dynamic apps', () => {
return RNFirebase
.initializeApp(Platform.OS === 'ios' ? iosTestConfig : androidTestConfig, 'testsCoreApp')
.onReady()
.then((newApp) => {
newApp.name.should.equal('TESTSCOREAPP');
newApp.options.apiKey.should.equal((Platform.OS === 'ios' ? iosTestConfig : androidTestConfig).apiKey);
return newApp.delete();
});
});
// TODO add back in when android sdk support becomes available
// it('it should initialize dynamic apps', () => {
// return RNFirebase
// .initializeApp(Platform.OS === 'ios' ? iosTestConfig : androidTestConfig, 'testsCoreApp')
// .onReady()
// .then((newApp) => {
// newApp.name.should.equal('TESTSCOREAPP');
// newApp.options.apiKey.should.equal((Platform.OS === 'ios' ? iosTestConfig : androidTestConfig).apiKey);
// return newApp.delete();
// });
// });
});
}

View File

@@ -0,0 +1,385 @@
import sinon from 'sinon';
import 'should-sinon';
import should from 'should';
function collectionReferenceTests({ describe, it, context, firebase }) {
describe('CollectionReference', () => {
context('class', () => {
it('should return instance methods', () => {
return new Promise((resolve) => {
const collection = firebase.native.firestore().collection('collection-tests');
collection.should.have.property('firestore');
// TODO: Remaining checks
resolve();
});
});
});
context('add()', () => {
it('should create Document', () => {
return firebase.native.firestore()
.collection('collection-tests')
.add({ first: 'Ada', last: 'Lovelace', born: 1815 })
.then(async (docRef) => {
const doc = await firebase.native.firestore().doc(docRef.path).get();
doc.data().first.should.equal('Ada');
});
});
});
context('doc()', () => {
it('should create DocumentReference with correct path', () => {
return new Promise((resolve) => {
const docRef = firebase.native.firestore().collection('collection-tests').doc('doc');
should.equal(docRef.path, 'collection-tests/doc');
resolve();
});
});
});
context('get()', () => {
it('should retrieve a single document', () => {
return firebase.native.firestore()
.collection('collection-tests')
.get()
.then((querySnapshot) => {
should.equal(querySnapshot.size, 1);
querySnapshot.forEach((documentSnapshot) => {
should.equal(documentSnapshot.data().baz, true);
});
});
});
});
context('onSnapshot()', () => {
it('calls callback with the initial data and then when document changes', () => {
return new Promise(async (resolve) => {
const collectionRef = firebase.native.firestore().collection('document-tests');
const currentDocValue = { name: 'doc1' };
const newDocValue = { name: 'updated' };
const callback = sinon.spy();
// Test
let unsubscribe;
await new Promise((resolve2) => {
unsubscribe = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callback(doc.data()));
resolve2();
});
});
callback.should.be.calledWith(currentDocValue);
const docRef = firebase.native.firestore().doc('document-tests/doc1');
await docRef.set(newDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
// Assertions
callback.should.be.calledWith(newDocValue);
callback.should.be.calledTwice();
// Tear down
unsubscribe();
resolve();
});
});
});
context('onSnapshot()', () => {
it('calls callback with the initial data and then when document is added', () => {
return new Promise(async (resolve) => {
const collectionRef = firebase.native.firestore().collection('document-tests');
const currentDocValue = { name: 'doc1' };
const newDocValue = { name: 'updated' };
const callback = sinon.spy();
// Test
let unsubscribe;
await new Promise((resolve2) => {
unsubscribe = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callback(doc.data()));
resolve2();
});
});
callback.should.be.calledWith(currentDocValue);
const docRef = firebase.native.firestore().doc('document-tests/doc2');
await docRef.set(newDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
// Assertions
callback.should.be.calledWith(currentDocValue);
callback.should.be.calledWith(newDocValue);
callback.should.be.calledThrice();
// Tear down
unsubscribe();
resolve();
});
});
});
context('onSnapshot()', () => {
it('doesn\'t call callback when the ref is updated with the same value', async () => {
return new Promise(async (resolve) => {
const collectionRef = firebase.native.firestore().collection('document-tests');
const currentDocValue = { name: 'doc1' };
const callback = sinon.spy();
// Test
let unsubscribe;
await new Promise((resolve2) => {
unsubscribe = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callback(doc.data()));
resolve2();
});
});
callback.should.be.calledWith(currentDocValue);
const docRef = firebase.native.firestore().doc('document-tests/doc1');
await docRef.set(currentDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
// Assertions
callback.should.be.calledOnce(); // Callback is not called again
// Tear down
unsubscribe();
resolve();
});
});
});
context('onSnapshot()', () => {
it('allows binding multiple callbacks to the same ref', () => {
return new Promise(async (resolve) => {
// Setup
const collectionRef = firebase.native.firestore().collection('document-tests');
const currentDocValue = { name: 'doc1' };
const newDocValue = { name: 'updated' };
const callbackA = sinon.spy();
const callbackB = sinon.spy();
// Test
let unsubscribeA;
let unsubscribeB;
await new Promise((resolve2) => {
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callbackA(doc.data()));
resolve2();
});
});
await new Promise((resolve2) => {
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callbackB(doc.data()));
resolve2();
});
});
callbackA.should.be.calledWith(currentDocValue);
callbackA.should.be.calledOnce();
callbackB.should.be.calledWith(currentDocValue);
callbackB.should.be.calledOnce();
const docRef = firebase.native.firestore().doc('document-tests/doc1');
await docRef.set(newDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackA.should.be.calledWith(newDocValue);
callbackB.should.be.calledWith(newDocValue);
callbackA.should.be.calledTwice();
callbackB.should.be.calledTwice();
// Tear down
unsubscribeA();
unsubscribeB();
resolve();
});
});
});
context('onSnapshot()', () => {
it('listener stops listening when unsubscribed', () => {
return new Promise(async (resolve) => {
// Setup
const collectionRef = firebase.native.firestore().collection('document-tests');
const currentDocValue = { name: 'doc1' };
const newDocValue = { name: 'updated' };
const callbackA = sinon.spy();
const callbackB = sinon.spy();
// Test
let unsubscribeA;
let unsubscribeB;
await new Promise((resolve2) => {
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callbackA(doc.data()));
resolve2();
});
});
await new Promise((resolve2) => {
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
snapshot.forEach(doc => callbackB(doc.data()));
resolve2();
});
});
callbackA.should.be.calledWith(currentDocValue);
callbackA.should.be.calledOnce();
callbackB.should.be.calledWith(currentDocValue);
callbackB.should.be.calledOnce();
const docRef = firebase.native.firestore().doc('document-tests/doc1');
await docRef.set(newDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackA.should.be.calledWith(newDocValue);
callbackB.should.be.calledWith(newDocValue);
callbackA.should.be.calledTwice();
callbackB.should.be.calledTwice();
// Unsubscribe A
unsubscribeA();
await docRef.set(currentDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackB.should.be.calledWith(currentDocValue);
callbackA.should.be.calledTwice();
callbackB.should.be.calledThrice();
// Unsubscribe B
unsubscribeB();
await docRef.set(newDocValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackA.should.be.calledTwice();
callbackB.should.be.calledThrice();
resolve();
});
});
});
// Where
context('where()', () => {
it('correctly handles == boolean values', () => {
return firebase.native.firestore()
.collection('collection-tests')
.where('baz', '==', true)
.get()
.then((querySnapshot) => {
should.equal(querySnapshot.size, 1);
querySnapshot.forEach((documentSnapshot) => {
should.equal(documentSnapshot.data().baz, true);
});
});
});
it('correctly handles == string values', () => {
return firebase.native.firestore()
.collection('collection-tests')
.where('foo', '==', 'bar')
.get()
.then((querySnapshot) => {
should.equal(querySnapshot.size, 1);
querySnapshot.forEach((documentSnapshot) => {
should.equal(documentSnapshot.data().foo, 'bar');
});
});
});
it('correctly handles == null values', () => {
return firebase.native.firestore()
.collection('collection-tests')
.where('naz', '==', null)
.get()
.then((querySnapshot) => {
should.equal(querySnapshot.size, 1);
querySnapshot.forEach((documentSnapshot) => {
should.equal(documentSnapshot.data().naz, null);
});
});
});
it('correctly handles >= number values', () => {
return firebase.native.firestore()
.collection('collection-tests')
.where('daz', '>=', 123)
.get()
.then((querySnapshot) => {
should.equal(querySnapshot.size, 1);
querySnapshot.forEach((documentSnapshot) => {
should.equal(documentSnapshot.data().daz, 123);
});
});
});
it('correctly handles <= float values', () => {
return firebase.native.firestore()
.collection('collection-tests')
.where('gaz', '<=', 12.1234666)
.get()
.then((querySnapshot) => {
should.equal(querySnapshot.size, 1);
querySnapshot.forEach((documentSnapshot) => {
should.equal(documentSnapshot.data().gaz, 12.1234567);
});
});
});
});
});
}
export default collectionReferenceTests;

View File

@@ -0,0 +1,299 @@
import sinon from 'sinon';
import 'should-sinon';
import should from 'should';
function collectionReferenceTests({ describe, it, context, firebase }) {
describe('DocumentReference', () => {
context('class', () => {
it('should return instance methods', () => {
return new Promise((resolve) => {
const document = firebase.native.firestore().doc('document-tests/doc1');
document.should.have.property('firestore');
// TODO: Remaining checks
resolve();
});
});
});
context('delete()', () => {
it('should delete Document', () => {
return firebase.native.firestore()
.doc('document-tests/doc1')
.delete()
.then(async () => {
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
should.equal(doc.exists, false);
});
});
});
context('onSnapshot()', () => {
it('calls callback with the initial data and then when value changes', () => {
return new Promise(async (resolve) => {
const docRef = firebase.native.firestore().doc('document-tests/doc1');
const currentDataValue = { name: 'doc1' };
const newDataValue = { name: 'updated' };
const callback = sinon.spy();
// Test
let unsubscribe;
await new Promise((resolve2) => {
unsubscribe = docRef.onSnapshot((snapshot) => {
callback(snapshot.data());
resolve2();
});
});
callback.should.be.calledWith(currentDataValue);
// Update the document
await docRef.set(newDataValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
// Assertions
callback.should.be.calledWith(newDataValue);
callback.should.be.calledTwice();
// Tear down
unsubscribe();
resolve();
});
});
});
context('onSnapshot()', () => {
it('doesn\'t call callback when the ref is updated with the same value', async () => {
return new Promise(async (resolve) => {
const docRef = firebase.native.firestore().doc('document-tests/doc1');
const currentDataValue = { name: 'doc1' };
const callback = sinon.spy();
// Test
let unsubscribe;
await new Promise((resolve2) => {
unsubscribe = docRef.onSnapshot((snapshot) => {
callback(snapshot.data());
resolve2();
});
});
callback.should.be.calledWith(currentDataValue);
await docRef.set(currentDataValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
// Assertions
callback.should.be.calledOnce(); // Callback is not called again
// Tear down
unsubscribe();
resolve();
});
});
});
context('onSnapshot()', () => {
it('allows binding multiple callbacks to the same ref', () => {
return new Promise(async (resolve) => {
// Setup
const docRef = firebase.native.firestore().doc('document-tests/doc1');
const currentDataValue = { name: 'doc1' };
const newDataValue = { name: 'updated' };
const callbackA = sinon.spy();
const callbackB = sinon.spy();
// Test
let unsubscribeA;
let unsubscribeB;
await new Promise((resolve2) => {
unsubscribeA = docRef.onSnapshot((snapshot) => {
callbackA(snapshot.data());
resolve2();
});
});
await new Promise((resolve2) => {
unsubscribeB = docRef.onSnapshot((snapshot) => {
callbackB(snapshot.data());
resolve2();
});
});
callbackA.should.be.calledWith(currentDataValue);
callbackA.should.be.calledOnce();
callbackB.should.be.calledWith(currentDataValue);
callbackB.should.be.calledOnce();
await docRef.set(newDataValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackA.should.be.calledWith(newDataValue);
callbackB.should.be.calledWith(newDataValue);
callbackA.should.be.calledTwice();
callbackB.should.be.calledTwice();
// Tear down
unsubscribeA();
unsubscribeB();
resolve();
});
});
});
context('onSnapshot()', () => {
it('listener stops listening when unsubscribed', () => {
return new Promise(async (resolve) => {
// Setup
const docRef = firebase.native.firestore().doc('document-tests/doc1');
const currentDataValue = { name: 'doc1' };
const newDataValue = { name: 'updated' };
const callbackA = sinon.spy();
const callbackB = sinon.spy();
// Test
let unsubscribeA;
let unsubscribeB;
await new Promise((resolve2) => {
unsubscribeA = docRef.onSnapshot((snapshot) => {
callbackA(snapshot.data());
resolve2();
});
});
await new Promise((resolve2) => {
unsubscribeB = docRef.onSnapshot((snapshot) => {
callbackB(snapshot.data());
resolve2();
});
});
callbackA.should.be.calledWith(currentDataValue);
callbackA.should.be.calledOnce();
callbackB.should.be.calledWith(currentDataValue);
callbackB.should.be.calledOnce();
await docRef.set(newDataValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackA.should.be.calledWith(newDataValue);
callbackB.should.be.calledWith(newDataValue);
callbackA.should.be.calledTwice();
callbackB.should.be.calledTwice();
// Unsubscribe A
unsubscribeA();
await docRef.set(currentDataValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackB.should.be.calledWith(currentDataValue);
callbackA.should.be.calledTwice();
callbackB.should.be.calledThrice();
// Unsubscribe B
unsubscribeB();
await docRef.set(newDataValue);
await new Promise((resolve2) => {
setTimeout(() => resolve2(), 5);
});
callbackA.should.be.calledTwice();
callbackB.should.be.calledThrice();
resolve();
});
});
});
context('set()', () => {
it('should create Document', () => {
return firebase.native.firestore()
.doc('document-tests/doc2')
.set({ name: 'doc2', testArray: [] })
.then(async () => {
const doc = await firebase.native.firestore().doc('document-tests/doc2').get();
doc.data().name.should.equal('doc2');
});
});
});
context('set()', () => {
it('should merge Document', () => {
return firebase.native.firestore()
.doc('document-tests/doc1')
.set({ merge: 'merge' }, { merge: true })
.then(async () => {
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
doc.data().name.should.equal('doc1');
doc.data().merge.should.equal('merge');
});
});
});
context('set()', () => {
it('should overwrite Document', () => {
return firebase.native.firestore()
.doc('document-tests/doc1')
.set({ name: 'overwritten' })
.then(async () => {
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
doc.data().name.should.equal('overwritten');
});
});
});
context('update()', () => {
it('should update Document', () => {
return firebase.native.firestore()
.doc('document-tests/doc1')
.set({ name: 'updated' })
.then(async () => {
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
doc.data().name.should.equal('updated');
});
});
});
});
}
export default collectionReferenceTests;

View File

@@ -0,0 +1,63 @@
import should from 'should';
function firestoreTests({ describe, it, context, firebase }) {
describe('firestore()', () => {
context('collection()', () => {
it('should create CollectionReference with the right id', () => {
return new Promise((resolve) => {
const collectionRef = firebase.native.firestore().collection('collection1/doc1/collection2');
should.equal(collectionRef.id, 'collection2');
resolve();
});
});
});
context('doc()', () => {
it('should create DocumentReference with correct path', () => {
return new Promise((resolve) => {
const docRef = firebase.native.firestore().doc('collection1/doc1/collection2/doc2');
should.equal(docRef.path, 'collection1/doc1/collection2/doc2');
resolve();
});
});
});
context('batch()', () => {
it('should create / update / delete as expected', () => {
const ayRef = firebase.native.firestore().collection('firestore-tests').doc('AY');
const lRef = firebase.native.firestore().collection('firestore-tests').doc('LON');
const nycRef = firebase.native.firestore().collection('firestore-tests').doc('NYC');
const sfRef = firebase.native.firestore().collection('firestore-tests').doc('SF');
return firebase.native.firestore()
.batch()
.set(ayRef, { name: 'Aylesbury' })
.set(lRef, { name: 'London' })
.set(nycRef, { name: 'New York City' })
.set(sfRef, { name: 'San Francisco' })
.update(nycRef, { population: 1000000 })
.update(sfRef, { name: 'San Fran' })
.set(lRef, { population: 3000000 }, { merge: true })
.delete(ayRef)
.commit()
.then(async () => {
const ayDoc = await ayRef.get();
should.equal(ayDoc.exists, false);
const lDoc = await lRef.get();
lDoc.data().name.should.equal('London');
lDoc.data().population.should.equal(3000000);
const nycDoc = await nycRef.get();
nycDoc.data().name.should.equal('New York City');
nycDoc.data().population.should.equal(1000000);
const sfDoc = await sfRef.get();
sfDoc.data().name.should.equal('San Fran');
});
});
});
});
}
export default firestoreTests;

View File

@@ -0,0 +1,66 @@
import firebase from '../../firebase';
import TestSuite from '../../../lib/TestSuite';
/*
Test suite files
*/
import collectionReferenceTests from './collectionReferenceTests';
import documentReferenceTests from './documentReferenceTests';
import firestoreTests from './firestoreTests';
const suite = new TestSuite('Firestore', 'firebase.firestore()', firebase);
const testGroups = [
collectionReferenceTests,
documentReferenceTests,
firestoreTests,
];
function firestoreTestSuite(testSuite) {
testSuite.beforeEach(async () => {
this.collectionTestsCollection = testSuite.firebase.native.firestore().collection('collection-tests');
this.documentTestsCollection = testSuite.firebase.native.firestore().collection('document-tests');
this.firestoreTestsCollection = testSuite.firebase.native.firestore().collection('firestore-tests');
// Clean the collections in case the last run failed
await cleanCollection(this.collectionTestsCollection);
await cleanCollection(this.documentTestsCollection);
await cleanCollection(this.firestoreTestsCollection);
await this.collectionTestsCollection.add({
baz: true,
daz: 123,
foo: 'bar',
gaz: 12.1234567,
naz: null,
});
await this.documentTestsCollection.doc('doc1').set({
name: 'doc1',
});
});
testSuite.afterEach(async () => {
await cleanCollection(this.collectionTestsCollection);
await cleanCollection(this.documentTestsCollection);
await cleanCollection(this.firestoreTestsCollection);
});
testGroups.forEach((testGroup) => {
testGroup(testSuite);
});
}
/*
Register tests with test suite
*/
suite.addTests(firestoreTestSuite);
export default suite;
/* HELPER FUNCTIONS */
async function cleanCollection(collection) {
const collectionTestsDocs = await collection.get();
const tasks = [];
collectionTestsDocs.forEach(doc => tasks.push(doc.ref.delete()));
await Promise.all(tasks);
}

Some files were not shown because too many files have changed in this diff Show More