0

so I am new in MVVM pattern in Android. I want to perform two actions, when a button is clicked then first I check the internet connection, if the internet connection is available then perform login to server.

here is my ViewModel

class LoginViewModel(application: Application) : AndroidViewModel(application) {


    private val _hasInternetConnection = MutableLiveData(false)
    val hasInternetConnection: LiveData<Boolean>
        get() = _hasInternetConnection


    fun checkIfItHasInternetConnection() {

        if (InternetConnection.checkConnection(getApplication())) {
            _hasInternetConnection.value = true
        } else {
            _hasInternetConnection.value = false

        }

    }

    fun performLogin() {

    }



}

and here is my fragment

class LoginFragment : Fragment() {

    lateinit var model: LoginViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        model = ViewModelProvider(this).get(LoginViewModel::class.java)

        button.setOnClickListener {
            model.checkIfItHasInternetConnection()
        }

        model.hasInternetConnection.observe(this, Observer { hasInternetConnection ->

            if (!hasInternetConnection) {
                longToast("You have no internet connection")
            } else {

            }

        })



    }


}

The problem is, I am not sure where do I have to call performLogin method from my viewmodel, do I have to call it in my fragment like this in the observer ?

model.hasInternetConnection.observe(this, Observer { hasInternetConnection ->

            if (!hasInternetConnection) {
                longToast("You have no internet connection")
            } else {
                model.performLogin()
            }

 })

or do I have to call it inside the viewmodel itself after perform internet connection checking ? like this

// inside viewModel
fun checkIfItHasInternetConnection() {

        if (InternetConnection.checkConnection(getApplication())) {
            _hasInternetConnection.value = true
            performLogin()
        } else {
            _hasInternetConnection.value = false

        }

    }

sorry if the question is too basic, I try to learn MVVM, and from the tutorials I watch, it seems I have to call the method in viewModel from the fragment, but it will be more convenient if I call it from the viewmodel itself. I need to know the correct way to solve case like this

Alexa289
  • 8,089
  • 10
  • 74
  • 178
  • `model.performLogin()` is fine. you can also look at https://github.com/android/plaid/blob/master/core/src/main/java/io/plaidapp/core/ui/ConnectivityChecker.kt and how this is used in plaid – Raghunandan Mar 17 '20 at 10:50

2 Answers2

0

you can work with both method but for code separation point of view you would follow first approach

0

I suggest you take a look at the Android Developer Guide to App Architecture. It is based on the MVVM architecture and introduces a repository that handles web calls. Maybe this will help you.

But to answer your question: Following a clear MVVM architecture, the Activity should only call a method in the ViewModel and not do any logic. This method in the ViewModel then should either do the logic, or delegate further down to the Model, or, for example, a Repository.

If the login can not be performed due to missing Internet connection, the ViewModel should handle this by setting some LiveData, maybe LiveData<String> errorMessage, and the Activity should observe this LiveData and show a message to the user whenever this LiveData provides a new String.

Edit: some example code.

Repository:

public void performLogin(String username, String password, MutableLiveData<LoginStatus> loginStatus) {
  if (/*check Internet access*/) {
    performLoginAsync(username, password, loginStatus);
  } else {
    loginStatus.setValue(LoginStatus.NO_INTERNET);
  }
}
private void performLoginAsync(String username, String password, MutableLiveData<LoginStatus> loginStatus) {
  /*perform async login with success and error callback*/
  myWebservice.login(
    username,
    password,
    () -> loginStatus.setValue(LoginStatus.SUCCESS), /*success callback*/
    error -> loginStatus.setValue(LoginStatus.ERROR) /*error callback*/
  );
}

ViewModel:

public MutableLiveData<LoginStatus> loginStatus;
private Repository myRepository;
public void performLogin(String username, String password) {
  myRepository.performLogin(username, password, loginStatus);
}

Fragment:

private ViewModel myViewModel;
private EditText loginUsername, loginPassword;
private Button loginButton;

/* in some method, where you want your initialization, e.g. in onViewCreated */
  loginButton.setOnClickListener(v -> {
    myViewModel.login(loginUsername.getText().toString(), loginPassword.getText().toString());
  });
  myViewModel.loginStatus.observe(this, loginStatus -> Toast.makeText(this, loginStatus.getText(), Toast.LENGTH_LONG));

This code is not a working example, but it should contain most of it and should point you in the correct direction. Hope it helps. If something is not clear, make another comment

Sebu
  • 4,671
  • 5
  • 16
  • 29
  • if I create errorMessage LiveData, then where I should call performLogin method in my ViewModel ? in fragment or in viewModel itself ?. is it okay to call the method from the viewmodel itself ? – Alexa289 Mar 18 '20 at 03:30
  • could you please help me by showing a code to keep the logic inside the viewModel using that error message liveData sir ? – Alexa289 Mar 18 '20 at 06:00