Merge remote-tracking branch 'upstream/master'

This commit is contained in:
taljacobson 2017-05-07 12:27:37 +03:00
commit 0177a9f1c2
61 changed files with 796 additions and 440 deletions

19
.gitignore vendored
View File

@ -19,6 +19,9 @@ xcuserdata/
# Crashlytics configuations
android/com_crashlytics_export_strings.xml
# Signing files
android/.signing/
# Local configuration file (sdk path, etc)
**/android/local.properties
@ -54,12 +57,24 @@ ehthumbs.db
Thumbs.dbandroid/gradle
android/gradlew
android/build
android/.gradle
android/gradlew.bat
android/gradle/
.idea
android/gradle
lib/.watchmanconfig
.idea
coverage
yarn.lock
tests/build
tests/ios/Podfile.lock
tests/android/app/build
tests/ios/Pods
tests/ios/Podfile.lock
tests/firebase
.gradle
local.properties
*.iml
**/ios/Pods/**
**/ios/ReactNativeFirebaseDemo.xcworkspace/

View File

@ -61,3 +61,5 @@ docs
coverage
yarn.lock
tests
lib/.watchmanconfig

View File

@ -1,4 +1,4 @@
# React Native Firebase<img align="left" src="http://i.imgur.com/01XQL0x.png">
# React Native Firebase<a href="https://invertase.io/react-native-firebase"><img align="left" src="http://i.imgur.com/01XQL0x.png"></a>
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/t6bdqMs)
[![Gitter](https://badges.gitter.im/invertase/react-native-firebase.svg)](https://gitter.im/invertase/react-native-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@ -6,15 +6,19 @@
[![License](https://img.shields.io/npm/l/react-native-firebase.svg)](/LICENSE)
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
> [Documentation](https://invertase.io/react-native-firebase)
<hr>
### Install
```
```bash
npm i react-native-firebase --save
```
#### Platform specific setup guides:
[![ios](https://a.fsdn.com/sd/topics/ios_64.png)](docs/installation.ios.md) [![android](https://a.fsdn.com/sd/topics/android_64.png)](docs/installation.android.md)
[![ios](https://a.fsdn.com/sd/topics/ios_64.png)](http://invertase.io/react-native-firebase/#/installation-ios) [![android](https://a.fsdn.com/sd/topics/android_64.png)](http://invertase.io/react-native-firebase/#/installation-android)
<hr>
@ -30,47 +34,6 @@ The native SDKs also allow us to hook into device sdk's which are not possible w
<hr>
### Test app
To help ensure changes and features work across both iOS & Android, we've developed an app specifically to test `react-native-firebase` against the [`firebase` web SDK](https://www.npmjs.com/package/firebase). Please see the [`tests`](tests/README.md) directory for more information.
<hr>
### Examples app
There's currently a work in progress [examples app](https://github.com/invertase/react-native-firebase-examples) which aims to demonstrate various real world use-case scenarios with React Native & Firebase. We welcome any new examples or updates to existing ones.
<hr>
### Documentation
RNFirebase aims to replicate the Firebase Web SDK as closely as possible. Because of this, the documentation focuses around the installation, differences & best practices of this library. Please see the [Firebase Web SDK](https://firebase.google.com/docs/reference/js/) documentation for Firebase functionality.
> If you find any discrepancies between the two libraries, please raise an issue or PR.
* [Firebase Setup](docs/firebase-setup.md)
* API
* [Authentication](docs/api/authentication.md)
* [Realtime Database](docs/api/database.md)
* [Analytics](docs/api/analytics.md)
* [Storage](docs/api/storage.md)
* [Messaging](docs/api/cloud-messaging.md)
* [Crash](docs/api/crash.md)
* [Transactions](docs/api/transactions.md)
* [FAQs / Troubleshooting](docs/faqs.md)
<hr>
### Contributing
We welcome any contribution to the repository. Please ensure your changes to the JavaScript code follow the styling guides controlled by ESlint. Changes to native code should be kept clean and follow the standard of existing code.
Changes to existing code should ensure all relevant tests on the test app pass. Any new features should have new tests created and ensure all existing tests pass.
**Project board:** https://github.com/invertase/react-native-firebase/projects
<hr>
### License
- MIT

View File

@ -6,7 +6,7 @@ Pod::Spec.new do |s|
s.version = package["version"]
s.summary = package["description"]
s.description = <<-DESC
Wanna integrate firebase into your app using React Native?
Integrate firebase into your app using the React Native SDKs.
DESC
s.homepage = "http://invertase.io"
s.license = package['license']
@ -16,11 +16,4 @@ Pod::Spec.new do |s|
s.platform = :ios, "8.0"
s.preserve_paths = 'README.md', 'package.json', '*.js'
s.source_files = 'ios/RNFirebase/*.{h,m}'
s.dependency 'React'
s.dependency 'Firebase/Auth'
s.dependency 'Firebase/Core'
s.dependency 'Firebase/Database'
s.dependency 'Firebase/Messaging'
s.dependency 'Firebase/RemoteConfig'
s.dependency 'Firebase/Storage'
end

View File

@ -213,7 +213,7 @@ public class RNFirebaseDatabaseReference {
query = query.orderByChild(key);
}
} else if ("limit".equals(type)) {
int limit = (Integer) modifier.get("limit");
int limit = ((Double)modifier.get("limit")).intValue();
if ("limitToLast".equals(name)) {
query = query.limitToLast(limit);
} else if ("limitToFirst".equals(name)) {
@ -249,46 +249,46 @@ public class RNFirebaseDatabaseReference {
if ("number".equals(valueType)) {
double value = (Double) modifier.get("value");
if (key == null) {
query = query.equalTo(value);
query = query.endAt(value);
} else {
query = query.equalTo(value, key);
query = query.endAt(value, key);
}
} else if ("boolean".equals(valueType)) {
boolean value = (Boolean) modifier.get("value");
if (key == null) {
query = query.equalTo(value);
query = query.endAt(value);
} else {
query = query.equalTo(value, key);
query = query.endAt(value, key);
}
} else if ("string".equals(valueType)) {
String value = (String) modifier.get("value");
if (key == null) {
query = query.equalTo(value);
query = query.endAt(value);
} else {
query = query.equalTo(value, key);
query = query.endAt(value, key);
}
}
} else if ("startAt".equals(name)) {
if ("number".equals(valueType)) {
double value = (Double) modifier.get("value");
if (key == null) {
query = query.equalTo(value);
query = query.startAt(value);
} else {
query = query.equalTo(value, key);
query = query.startAt(value, key);
}
} else if ("boolean".equals(valueType)) {
boolean value = (Boolean) modifier.get("value");
if (key == null) {
query = query.equalTo(value);
query = query.startAt(value);
} else {
query = query.equalTo(value, key);
query = query.startAt(value, key);
}
} else if ("string".equals(valueType)) {
String value = (String) modifier.get("value");
if (key == null) {
query = query.equalTo(value);
query = query.startAt(value);
} else {
query = query.equalTo(value, key);
query = query.startAt(value, key);
}
}
}

0
docs/.nojekyll Normal file
View File

25
docs/README.md Normal file
View File

@ -0,0 +1,25 @@
<h1 align="center">
<img src="https://camo.githubusercontent.com/6c827e5a0bb91259f82a1f4923ab7efa4891b119/687474703a2f2f692e696d6775722e636f6d2f303158514c30782e706e67"/><br>
React Native Firebase
</h1>
<div style="text-align: center;">
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/t6bdqMs)
[![Gitter](https://badges.gitter.im/invertase/react-native-firebase.svg)](https://gitter.im/invertase/react-native-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg)](https://www.npmjs.com/package/react-native-firebase)
[![License](https://img.shields.io/npm/l/react-native-firebase.svg)](/LICENSE)
<br />
A well tested Firebase implementation for React Native, supporting both iOS & Android apps.
</div>
---
RNFirebase is a _light-weight_ layer sitting on-top of the native Firebase libraries for both iOS and Android which mirrors the Firebase Web SDK as closely as possible.
Although the [Firebase Web SDK](https://www.npmjs.com/package/firebase) library will work with React Native, it is mainly built for the web.
RNFirebase provides a JavaScript bridge to the native Firebase SDKs for both iOS and Android. Firebase will run on the native thread, allowing the rest of your app to run on the [JS thread](https://facebook.github.io/react-native/docs/performance.html#javascript-frame-rate). The Firebase Web SDK also runs on the JS thread, therefore potentially affecting the frame rate causing jank with animations, touch events etc. All in all, RNFirebase provides much faster performance (~2x) over the web SDK.
The native SDKs also allow us to hook into device sdk's which are not possible with the web SDK, for example crash reporting, offline realtime database support, analyics and more!

25
docs/_sidebar.md Normal file
View File

@ -0,0 +1,25 @@
- Getting started
- [Installation - iOS](/installation-ios)
- [Installation - Android](/installation-android)
- [Firebase Setup](/firebase-setup.md)
- [Usage](/usage)
- Contributing
- [Guidelines](/contributing/guidelines)
- [Testing](/contributing/testing)
- Modules
- [Authentication](/modules/authentication)
- [Realtime Database](/modules/database)
- [Analytics](/modules/analytics)
- [Storage](/modules/storage)
- [Cloud Messaging](/modules/cloud-messaging)
- [Crash Reporting](/modules/crash)
- [Transactions](/modules/transactions)
- Other
- [Project Board](https://github.com/invertase/react-native-firebase/projects)
- [FAQs / Troubleshooting](/faqs)
- [Examples](https://github.com/invertase/react-native-firebase-examples)
- [Chat](https://discord.gg/t6bdqMs)
- [Gitter](https://gitter.im/invertase/react-native-firebase)

View File

@ -1 +0,0 @@
# Remote Config

View File

@ -1,23 +0,0 @@
# Transactions
Transactions are currently an experimental feature as they can not be integrated as easily as the other Firebase features. Please see the [Firebase documentation](https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction) for full implemtation details.
## Example
```javascript
const ref = firebase.database().ref('user/posts');
ref.transaction((posts) => {
return (posts || 0) + 1;
}, (error, committed, snapshot) => {
if (error) {
console.log('Something went wrong', error);
} else if (!committed) {
console.log('Aborted'); // Returning undefined will trigger this
} else {
console.log('User posts incremented by 1');
}
console.log('User posts is now: ', snapshot.val());
});
```

View File

@ -0,0 +1,5 @@
# Guidelines
We welcome any contribution to the repository. Please ensure your changes to the JavaScript code follow the styling guides controlled by ESlint. Changes to native code should be kept clean and follow the standard of existing code.
Changes to existing code should ensure all relevant tests on the test app pass. Any new features should have new tests created and ensure all existing tests pass.

View File

@ -0,0 +1,3 @@
# Testing

View File

@ -1,6 +1,6 @@
# FAQs / Troubleshooting
### Comparison to Firestack
## Comparison to Firestack
Firestack was a great start to integrating Firebase and React Native, however has underlying issues which needed to be fixed.
A V3 fork of Firestack was created to help address issues such as lack of standardisation with the Firebase Web SDK,
@ -10,7 +10,7 @@ too large to manage on the existing repository, whilst trying to maintain backwa
RNFirebase was re-written from the ground up, addressing these issues with core focus being around matching the Web SDK as
closely as possible and fixing the major bugs/issues along the way.
### How do I integrate Redux with RNFirebase
## How do I integrate Redux with RNFirebase
As every project has different requirements & structure, RNFirebase *currently* has no built in methods for Redux integration.
As RNFirebase can be used outside of a Components context, you do have free reign to integrate it as you see fit. For example,
@ -42,7 +42,7 @@ export function onAuthStateChanged() {
}
```
### [Android] Google Play Services related issues
## [Android] Google Play Services related issues
The firebase SDK requires a certain version of Google Play Services installed on Android in order to function properly.
@ -68,7 +68,7 @@ party emulator such as GenyMotion.
Using this kind of workaround with Google Play Services can be problematic, so we
recommend using the native Android Studio emulators to reduce the chance of these complications.
### [Android] Turning off Google Play Services availability errors
## [Android] Turning off Google Play Services availability errors
G.P.S errors can be turned off using a config option like so:
@ -79,7 +79,7 @@ const firebase = RNFirebase.initializeApp({
```
This will stop your app from immediately red-boxing or crashing, but won't solve the underlying issue of G.P.S not being available or of the correct version. This will mean certain functionalities won't work properly and your app may even crash.
### [Android] Checking for Google Play Services availability with React Native Firebase
## [Android] Checking for Google Play Services availability with React Native Firebase
React Native Firebase actually has a useful helper object for checking G.P.S availability:
@ -109,7 +109,7 @@ This error will match the messages and error codes mentioned above, and can be f
https://developers.google.com/android/reference/com/google/android/gms/common/ConnectionResult#SERVICE_VERSION_UPDATE_REQUIRED
### [Android] Duplicate Dex Files error (build time error)
## [Android] Duplicate Dex Files error (build time error)
A common build time error when using libraries that require google play services is of the form:
'Failed on android with com.android.dex.DexException: Multiple dex files... '

View File

@ -4,51 +4,17 @@ The RNFirebase library is intended on making it easy to work with [Firebase](htt
To add Firebase to your project, make sure to create a project in the [Firebase console](https://firebase.google.com/console)
![Create a new project](http://d.pr/i/17cJ2.png)
![Create a new project](https://i.imgur.com/KbbamwD.png)
Each platform uses a different setup method after creating the project.
## iOS
See the [ios setup guide](./installation.ios.md).
For iOS, ensure you've followed the instructions provided by Firebase; adding your [GoogleService-Info.plist](https://github.com/invertase/react-native-firebase/blob/master/tests/ios/GoogleService-Info.plist)
file to the project, and [configuring your AppDelegate](https://github.com/invertase/react-native-firebase/blob/master/tests/ios/ReactNativeFirebaseDemo/AppDelegate.m#L20).
## Android
See the [android setup guide](./installation.android.md).
## Usage
After creating a Firebase project and installing the library, we can use it in our project by importing the library in our JavaScript:
```javascript
import RNFirebase from 'react-native-firebase'
```
We need to tell the Firebase library we want to _configure_ the project. RNFirebase provides a way to configure both the native and the JavaScript side of the project at the same time with a single command:
```javascript
const firebase = RNFirebase.initializeApp({
// config options
});
```
### Configuration Options
| option | type | Default Value | Description |
|----------------|----------|-------------------------|----------------------------------------|
| debug | bool | false | When set to true, RNFirebase will log messages to the console and fire `debug` events we can listen to in `js` |
| persistence | bool | false | When set to true, database persistence will be enabled. |
For instance:
```javascript
import RNFirebase from 'react-native-firebase';
const configurationOptions = {
debug: true
};
const firebase = RNFirebase.initializeApp(configurationOptions);
export default firebase;
```
For Android, ensure you've followed the instructions provided by Firebase; adding your [google-services.json](https://github.com/invertase/react-native-firebase/blob/master/tests/android/app/google-services.json)
file to the project, installing the [google-services](https://github.com/invertase/react-native-firebase/blob/master/tests/android/build.gradle#L9)
plugin and applying **at the end** of your [`build.gradle`](https://github.com/invertase/react-native-firebase/blob/master/tests/android/app/build.gradle#L144).

54
docs/index.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-native-firebase - A react native firebase library supporting both android and ios native firebase SDK's</title>
<meta name="description" content="A react native firebase library supporting both android and ios native firebase SDK's">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" type="image/png" href="https://camo.githubusercontent.com/6c827e5a0bb91259f82a1f4923ab7efa4891b119/687474703a2f2f692e696d6775722e636f6d2f303158514c30782e706e67"/>
</head>
<body>
<div id="app"></div>
</body>
<script>
window.$docsify = {
name: 'react-native-firebase',
repo: 'https://github.com/invertase/react-native-firebase',
loadSidebar: true,
search: 'auto',
themeColor: '#f5820b',
subMaxLevel: 2,
maxLevel: 4,
ga: 'UA-98196653-1',
plugins: [
function (hook) {
var footer = [
'<hr/>',
'<footer>',
`<span>Caught a mistake or want to contribute to the documentation? <a href="https://github.com/invertase/react-native-firebase/tree/master/docs" target="_blank">Edit documentation on Github!</a>.</span>`,
'</footer>'
].join('');
hook.afterEach(function (html) {
return html + footer
})
}
]
}
</script>
<script>
if (typeof navigator.serviceWorker !== 'undefined') {
navigator.serviceWorker.register('sw.js')
}
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.js"></script>
<link rel="stylesheet" href="//unpkg.com/docsify/themes/vue.css">
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/ga.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-javascript.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-java.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-swift.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-objectivec.min.js"></script>
</html>

View File

@ -1,6 +1,6 @@
# Android Installation
### 1 - Setup google-services.json
## 1) Setup google-services.json
Download the `google-services.json` file provided by Firebase in the _Add Firebase to Android_ platform menu in your Firebase configuration console. This file should be downloaded to `YOUR_PROJECT/android/app/google-services.json`.
Next you'll have to add the google-services gradle plugin in order to parse it.
@ -23,7 +23,7 @@ In your app build.gradle file, add the gradle plugin at the VERY BOTTOM of the f
apply plugin: 'com.google.gms.google-services'
```
### 2 - Link RNFirebase
## 2) Link RNFirebase
To install `react-native-firebase` in your project, you'll need to import the package from `io.invertase.firebase` in your project's `android/app/src/main/java/com/[app name]/MainApplication.java` and list it as a package for ReactNative in the `getPackages()` function:
@ -61,7 +61,7 @@ include ':react-native-firebase'
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
```
### 3 - Cloud Messaging (optional)
## 3) Cloud Messaging (optional)
If you plan on using [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/), add the following to `android/app/src/main/AndroidManifest.xml`.

View File

@ -1,9 +1,9 @@
## iOS Installation
# iOS Installation
### 1 - Setup google-services.plist and dependencies
## 1) Setup google-services.plist and dependencies
Setup the `google-services.plist` file and Firebase ios frameworks first; check out the relevant Firebase docs [here](https://firebase.google.com/docs/ios/setup#frameworks).
#### 1.1 - Initialisation
### 1.1) Initialisation
Make sure you've added the following to the top of your `ios/[YOUR APP NAME]]/AppDelegate.m` file:
`#import <Firebase.h>`
@ -12,10 +12,11 @@ and this to the `didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` me
`[FIRApp configure];`
### 2 - Link RNFirebase
## 2) Link RNFirebase
There are multiple ways to install RNFirebase depending on how your project is currently setup:
#### 2.1 - Existing Cocoapods setup, including React Native as a pod
### 2.1) You already use Cocoapods and have React Native installed as a pod
Simply add the following to your `Podfile`:
```ruby
@ -33,25 +34,27 @@ pod 'Firebase/Storage'
pod 'RNFirebase', :path => '../node_modules/react-native-firebase'
```
#### 2.2 - Via react-native-cli link
### 2.2) You're not using Cocoapods or don't have React Native installed as a pod (Automatic install)
React native ships with a `link` command that can be used to link the projects together, which can help automate the process of linking our package environments.
```bash
react-native link react-native-firebase
```
#### cocoapods
We've automated the process of setting up with cocoapods. This will happen automatically upon linking the package with `react-native-cli`.
Update the newly installed pods once the linking is done:
```bash
cd ios && pod update --verbose
```
##### cocoapods
We've automated the process of setting up with cocoapods. This will happen automatically upon linking the package with `react-native-cli`.
**NOTE: You need to use the `ios/[YOUR APP NAME].xcworkspace` instead of the `ios/[YOUR APP NAME].xcproj` file from now on.**
**Remember to use the `ios/[YOUR APP NAME].xcworkspace` instead of the `ios/[YOUR APP NAME].xcproj` file from now on**.
#### 2.3 - Manually
### 2.3) You're not using Cocoapods or don't have React Native installed as a pod (Manual install)
If you prefer not to use `react-native link`, we can manually link the package together with the following steps, after `npm install`:
@ -90,4 +93,70 @@ pod 'Firebase/RemoteConfig'
pod 'Firebase/Storage'
```
Then you can run `(cd ios && pod install)` to get the pods opened. If you do use this route, remember to use the `.xcworkspace` file.
Then you can run `(cd ios && pod install)` to get the pods opened.
**NOTE: You need to use the `ios/[YOUR APP NAME].xcworkspace` instead of the `ios/[YOUR APP NAME].xcproj` file from now on.**
## 3) Cloud Messaging (optional)
If you plan on using [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) then, you need to:
**NOTE: FCM does not work on the iOS simulator, you must test is using a real device. This is a restriction enforced by Apple for some unknown reason.**
### 3.1) Set up certificates
Follow the instructions at https://firebase.google.com/docs/cloud-messaging/ios/certs
### 3.2) Enable capabilities
In Xcode, enable the following capabilities:
1) Push Notifications
2) Background modes > Remove notifications
### 3.3) Update `AppDelegate.h`
Add the following import:
`@import UserNotifications;`
Change the interface descriptor to:
`@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>`
### 3.4) Update `AppDelegate.m`
Add the following import:
`#import "RNFirebaseMessaging.h"`
Add the following to the `didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` method after `[FIRApp Configure]`:
`[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];`
Add the following methods:
```objectivec
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
[RNFirebaseMessaging willPresentNotification:notification withCompletionHandler:completionHandler];
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
{
[RNFirebaseMessaging didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[RNFirebaseMessaging didReceiveLocalNotification:notification];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
[RNFirebaseMessaging didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
```

View File

@ -1,6 +1,6 @@
# Cloud Messaging
Firebase Cloud Messaging ([FCM](https://firebase.google.com/docs/cloud-messaging/)) allows you to send push messages at no
Firebase Cloud Messaging ([FCM](https://firebase.google.com/docs/cloud-messaging/)) allows you to send push messages at no
cost to both Android & iOS platforms. Assuming the installation instructions have been followed, FCM is ready to go.
As the Firebase Web SDK has limited messaging functionality, the following methods within `react-native-firebase` have been
@ -26,7 +26,7 @@ firebase.messaging().unsubscribeFromTopic('foobar');
### getInitialNotification(): `Promise<Object>`
When the application has been opened from a notification `getInitialNotification` is called and the notification payload
When the application has been opened from a notification `getInitialNotification` is called and the notification payload
is returned. Use `onMessage` for notifications when the app is running.
```javascript

View File

@ -4,7 +4,7 @@ RNFirebase provides crash reporting for your app out of the box. Please note cra
## Manual Crash Reporting
If you want to manually report a crash, such as a pre-caught exception this is possible by using the `report` method.
If you want to manually report a crash, such as a pre-caught exception this is possible by using the `report` method.
```javascript
try {

View File

@ -72,7 +72,7 @@ class MyComponent extends Component {
### Reading data
Firstack allows the database instance to [persist on disk](https://firebase.google.com/docs/database/android/offline-capabilities) if enabled.
Firebase allows the database instance to [persist on disk](https://firebase.google.com/docs/database/android/offline-capabilities) if enabled.
To enable database persistence, pass the configuration option `persistence` before calls are made:
```javascript
@ -181,7 +181,7 @@ class ToDos extends Component {
#### Differences between `.on` & `.once`
With persistence enabled, any calls to a ref with `.once` will always read the data from disk and not contact the server.
On behavious differently, by first checking for a connection and if none exists returns the persisted data. If it successfully connects
On behaves differently, by first checking for a connection and if none exists returns the persisted data. If it successfully connects
to the server, the new data will be returned and the disk data will be updated.
The database refs has a `keepSynced()` function to tell the RNFirebase library to keep the data at the `ref` in sync.

View File

@ -3,8 +3,6 @@
RNFirebase mimics the [Web Firebase SDK Storage](https://firebase.google.com/docs/storage/web/start), whilst
providing some iOS and Android specific functionality.
All Storage operations are accessed via `storage()`.
## Uploading files
### Simple

View File

@ -0,0 +1,38 @@
# Transactions
!> Transactions is currently an experimental feature in RNFirebase. Whilst it does work there may still be some issues with it, especially around offline connectivity handling. Please report any issues in the usual manner.
?> For help on how to use firebase transactions please see the [Firebase Transaction Documentation](https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction).
### Android Implementation
The [android implementation](https://github.com/invertase/react-native-firebase/blob/master/android/src/main/java/io/invertase/firebase/database/RNFirebaseTransactionHandler.java) makes use of [Condition](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html) and [ReentrantLock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html) locks to handle transactions across the React Native Bridge.
### iOS Implementation
The [iOS implementation](https://github.com/invertase/react-native-firebase/blob/master/ios/RNFirebase/RNFirebaseDatabase.m#L279) makes use of GCD (Grand Central Dispatch) to handle transactions across the React Native Bridge without blocking the application thread. Check out [this](https://mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html) post for some 'light' reading about it.
!> Transactions that receive no response from react native's JS thread within 30 seconds are automatically aborted - this value is currently not configurable - PR welcome.
## Example
```javascript
const ref = firebase.database().ref('user/posts');
ref.transaction((posts) => {
return (posts || 0) + 1;
}, (error, committed, snapshot) => {
if (error) {
console.log('Something went wrong', error);
} else if (!committed) {
console.log('Aborted'); // Returning undefined will trigger this
} else {
console.log('User posts incremented by 1');
}
console.log('User posts is now: ', snapshot.val());
});
```

75
docs/sw.js Normal file
View File

@ -0,0 +1,75 @@
const RUNTIME = 'docsify'
const HOSTNAME_WHITELIST = [
self.location.hostname,
'fonts.gstatic.com',
'fonts.googleapis.com',
'unpkg.com'
]
// The Util Function to hack URLs of intercepted requests
const getFixedUrl = (req) => {
var now = Date.now()
var url = new URL(req.url)
// 1. fixed http URL
// Just keep syncing with location.protocol
// fetch(httpURL) belongs to active mixed content.
// And fetch(httpRequest) is not supported yet.
url.protocol = self.location.protocol
// 2. add query for caching-busting.
// Github Pages served with Cache-Control: max-age=600
// max-age on mutable content is error-prone, with SW life of bugs can even extend.
// Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
// Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
if (url.hostname === self.location.hostname) {
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
}
return url.href
}
/**
* @Lifecycle Activate
* New one activated when old isnt being used.
*
* waitUntil(): activating ====> activated
*/
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim())
})
/**
* @Functional Fetch
* All network requests are being intercepted here.
*
* void respondWith(Promise<Response> r)
*/
self.addEventListener('fetch', event => {
// Skip some of cross-origin requests, like those for Google Analytics.
if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
// Stale-while-revalidate
// similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
// Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
const cached = caches.match(event.request)
const fixedUrl = getFixedUrl(event.request)
const fetched = fetch(fixedUrl, { cache: 'no-store' })
const fetchedCopy = fetched.then(resp => resp.clone())
// Call respondWith() with whatever we get first.
// If the fetch fails (e.g disconnected), wait for the cache.
// If theres nothing in cache, wait for the fetch.
// If neither yields a response, return offline pages.
event.respondWith(
Promise.race([fetched.catch(_ => cached), cached])
.then(resp => resp || fetched)
.catch(_ => { /* eat any errors */ })
)
// Update the cache with the version we fetched (only for ok status)
event.waitUntil(
Promise.all([fetchedCopy, caches.open(RUNTIME)])
.then(([response, cache]) => response.ok && cache.put(event.request, response))
.catch(_ => { /* eat any errors */ })
)
}
})

36
docs/usage.md Normal file
View File

@ -0,0 +1,36 @@
# Usage
After creating a Firebase project and installing the library, we can use it in our project by importing the library in our JavaScript:
```javascript
import RNFirebase from 'react-native-firebase'
```
We need to tell the Firebase library we want to _configure_ the project. RNFirebase provides a way to configure both the native and the JavaScript side of the project at the same time with a single command:
```javascript
const firebase = RNFirebase.initializeApp({
// config options
});
```
## Configuration Options
| option | type | Default Value | Description |
|----------------|----------|-------------------------|----------------------------------------|
| debug | bool | false | When set to true, RNFirebase will log messages to the console and fire `debug` events we can listen to in `js` |
| persistence | bool | false | When set to true, database persistence will be enabled. |
For instance:
```javascript
import RNFirebase from 'react-native-firebase';
const configurationOptions = {
debug: true
};
const firebase = RNFirebase.initializeApp(configurationOptions);
export default firebase;
```

View File

@ -1,71 +0,0 @@
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
platform :ios, '8.0'
def common_pods
# pod 'RNFirebase', :path => '../'
# pod 'React', :path => '../node_modules/react-native'
[
'Firebase',
'Firebase/Core',
'Firebase/Auth',
'Firebase/Storage',
'Firebase/Database',
'Firebase/RemoteConfig',
'Firebase/Messaging'
].each do |lib|
pod lib
end
end
def test_pods
pod 'Quick', '~> 0.8.0'
pod 'Nimble', '~> 3.0.0'
end
def setup
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |configuration|
# configuration.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = ""
# configuration.build_settings['CODE_SIGNING_REQUIRED'] = "YES"
# configuration.build_settings['CODE_SIGNING_ALLOWED'] = "YES"
target.build_settings(configuration.name)['OTHER_LDFLAGS'] = '$(inherited)'
target.build_settings(configuration.name)['USER_HEADER_SEARCH_PATHS'] = "$(BUILT_PRODUCTS_DIR)"
target.build_settings(configuration.name)['LD_DYLIB_INSTALL_NAME'] = '@rpath/${EXECUTABLE_NAME}'
target.build_settings(configuration.name)['LD_RUNPATH_SEARCH_PATHS'] = '$(inherited) @rpath @loader_path/../Frameworks @executable_path/Frameworks'
target.build_settings(configuration.name)['ONLY_ACTIVE_ARCH'] = 'NO'
target.build_settings(configuration.name)['HEADER_SEARCH_PATHS'] = [
"$(inherited)",
"${PODS_ROOT}/Headers/**",
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../react-native/React/**",
'$(RN_ROOT)/React/**',
'$(PODS_ROOT)/Headers'
].join(' ')
target.build_settings(configuration.name)['FRAMEWORK_SEARCH_PATHS'] = [
"$(inherited)",
'$(PODS_ROOT)/**',
'$(PODS_CONFIGURATION_BUILD_DIR)/**'
].join(' ')
target.build_settings(configuration.name)['OTHER_LDFLAGS'] = "$(inherited)"
end
end
end
end
# target 'RNFirebase' do
# common_pods
# project "RNFirebase.xcodeproj"
# setup
# end
target 'RNFirebaseTests' do
use_frameworks!
common_pods
test_pods
pod 'React', :path => '../node_modules/react-native'
setup
end

View File

@ -8,3 +8,4 @@ pod 'Firebase/DynamicLinks'
pod 'Firebase/Messaging'
pod 'Firebase/RemoteConfig'
pod 'Firebase/Storage'
pod 'RNFirebase', :path => '../node_modules/react-native-firebase'

View File

@ -2,9 +2,21 @@
#define RNFirebase_h
#import <UIKit/UIKit.h>
#import "RCTBridgeModule.h"
#if __has_include(<React/RCTEventDispatcher.h>)
#import <React/RCTEventDispatcher.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventDispatcher.h"
#endif
#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventEmitter.h"
#endif
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
@interface RNFirebase : RCTEventEmitter <RCTBridgeModule> {
}

View File

@ -1,7 +1,11 @@
#ifndef RNFirebaseAnalytics_h
#define RNFirebaseAnalytics_h
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
@interface RNFirebaseAnalytics : NSObject <RCTBridgeModule> {

View File

@ -2,8 +2,16 @@
#define RNFirebaseAuth_h
#import "Firebase.h"
#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventEmitter.h"
#endif
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
@interface RNFirebaseAuth : RCTEventEmitter <RCTBridgeModule> {
FIRAuthStateDidChangeListenerHandle authListenerHandle;

View File

@ -1,7 +1,11 @@
#ifndef RNFirebaseCrash_h
#define RNFirebaseCrash_h
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
@interface RNFirebaseCrash : NSObject <RCTBridgeModule> {

View File

@ -2,8 +2,16 @@
#define RNFirebaseDatabase_h
#import "Firebase.h"
#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventEmitter.h"
#endif
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
@interface RNFirebaseDatabase : RCTEventEmitter <RCTBridgeModule> {

View File

@ -1,7 +1,11 @@
#ifndef RNFirebaseErrors_h
#define RNFirebaseErrors_h
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
#import "Firebase.h"
@interface RNFirebaseErrors : NSObject <RCTBridgeModule> {

View File

@ -5,9 +5,22 @@
#import <UIKit/UIKit.h>
#import "Firebase.h"
#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventEmitter.h"
#endif
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
#if __has_include(<React/RCTUtils.h>)
#import <React/RCTUtils.h>
#else // Compatibility for RN version < 0.40
#import "RCTUtils.h"
#endif
@import UserNotifications;

View File

@ -1,8 +1,20 @@
#import "RNFirebaseMessaging.h"
#import <React/RCTConvert.h>
#if __has_include(<React/RCTEventDispatcher.h>)
#import <React/RCTEventDispatcher.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventDispatcher.h"
#endif
#if __has_include(<React/RCTConvert.h>)
#import <React/RCTConvert.h>
#else // Compatibility for RN version < 0.40
#import "RCTConvert.h"
#endif
#if __has_include(<React/RCTUtils.h>)
#import <React/RCTUtils.h>
#else // Compatibility for RN version < 0.40
#import "RCTUtils.h"
#endif
@import UserNotifications;
#import <FirebaseMessaging/FirebaseMessaging.h>
@ -53,13 +65,13 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
content.categoryIdentifier = [RCTConvert NSString:details[@"click_action"]];
content.userInfo = details;
content.badge = [RCTConvert NSNumber:details[@"badge"]];
NSDate *fireDate = [RCTConvert NSDate:details[@"fire_date"]];
if(fireDate == nil){
return [UNNotificationRequest requestWithIdentifier:[RCTConvert NSString:details[@"id"]] content:content trigger:nil];
}
NSCalendarUnit interval = [RCTConvert NSCalendarUnit:details[@"repeat_interval"]];
NSCalendarUnit unitFlags;
switch (interval) {
@ -177,12 +189,12 @@ RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationReceived:)
name:FCMNotificationReceived
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(disconnectFCM)
name:UIApplicationDidEnterBackgroundNotification
@ -191,19 +203,19 @@ RCT_EXPORT_MODULE()
selector:@selector(connectToFCM)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(onTokenRefresh)
name:kFIRInstanceIDTokenRefreshNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(sendDataMessageFailure:)
name:FIRMessagingSendErrorNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(sendDataMessageSuccess:)
name:FIRMessagingSendSuccessNotification object:nil];
// For iOS 10 data message (sent via FCM)
dispatch_async(dispatch_get_main_queue(), ^{
[[FIRMessaging messaging] setRemoteMessageDelegate:self];
@ -283,7 +295,7 @@ RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(R
];
#endif
}
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
@ -459,22 +471,22 @@ RCT_EXPORT_METHOD(finishNotificationResponse: (NSString *)completionHandlerId){
self.notificationCallbacks[completionHandlerId] = completionHandler;
data[@"_completionHandlerId"] = completionHandlerId;
}
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:data];
}
- (void)sendDataMessageFailure:(NSNotification *)notification
{
NSString *messageID = (NSString *)notification.userInfo[@"messageID"];
NSLog(@"sendDataMessageFailure: %@", messageID);
}
- (void)sendDataMessageSuccess:(NSNotification *)notification
{
NSString *messageID = (NSString *)notification.userInfo[@"messageID"];
NSLog(@"sendDataMessageSuccess: %@", messageID);
}

View File

@ -2,8 +2,16 @@
#define RNFirebaseStorage_h
#import "Firebase.h"
#import "RCTBridgeModule.h"
#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
#else // Compatibility for RN version < 0.40
#import "RCTEventEmitter.h"
#endif
#if __has_include(<React/RCTBridgeModule.h>)
#import <React/RCTBridgeModule.h>
#else // Compatibility for RN version < 0.40
#import "RCTBridgeModule.h"
#endif
@interface RNFirebaseStorage : RCTEventEmitter <RCTBridgeModule> {

View File

@ -35,8 +35,10 @@ export default class Firebase {
_crash: ?Object;
auth: Function;
crash: Function;
storage: Function;
database: Function;
analytics: Function;
messaging: Function;
eventHandlers: Object;
@ -79,6 +81,8 @@ export default class Firebase {
this.storage = this._staticsOrInstance('storage', StorageStatics, Storage);
this.database = this._staticsOrInstance('database', DatabaseStatics, Database);
this.messaging = this._staticsOrInstance('messaging', MessagingStatics, Messaging);
this.analytics = this._staticsOrInstance('analytics', {}, Analytics);
this.crash = this._staticsOrInstance('crash', {}, Crash);
// init auth to start listeners
this.auth();
@ -107,20 +111,6 @@ export default class Firebase {
return instances[name];
}
analytics() {
if (!this._analytics) {
this._analytics = new Analytics(this);
}
return this._analytics;
}
crash() {
if (!this._crash) {
this._crash = new Crash(this);
}
return this._crash;
}
get apps(): Array<string> {
return Object.keys(instances);
}

View File

@ -83,7 +83,12 @@ export default class Database extends Base {
* @param origCB
* @returns {*}
*/
off(refId: number, listeners: Array<DatabaseListener>, remainingListenersCount: number) {
off(
refId: number,
// $FlowFixMe
listeners: Array<DatabaseListener>,
remainingListenersCount: number
) {
this.log.debug('off() : ', refId, listeners);
// Delete the reference if there are no more listeners

View File

@ -151,23 +151,25 @@ export default class Reference extends ReferenceBase {
*/
off(eventName?: string = '', origCB?: () => any) {
this.log.debug('ref.off(): ', this.refId, eventName);
// $FlowFixMe
const listeners: Array<DatabaseListener> = Object.values(this.listeners);
let listenersToRemove;
if (eventName && origCB) {
listenersToRemove = Object.values(this.listeners).filter((listener) => {
listenersToRemove = listeners.filter((listener) => {
return listener.eventName === eventName && listener.successCallback === origCB;
});
// Only remove a single listener as per the web spec
if (listenersToRemove.length > 1) listenersToRemove = [listenersToRemove[0]];
} else if (eventName) {
listenersToRemove = Object.values(this.listeners).filter((listener) => {
listenersToRemove = listeners.filter((listener) => {
return listener.eventName === eventName;
});
} else if (origCB) {
listenersToRemove = Object.values(this.listeners).filter((listener) => {
listenersToRemove = listeners.filter((listener) => {
return listener.successCallback === origCB;
});
} else {
listenersToRemove = Object.values(this.listeners);
listenersToRemove = listeners;
}
// Remove the listeners from the reference to prevent memory leaks
listenersToRemove.forEach((listener) => {
@ -183,7 +185,7 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @param applyLocally
*/
transaction(transactionUpdate: Function, onComplete, applyLocally: boolean = false) {
transaction(transactionUpdate: Function, onComplete: (?Error, boolean, ?Snapshot) => *, applyLocally: boolean = false) {
if (!isFunction(transactionUpdate)) return Promise.reject(new Error('Missing transactionUpdate function argument.'));
return new Promise((resolve, reject) => {

View File

@ -80,7 +80,7 @@ export default class StorageReference extends ReferenceBase {
* Alias to putFile
* @returns {StorageReference.putFile}
*/
get put() {
get put(): Function {
return this.putFile;
}

View File

@ -11,11 +11,13 @@ declare type UploadTaskSnapshotType = {
downloadURL: string|null,
metadata: Object, // TODO flow type def for https://firebase.google.com/docs/reference/js/firebase.storage.FullMetadata.html
ref: StorageReference,
state: StorageStatics.TaskState.RUNNING
|StorageStatics.TaskState.PAUSED
|StorageStatics.TaskState.SUCCESS
|StorageStatics.TaskState.CANCELLED
|StorageStatics.TaskState.ERROR,
state: (
typeof StorageStatics.TaskState.RUNNING
| typeof StorageStatics.TaskState.PAUSED
| typeof StorageStatics.TaskState.SUCCESS
| typeof StorageStatics.TaskState.CANCELLED
| typeof StorageStatics.TaskState.ERROR
),
task: StorageTask,
totalBytes: number,
};
@ -24,15 +26,26 @@ declare type FuncSnapshotType = null|(snapshot: UploadTaskSnapshotType) => any;
declare type FuncErrorType = null|(error: Error) => any;
declare type NextOrObserverType = null
|{ next?: FuncSnapshotType, error?: FuncErrorType, complete?:FuncSnapshotType }
|FuncSnapshotType;
declare type NextOrObserverType = null |
{
next?: FuncSnapshotType,
error?: FuncErrorType,
complete?:FuncSnapshotType
} |
FuncSnapshotType;
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.UploadTask
*/
export default class StorageTask {
constructor(type: UPLOAD_TASK|DOWNLOAD_TASK, promise: Promise, storageRef: StorageReference) {
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK
ref: StorageReference
storage: StorageReference.storage
path: StorageReference.path
then: Promise<*>
catch: () => Promise<*>
constructor(type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK, promise: Promise<*>, storageRef: StorageReference) {
this.type = type;
this.ref = storageRef;
this.storage = storageRef.storage;
@ -49,13 +62,13 @@ export default class StorageTask {
* @returns {Promise.<T>}
* @private
*/
_interceptSnapshotEvent(f: Function|null|undefined): null|() => any {
_interceptSnapshotEvent(f: ?Function): null | () => * {
if (!isFunction(f)) return null;
return (snapshot) => {
const _snapshot = Object.assign({}, snapshot);
_snapshot.task = this;
_snapshot.ref = this.ref;
return f(_snapshot);
return f && f(_snapshot);
};
}
@ -65,12 +78,13 @@ export default class StorageTask {
* @returns {*}
* @private
*/
_interceptErrorEvent(f: Function|null|undefined): null|() => any {
_interceptErrorEvent(f: ?Function): null | (Error) => * {
if (!isFunction(f)) return null;
return (error) => {
const _error = new Error(error.message);
// $FlowFixMe
_error.code = error.code;
return f(_error);
return f && f(_error);
};
}
@ -83,15 +97,41 @@ export default class StorageTask {
* @private
*/
_subscribe(nextOrObserver: NextOrObserverType, error: FuncErrorType, complete: FuncSnapshotType): Function {
const observer = isObject(nextOrObserver);
let _error;
let _next;
let _complete;
const _error = this._interceptErrorEvent(observer ? nextOrObserver.error : error);
const _next = this._interceptSnapshotEvent(observer ? nextOrObserver.next : nextOrObserver);
const _complete = this._interceptSnapshotEvent(observer ? nextOrObserver.complete : complete);
if (typeof nextOrObserver === 'function') {
_error = this._interceptErrorEvent(error);
_next = this._interceptSnapshotEvent(nextOrObserver);
_complete = this._interceptSnapshotEvent(complete);
} else if (nextOrObserver) {
_error = this._interceptErrorEvent(nextOrObserver.error);
_next = this._interceptSnapshotEvent(nextOrObserver.next);
_complete = this._interceptSnapshotEvent(nextOrObserver.complete);
}
if (_next) this.storage._addListener(this.path, StorageStatics.TaskEvent.STATE_CHANGED, _next);
if (_error) this.storage._addListener(this.path, `${this.type}_failure`, _error);
if (_complete) this.storage._addListener(this.path, `${this.type}_success`, _complete);
if (_next) {
this.storage._addListener(
this.path,
StorageStatics.TaskEvent.STATE_CHANGED,
_next
);
}
if (_error) {
this.storage._addListener(
this.path,
`${this.type}_failure`,
_error
);
}
if (_complete) {
this.storage._addListener(
this.path,
`${this.type}_success`,
_complete
);
}
return () => {
if (_next) this.storage._removeListener(this.path, StorageStatics.TaskEvent.STATE_CHANGED, _next);

View File

@ -1,6 +1,6 @@
{
"name": "react-native-firebase",
"version": "1.0.0-alpha12",
"version": "1.0.2",
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
"description": "A react native firebase library supporting both android and ios native firebase SDK's",
"main": "index",
@ -9,7 +9,12 @@
"dev": "npm run compile -- --watch",
"lint": "eslint ./src",
"publish_pages": "gh-pages -d public/",
"watchcpx": "echo 'See https://github.com/wix/wml for watching changes. \r\n'",
"tests-npm-install": "cd tests && npm install",
"tests-packager": "cd tests && npm run start",
"tests-watch-init": "wml add $(node --eval \"console.log(require('path').resolve('./lib'));\") $(node --eval \"console.log(require('path').resolve('./tests/firebase'));\")",
"tests-watch-start": "watchman watch $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml start",
"tests-watch-stop": "watchman watch-del $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml stop",
"tests-pod-install": "cd tests && npm run ios:pod:install",
"flow": "flow"
},
"repository": {
@ -72,7 +77,8 @@
"flow-bin": "^0.40.0",
"react": "^15.3.0",
"react-dom": "^15.3.0",
"react-native": "^0.42.0"
"react-native": "^0.42.0",
"wml": "0.0.82"
},
"dependencies": {
"bows": "^1.6.0",

1
tests/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__tests__/build/

View File

@ -103,6 +103,84 @@ function pendingTestTests({ it: _it, describe: _describe }) {
otherTest.should.be.called();
});
});
_describe('when an outer context is focused', () => {
_it('a pending test will still not run', async () => {
const pendingTest = sinon.spy();
const otherTest = sinon.spy();
const unfocusedTest = sinon.spy();
const testSuite = new TestSuite('', '', {});
testSuite.addTests(({ fdescribe, it, xit }) => {
fdescribe('', () => {
xit('', pendingTest);
it('', otherTest);
});
it('', unfocusedTest);
});
testSuite.setStore({
getState: () => { return {}; },
});
const testIdsToRun = Object.keys(testSuite.testDefinitions.focusedTestIds).reduce((memo, testId) => {
if (!testSuite.testDefinitions.pendingTestIds[testId]) {
memo.push(testId);
}
return memo;
}, []);
await testSuite.run(testIdsToRun);
pendingTest.should.not.be.called();
otherTest.should.be.called();
unfocusedTest.should.not.be.called();
});
});
_describe('when an outer context is focused', () => {
_it('a pending context will still not run', async () => {
const pendingTest = sinon.spy();
const otherTest = sinon.spy();
const unfocusedTest = sinon.spy();
const testSuite = new TestSuite('', '', {});
testSuite.addTests(({ fdescribe, it, xdescribe }) => {
fdescribe('', () => {
xdescribe('', () => {
it('', pendingTest);
});
it('', otherTest);
});
it('', unfocusedTest);
});
testSuite.setStore({
getState: () => { return {}; },
});
const testIdsToRun = Object.keys(testSuite.testDefinitions.focusedTestIds).reduce((memo, testId) => {
if (!testSuite.testDefinitions.pendingTestIds[testId]) {
memo.push(testId);
}
return memo;
}, []);
await testSuite.run(testIdsToRun);
pendingTest.should.not.be.called();
otherTest.should.be.called();
unfocusedTest.should.not.be.called();
});
});
}
export default pendingTestTests;

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.google.gms:google-services:3.0.0'
// NOTE: Do not place your application dependencies here; they belong

View File

@ -18,3 +18,4 @@
# org.gradle.parallel=true
android.useDeprecatedNdk=true
org.gradle.jvmargs=-Xmx1536M

View File

@ -1,6 +1,6 @@
rootProject.name = 'ReactNativeFirebaseDemo'
include ':react-native-firebase'
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, './../../android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')

0
tests/firebase/.gitkeep Normal file
View File

View File

@ -36,5 +36,6 @@ target 'ReactNativeFirebaseDemo' do
pod 'Firebase/RemoteConfig'
pod 'Firebase/Storage'
pod 'RNFirebase', :path => '../node_modules/react-native-firebase'
pod 'RNFirebase', :path => './../../'
end

View File

@ -1,31 +1,31 @@
PODS:
- Firebase/Analytics (3.15.0):
- Firebase/Analytics (3.14.0):
- Firebase/Core
- Firebase/AppIndexing (3.15.0):
- Firebase/AppIndexing (3.14.0):
- Firebase/Core
- FirebaseAppIndexing (= 1.2.0)
- Firebase/Auth (3.15.0):
- Firebase/Auth (3.14.0):
- Firebase/Core
- FirebaseAuth (= 3.1.1)
- Firebase/Core (3.15.0):
- Firebase/Core (3.14.0):
- FirebaseAnalytics (= 3.7.0)
- FirebaseCore (= 3.5.2)
- Firebase/Crash (3.15.0):
- FirebaseCore (= 3.5.1)
- Firebase/Crash (3.14.0):
- Firebase/Core
- FirebaseCrash (= 1.1.6)
- Firebase/Database (3.15.0):
- Firebase/Database (3.14.0):
- Firebase/Core
- FirebaseDatabase (= 3.1.2)
- Firebase/DynamicLinks (3.15.0):
- Firebase/DynamicLinks (3.14.0):
- Firebase/Core
- FirebaseDynamicLinks (= 1.3.4)
- Firebase/Messaging (3.15.0):
- FirebaseDynamicLinks (= 1.3.3)
- Firebase/Messaging (3.14.0):
- Firebase/Core
- FirebaseMessaging (= 1.2.2)
- Firebase/RemoteConfig (3.15.0):
- Firebase/RemoteConfig (3.14.0):
- Firebase/Core
- FirebaseRemoteConfig (= 1.3.4)
- Firebase/Storage (3.15.0):
- Firebase/Storage (3.14.0):
- Firebase/Core
- FirebaseStorage (= 1.1.0)
- FirebaseAnalytics (3.7.0):
@ -37,7 +37,7 @@ PODS:
- FirebaseAnalytics (~> 3.7)
- GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)
- GTMSessionFetcher/Core (~> 1.1)
- FirebaseCore (3.5.2):
- FirebaseCore (3.5.1):
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- FirebaseCrash (1.1.6):
- FirebaseAnalytics (~> 3.7)
@ -47,7 +47,7 @@ PODS:
- Protobuf (~> 3.1)
- FirebaseDatabase (3.1.2):
- FirebaseAnalytics (~> 3.7)
- FirebaseDynamicLinks (1.3.4):
- FirebaseDynamicLinks (1.3.3):
- FirebaseAnalytics (~> 3.7)
- FirebaseInstanceID (1.0.9)
- FirebaseMessaging (1.2.2):
@ -75,10 +75,8 @@ PODS:
- GoogleToolboxForMac/Defines (= 2.1.1)
- GoogleToolboxForMac/NSString+URLArguments (= 2.1.1)
- GoogleToolboxForMac/NSString+URLArguments (2.1.1)
- GTMSessionFetcher/Core (1.1.9)
- GTMSessionFetcher/Core (1.1.8)
- Protobuf (3.2.0)
- React (0.40.0):
- React/Core (= 0.40.0)
- React/Core (0.40.0):
- React/cxxreact
- React/yoga
@ -112,14 +110,7 @@ PODS:
- React/RCTWebSocket (0.40.0):
- React/Core
- React/yoga (0.40.0)
- RNFirebase (1.0.0-alpha12):
- Firebase/Auth
- Firebase/Core
- Firebase/Database
- Firebase/Messaging
- Firebase/RemoteConfig
- Firebase/Storage
- React
- RNFirebase (1.0.0-alpha13)
DEPENDENCIES:
- Firebase/Analytics
@ -145,33 +136,33 @@ DEPENDENCIES:
- React/RCTText (from `../node_modules/react-native`)
- React/RCTVibration (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- RNFirebase (from `../node_modules/react-native-firebase`)
- RNFirebase (from `./../../`)
EXTERNAL SOURCES:
React:
:path: "../node_modules/react-native"
:path: ../node_modules/react-native
RNFirebase:
:path: "../node_modules/react-native-firebase"
:path: ./../../
SPEC CHECKSUMS:
Firebase: 2b1cdfba1cda8589f32904a697cc753322bff9d8
Firebase: 85a581fb04e44f63ae9f4fbc8d6dabf4a4c18653
FirebaseAnalytics: 0d1b7d81d5021155be37702a94ba1ec16d45365d
FirebaseAppIndexing: d0fa52ce0ad13f4b5b2f09e4b47fb0dc2213f4e9
FirebaseAuth: cc8a1824170adbd351edb7f994490a3fb5c18be6
FirebaseCore: a024587e43778508700af8c6b1209f7c4516ba02
FirebaseCore: 225d40532489835a034b8f4e2c9c87fbf4f615a2
FirebaseCrash: db4c05d9c75baa050744d31b36357c8f1efba481
FirebaseDatabase: 05c96d7b43a7368dc91c07791adb49683e1738d1
FirebaseDynamicLinks: 30fb0856dd9ae6d8ba4da00972141a5c293a27b2
FirebaseDynamicLinks: f0d025dd29a1d70418c003344813b67ab748ffb9
FirebaseInstanceID: 2d0518b1378fe9d685ef40cbdd63d2fdc1125339
FirebaseMessaging: df8267f378580a24174ce7861233aa11d5c90109
FirebaseRemoteConfig: af3003f4e8daa2bd1d5cf90d3cccc1fe224f8ed9
FirebaseStorage: a5c55b23741a49a72af8f30f95b3bb5ddbeda12d
GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0
GTMSessionFetcher: 5c046c76a1f859bc9c187e918f18e4fc7bb57b5e
GTMSessionFetcher: 6f8d8b28b7e345549ac471071608170b31cb4977
Protobuf: 745f59e122e5de98d4d7ef291e264a0eef80f58e
React: 6dfb2f72edb1d74a800127ae157af038646673ce
RNFirebase: 228c16667a3ed1ba3b9ff0702449dca3be1c3618
RNFirebase: 46bfe1099349ac6fac8c5e57cf4f0b0f4b7938ac
PODFILE CHECKSUM: 23445e2727726988c7338fa2f396980d6fd3906f
PODFILE CHECKSUM: f8bc5de55afd159ec2faf523f1b8e0d861d0832b
COCOAPODS: 1.2.0

View File

@ -286,8 +286,7 @@ class TestRun {
suiteId: this.testSuite.id,
status: RunStatus.ERR,
time: Date.now() - this.runStartTime,
message: `Test suite failed: ${error.message}`,
stackTrace: error.stack,
message: `Test suite failed: ${error.message}`
});
});
}
@ -306,7 +305,7 @@ class TestRun {
}
async _safelyRunFunction(func, timeOutDuration, description) {
const syncResultOrPromise = tryCatcher(func);
const syncResultOrPromise = captureThrownErrors(func);
if (syncResultOrPromise.error) {
// Synchronous Error
@ -314,49 +313,59 @@ class TestRun {
}
// Asynchronous Error
return promiseToCallBack(syncResultOrPromise.value, timeOutDuration, description);
return capturePromiseErrors(syncResultOrPromise.result, timeOutDuration, description);
}
}
/**
* Try catch to object
* @returns {{}}
* Call a function and capture any errors that are immediately thrown.
* @returns {Object} Object containing result of executing the function, or the error
* message that was captured
* @private
*/
function tryCatcher(func) {
function captureThrownErrors(func) {
const result = {};
try {
result.value = func();
} catch (e) {
result.error = e;
result.result = func();
} catch (error) {
result.error = error;
}
return result;
}
/**
* Make a promise callback-able to trap errors
* @param promise
* Wraps a promise so that if it's rejected or an error is thrown while it's being
* evaluated, it's captured and thrown no further
* @param {*} target - Target to wrap. If a thenable object, it's wrapped so if it's
* rejected or an error is thrown, it will be captured. If a non-thenable object,
* wrapped in resolved promise and returned.
* @param {Number} timeoutDuration - Number of milliseconds the promise is allowed
* to pend before it's considered timed out
* @param {String} description - Description of the context the promises is defined
* in, used for reporting where a timeout occurred in the resulting error message.
* @private
*/
function promiseToCallBack(promise, timeoutDuration, description) {
function capturePromiseErrors(target, timeoutDuration, description) {
let returnValue = null;
try {
returnValue = Promise.resolve(promise)
returnValue = Promise.resolve(target)
.then(() => {
return null;
}, (error) => {
return Promise.resolve(error);
})
.timeout(timeoutDuration, `${description} took longer than ${timeoutDuration}ms. This can be extended with the timeout option.`)
.catch((error) => {
return Promise.resolve(error);
});
})
.timeout(timeoutDuration,
`${description} took longer than ${timeoutDuration}ms. This can be extended with the timeout option.`
);
} catch (error) {
returnValue = Promise.resolve(error);
}

View File

@ -111,19 +111,19 @@ class TestSuite {
*/
async run(testIds = undefined) {
const testsToRun = (() => {
if (testIds) {
return testIds.map((id) => {
const test = this.testDefinitions.tests[id];
return (testIds || Object.keys(this.testDefinitions.tests)).reduce((memo, id) => {
const test = this.testDefinitions.tests[id];
if (!test) {
throw new RangeError(`ReactNativeFirebaseTests.TestRunError: Test with id ${id} not found in test suite ${this.name}`);
}
if (!test) {
throw new RangeError(`ReactNativeFirebaseTests.TestRunError: Test with id ${id} not found in test suite ${this.name}`);
}
return test;
});
}
if (!this.testDefinitions.pendingTestIds[id]) {
memo.push(test);
}
return Object.values(this.testDefinitions.tests);
return memo;
}, []);
})();
const testRun = new TestRun(this, testsToRun.reverse(), this.testDefinitions);

View File

@ -37,7 +37,7 @@
"react-native-firebase": "file:..",
"react-native-simple-toast": "0.0.5",
"react-native-vector-icons": "^4.0.0",
"react-navigation": "^1.0.0-beta.7",
"react-navigation": "^1.0.0-beta.9",
"react-redux": "^5.0.3",
"redux": "^3.6.0",
"redux-logger": "^2.8.2",

View File

@ -14,13 +14,14 @@ export function setSuiteStatus({ suiteId, status, time, message, progress }) {
};
}
export function setTestStatus({ testId, status, time = 0, message = null }) {
export function setTestStatus({ testId, status, stackTrace, time = 0, message = null }) {
return {
type: TEST_SET_STATUS,
testId,
status,
message,
stackTrace,
time,
};
}

View File

@ -25,7 +25,7 @@ class CoreContainer extends React.Component {
StatusBar.setBackgroundColor('#0279ba');
}
if (Platform.OS === 'ios') {
StatusBar.setBarStyle('light-content')
StatusBar.setBarStyle('light-content');
}
AppState.addEventListener('change', this.handleAppStateChange);
NetInfo.isConnected.fetch().then((isConnected) => {
@ -44,6 +44,7 @@ class CoreContainer extends React.Component {
}
props: Props;
_isConnected: boolean;
/**
* Handle app state changes

View File

@ -1,5 +1,5 @@
import firebase from 'firebase';
import RNfirebase from 'react-native-firebase';
import RNfirebase from './../firebase/firebase';
import DatabaseContents from './tests/support/DatabaseContents';

View File

@ -12,6 +12,7 @@ function testsReducers(state = initState.tests, action: Object): State {
flattened[`${action.testId}.status`] = action.status;
flattened[`${action.testId}.message`] = action.message;
flattened[`${action.testId}.time`] = action.time;
flattened[`${action.testId}.stackTrace`] = action.stackTrace;
return unflatten(flattened);
}

View File

@ -13,17 +13,11 @@ class Overview extends React.Component {
// noinspection JSUnusedGlobalSymbols
static navigationOptions = {
title: 'Test Suites',
header: () => {
return {
style: { backgroundColor: '#0288d1' },
tintColor: '#ffffff',
right: (
<View style={{ marginRight: 8 }}>
<OverviewControlButton />
</View>
),
};
},
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#0288d1' },
headerRight: <View style={{ marginRight: 8 }}>
<OverviewControlButton />
</View>
};
/**

View File

@ -11,25 +11,19 @@ import TestSuiteControlButton from '../components/TestSuiteControlButton';
class Suite extends React.Component {
static navigationOptions = {
title: ({ state: { params: { title } } }) => {
return title;
},
header: ({ state: { params: { testSuiteId, onlyShowFailingTests } }, setParams }) => {
return {
style: { backgroundColor: '#0288d1' },
tintColor: '#ffffff',
right: (
<View style={{ flexDirection: 'row', marginRight: 8 }}>
<TestSuiteControlButton
testSuiteId={testSuiteId}
onlyShowFailingTests={onlyShowFailingTests}
onFilterChange={setParams}
/>
</View>
),
};
},
static navigationOptions = ({ navigation: { state: { params: { title, testSuiteId, onlyShowFailingTests } }, setParams } }) => {
return {
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#0288d1' },
headerRight: <View style={{ flexDirection: 'row', marginRight: 8 }}>
<TestSuiteControlButton
testSuiteId={testSuiteId}
onlyShowFailingTests={onlyShowFailingTests}
onFilterChange={setParams}
/>
</View>,
};
};
/**

View File

@ -9,21 +9,15 @@ import TestControlButton from '../components/TestControlButton';
class Test extends React.Component {
static navigationOptions = {
title: ({ state: { params: { title } } }) => {
return title;
},
header: ({ state: { params: { testId } } }) => {
return {
style: { backgroundColor: '#0288d1' },
tintColor: '#ffffff',
right: (
<View style={{ marginRight: 8 }}>
<TestControlButton testId={testId} />
</View>
),
};
},
static navigationOptions = ({ navigation: { state: { params: { title, testId } } } }) => {
return {
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#0288d1' },
headerRight: <View style={{ marginRight: 8 }}>
<TestControlButton testId={testId} />
</View>,
};
};
static renderBanner({ status, time }) {
@ -57,35 +51,28 @@ class Test extends React.Component {
setParams({ test });
}
renderError() {
const { test: { message } } = this.props;
if (message) {
return (
<ScrollView>
<Text style={styles.codeHeader}>Test Error</Text>
<Text style={styles.code}>
<Text>{message}</Text>
</Text>
</ScrollView>
);
}
return null;
}
render() {
const { test: { func, status, time } } = this.props;
const { test: { stackTrace, description, func, status, time }, testContextName } = this.props;
return (
<View style={styles.container}>
{Test.renderBanner({ status, time })}
<View style={styles.content}>
{this.renderError()}
<Text style={styles.codeHeader}>Test Code Preview</Text>
<ScrollView>
<Text style={styles.code}>
<View >
<ScrollView style={styles.sectionContainer}>
<Text style={styles.heading}>{testContextName}</Text>
<Text style={styles.description}>{description}</Text>
</ScrollView>
<ScrollView style={styles.sectionContainer}>
<Text style={styles.heading}>Test Error</Text>
<Text style={styles.description}>
<Text>{stackTrace || 'None.'}</Text>
</Text>
</ScrollView>
<Text style={styles.heading}>
Test Code Preview
</Text>
<ScrollView style={styles.sectionContainer}>
<Text style={styles.description}>
{beautify(removeLastLine(removeFirstLine(func.toString())), { indent_size: 4, break_chained_methods: true })}
</Text>
</ScrollView>
@ -99,10 +86,13 @@ Test.propTypes = {
test: PropTypes.shape({
status: PropTypes.string,
time: PropTypes.number,
message: PropTypes.string,
func: PropTypes.function,
stackTrace: PropTypes.function,
description: PropTypes.string,
}).isRequired,
testContextName: PropTypes.string,
navigation: PropTypes.shape({
setParams: PropTypes.func.isRequired,
}).isRequired,
@ -113,27 +103,32 @@ const styles = StyleSheet.create({
flex: 1,
backgroundColor: '#ffffff',
},
content: {},
code: {
backgroundColor: '#3F373A',
color: '#c3c3c3',
padding: 5,
fontSize: 12,
sectionContainer: {
minHeight: 100,
},
codeHeader: {
fontWeight: '600',
fontSize: 18,
backgroundColor: '#000',
color: '#fff',
heading: {
padding: 5,
backgroundColor: '#0288d1',
fontWeight: '600',
color: '#ffffff',
fontSize: 16,
},
description: {
padding: 5,
fontSize: 14,
},
});
function select({ tests }, { navigation: { state: { params: { testId } } } }) {
function select({ tests, testContexts }, { navigation: { state: { params: { testId } } } }) {
const test = tests[testId];
let testContext = testContexts[test.testContextId];
while(testContext.parentContextId && testContexts[testContext.parentContextId].parentContextId) {
testContext = testContexts[testContext.parentContextId];
}
return {
test,
testContextName: testContext.name,
};
}