fullstackreact / react-native-firestack

A firestack v3 react-native implementation
MIT License
715 stars 132 forks source link

Exception 'Calls to setPersistenceEnabled must be made before any other usage of FIRDatabase instance.' was thrown while invoking enablePersistence on target FirestackDatabase with params #216

Open vivekmago opened 7 years ago

vivekmago commented 7 years ago

I cannot get persistence to work have the following code in my index file but it keeps cribbing about the error.. pls help what am I doing wrong...

const firestack = new Firestack();

firestack.onReady(()=> {
    firestack.database.setPersistence(true);
});
danielcolgan commented 7 years ago

Hello, @vivekmago ! Did you had some issues like this ?

moooji commented 7 years ago

I have the same problem in v3. Basically I am doing this:

const firestack = new Firestack();
firestack.database().setPersistence(true);

I guess the problem could be this code in the Database constructor:

this.offsetRef = this.ref('.info/serverTimeOffset');
this.offsetRef.on('value', (snapshot) => {
  this.serverTimeOffset = snapshot.val() || this.serverTimeOffset;
});

This accesses the database immediately, which means that it is not possible to execute setPersistence before. Do you think it is actually needed to have that method, or could it be just an option that can be passed to Firestack like const firestack = new Firestack({ persistence: true }): Does anybody see a use case that would require to switch persistence on/off/on during runtime?

moooji commented 7 years ago

Here is a PR that fixes the issue. https://github.com/fullstackreact/react-native-firestack/pull/232

setPersistence has to be called before any database references have been created. Since the database constructor immediately creates refs to .info/serverTimeOffset, calling setPersistence will always throw and it is not possible to enable persistence currently. Furthermore, setPersistence is not part of the Web SDK API anyway.

To fix this issue, this PR makes setPersistence internal and adds instead an option persistence that can be supplied to the Firestack constructor. This guarantees that persistence will be enabled before any refs are created.

const firestack = new Firestack({ persistence: true });
moooji commented 7 years ago

@Salakar how do you enable persistence in your code? Can you post a snippet? I have seen this issue with all v3 branches including yours :-/

I am using Using FirebaseDatabase (3.1.1) on iOS, are you using the same?

Also the official SDK docs say about persistenceEnabled on iOS:

Note that this property must be set before creating your first Database reference and only needs to be called once per application.

https://firebase.google.com/docs/reference/ios/firebasedatabase/api/reference/Classes/FIRDatabase#persistenceenabled

That being said, I cannot see how setPersistence could possibly work in v3 if the database constructor immediately creates references. Or do you see a way to call setPersistence before initializing the database?

Additionally, my thought is that an options is more suitable in this case than a method, because of the "[...] only needs to be called once per application."

@auser Are you using the persistence feature? Does it work for you in v3?

Ehesp commented 7 years ago

We just do the following, and import it throughout the app. No issues as far as I'm aware!

import Firestack from 'react-native-firestack';

const firestack = Firestack.initializeApp({
  debug: __DEV__ ? '*' : false,
  errorOnMissingPlayServices: false,
});

/**
 * Enable offline database persistence
 */
firestack.database().setPersistence(true);

export default firestack;
Salakar commented 7 years ago

@moooji can you confirm if @Ehesp 's solution works? We're on android only at the moment, but @chrisbianca is on ios and isn't facing this issue also I believe.

Think its a case of we need more docs 👀

chrisbianca commented 7 years ago

I'm not using .setPersistence() at the moment, so can't confirm either way on iOS.

Based on the docs @moooji is right, you need to call setPersistence before you create anything - perhaps this differs between iOS and Android? Regardless, it would make sense to follow the option approach to work around this as I can't see a reason where you'd want to enable and disable this feature throughout the app.

esdrasportillo commented 7 years ago

@Salakar I am facing this issue on iOS. I will try @Ehesp 's solution.

esdrasportillo commented 7 years ago

It doesn't work for me, even using @Ehesp 's solution. I am on v3 branch and on iOS.

Ehesp commented 7 years ago

Must be iOS specific. I think on Android it wasn't calling it before everything else so there was a fix.

auser commented 7 years ago

I like the idea that we can set this in the configuration, rather than it's own method.

SamMatthewsIsACommonName commented 7 years ago

Given this error on ios what method are you guys using to persist the user state at login? redux persist? Also when this works will it persist the auth state automatically or just the database state? Cheers

moooji commented 7 years ago

@SamMatthewsIsACommonName Database persistence works now on iOS using the latest v3 version and enabling the persistence option like this const firestack = new Firestack({ persistence: true });. This will persist the database state only. In regards to the user / auth persistence, the docs say

