10

Is there scope for improvement in implementing Architecture Components or in general considering:

  1. Note: if you choose to use an AuthStateListener, make sure to unregister it before launching the FirebaseUI flow and re-register it after the flow returns. FirebaseUI performs auth operations internally which may trigger the listener before the flow is complete.

LiveData

public class FirebaseAuthLiveData extends LiveData<FirebaseUser> {
    private FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();

    private FirebaseAuth.AuthStateListener authStateListener = 
            new FirebaseAuth.AuthStateListener() {
                @Override
                public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                    FirebaseUser firebaseUser = firebaseAuth.getCurrentUser();
                    setValue(firebaseUser);
                }
            };

    @Override
    protected void onActive() {
        super.onActive();
        firebaseAuth.addAuthStateListener(authStateListener);
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        firebaseAuth.removeAuthStateListener(authStateListener);
    }
}

ViewModel

public class FirebaseAuthViewModel extends ViewModel {
    private final FirebaseAuthLiveData firebaseAuthLiveData = new 
            FirebaseAuthLiveData();

    public LiveData<FirebaseUser> getFirebaseAuthLiveData() {
        return firebaseAuthLiveData; }
    }
}

MainActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FirebaseAuthViewModel firebaseAuthViewModel = 
    ViewModelProviders.of(MainActivity.this).get(FirebaseAuthViewModel.class);

    firebaseUserLiveData = firebaseAuthViewModel.getFirebaseAuthLiveData();

    firebaseUserLiveData.observe(MainActivity.this, new Observer<FirebaseUser>() {
        @Override
        public void onChanged(@Nullable FirebaseUser firebaseUser) {
            if (firebaseUser == null) {
                final Intent intent = AuthUI.getInstance().createSignInIntentBuilder()
                        .setAvailableProviders(Collections.singletonList(                            
                            new AuthUI.IdpConfig.Builder(AuthUI.GOOGLE_PROVIDER).build())
                        ).build();
                startActivityForResult(intent, SIGN_IN);
            } else {
                updateUI(firebaseUser);
            }

        }
    });
}
Derek Pollard
  • 6,953
  • 6
  • 39
  • 59
Johnny
  • 282
  • 1
  • 15
  • Is this implementation a good Architecture desing? I'm implementing FirebaseAuth with ViewModel and LiveData and your code seems simple and clear to me. – Jonathan Aste May 21 '19 at 14:40
  • I'm also wondering if this is the right way to implement authstatelistener.. Does this method work OK?.. It seems to keep the firestore repository seperate to the authentication which I guess is still in line with MVVM.. – Scamparelli Jan 11 '20 at 13:52

1 Answers1

1

You're almost there. The single problem that you have is the use of the "FirebaseUser" object inside the activity, which breaks the MVVM architecture pattern, where is said that the activity should know nothing about its data source.

So the simplest and cleanest solution might be using a LiveData class:

class AuthLiveData(
    private val auth: FirebaseAuth
): LiveData<Boolean>(), FirebaseAuth.AuthStateListener {
    override fun onAuthStateChanged(auth: FirebaseAuth) {
        value = auth.currentUser == null
    }

    override fun onActive() {
        super.onActive()
        auth.addAuthStateListener(this)
    }

    override fun onInactive() {
        super.onInactive()
        auth.removeAuthStateListener(this)
    }
}

And a Repository class:

class MyRepository {
    private val auth = FirebaseAuth.getInstance()

    fun getFirebaseAuthState(): AuthLiveData {
        return AuthLiveData(auth)
    }
}

Now in the ViewModel class, we can simply:

class MyViewModel: ViewModel() {
    val repository = MyRepository()

    fun getAuthState(): LiveData<Boolean> {
        return repository.getFirebaseAuthState()
    }
}

In the end, in the activity we can observe the auth state changes like this:

viewModel.getAuthState().observe(this, { isUserSignedOut ->
    if (isUserSignedOut) {
        //Update the UI
    }
})

This means that we'll always know when the user is signed in or not, without knowing which is the back-end.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193