When a user signs up or signs in, that user becomes the current user of the Auth instance. The Firebase Auth instance persists the user's state, so that refreshing the page (in a browser) or restarting the application doesn't lose the user's information.

When the user signs out, the Auth instance stops keeping a reference to the User object and no longer persists its state; there is no current user. However, the user instance continues to be completely functional: if you keep a reference to it, you can still access and update the user's data.

So this does not have to do with database persistence and should be handled by the Auth module I think.

UPDATE: I just tried out if auth persistence works in my app and it looks like it does in iOS at least (could not test Android). I signed in with email/password and fetched the token like this:

firestack.auth().getToken()
      .then(token => console.log(token));

Then, I closed the app and reopened it again. Without signing in, I could still fetch the same auth token again, so it looks like it is persisted.

In case there are more questions in regards to user persistence, we should maybe open a dedicated issue about that.

I think we can close this issue.

SamMatthewsIsACommonName commented 7 years ago

Thanks guys awesome work really appreciate it

esdrasportillo commented 7 years ago

Hi @moooji , does it work now? I can't see the commit in the v3 branch and I updated to the latest version and it is still not working. Am I missing something ?

moooji commented 7 years ago

@esdrasportillo Sorry, I thought that PR https://github.com/fullstackreact/react-native-firestack/pull/232 had been merged, but apparently it has just be closed... Looks like it won't be part of v3 then @auser?

moooji commented 7 years ago

@auser @Salakar Persistence on iOS is still broken because the PR has been rejected. What shall we do instead to fix it?

rikur commented 7 years ago

Having the persistence as an option through the constructor will make bootstrapping a lot easier, thank you!

However, I'm having hard time figuring out which version PR/commits are part of the current 3.0.0-rc.2 package. I will try to use the v3 branch directly from git instead, but.. would be nice to have v3 tags in this repository for clarity!

benadamstyles commented 7 years ago

@rikur I'm also struggling to work out which version has which code, but also which fork to use. @Salakar has merged into his fork a PR which fixes this issue, but a lot of the code in his fork is different to this repo and has quite a different API so I would have to rewrite my app to use it. Have you made any progress either with choosing a fork/version or with getting persistence to work? Thanks very much for your time.

chrisbianca commented 7 years ago

See my comment here: https://github.com/Salakar/react-native-firestack/commit/386be3d0b118256ca510bbe78ad21c4748b8f7d9#commitcomment-20812842

moooji commented 7 years ago

Thanks for your work @chrisbianca. Are there plans to merge the repos? IMHO it feels not feasible for anybody if there is the "main" repo, but all dev work happens in a separate fork. This is highly confusing for anybody who wants to contribute or use this library.

chrisbianca commented 7 years ago

@moooji We know, it's not great and we're just as frustrated that the work we've done has been undermined by some untested PRs. @Salakar @Ehesp and I have been chatting about it and trying to work something out. We'll hopefully have something for you soon...

moooji commented 7 years ago

@Salakar @Ehesp @chrisbianca Any news on this?

TomCyril1 commented 6 years ago

Hello i fixed this problem but it was really hard : follow my steps 👍 1) Android manifest add in application <application android:name="com."Your path".FireClass" 2) Create a Fireclass : `public class FireClass extends Application { public static boolean deja = false;

    @Override
    public void onCreate() {
        super.onCreate();

        if (!deja)
        {
            FirebaseDatabase.getInstance().setPersistenceEnabled(true);
            //deja = true;
        }

        FirebaseDatabase database = FirebaseDatabase.getInstance();

Log.d("FIREBASE","INITIALISATION"); } }`

3) Create a Dbutil.class: youll call him each time you need to run an db query : `public class databaseUtil { public static FirebaseDatabase mDatabase; public static FirebaseDatabase mDatabase22; public static DatabaseReference mDatabase2 ; public static DatabaseReference mDatabase3 ; public static FirebaseAuth mAuth; public static FirebaseAuth.AuthStateListener mAuthListener;

public static  FirebaseDatabase getDatabase() {

    if (mDatabase == null) {

        mDatabase = FirebaseDatabase.getInstance();
        mDatabase.setPersistenceEnabled(true);

        mDatabase2 =mDatabase.getReference("user");
        mDatabase3 =mDatabase.getReference("user");
        mDatabase2.keepSynced(true);
        mDatabase3.keepSynced(true);

    }

    return mDatabase;

}

`

4) on each class you need to do db thing : do declare : private db DatabaseUtil;

[...] On create (){ .... db.getDatabase();

[....] } 5) import mDatabase from DatabaseUtil.class and use them like 👍 mDatabase3.addValueEventListener(new ValueEventListener() { .......

6) run !