2

I need to access sharedPrefences in my model class since we shouldn't access context and views from model class, How can I get a value from the model? I don't want to parse each of my variables to viewmodel and then my model class.

model class:

class CategoryModel(private val netManager: NetManager) {
    var dateChanges: String = "null";
    var isLoading= ObservableField<Boolean>(false)

    fun getCats(): MutableLiveData<MutableList<Cat>> {
        isLoading.set(true)
        var list = MutableLiveData<MutableList<Cat>>();
        if (netManager.isConnected!!) {
            list = getCatsOnline();
        }
        return list
    }

    private fun getCatsOnline(): MutableLiveData<MutableList<Cat>> {
        var list = MutableLiveData<MutableList<Cat>>();
        val getCats = ApiConnection.client.create(Category::class.java)

        Log.v("this","uid "+MyApp().uid)
        getCats.getCats(MyApp().uid, dateChanges)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                { success ->
                    isLoading.set(false)
                    list+= success.cats
                },{
                    error->
                        isLoading.set(false)
                        Log.v("this","ErrorGetCats "+ error.localizedMessage);
                }
            )

        return list;
    }

    operator fun <T> MutableLiveData<MutableList<T>>.plusAssign(values: List<T>) {
        val value = this.value ?: arrayListOf()
        value.addAll(values)
        this.value = value
    }
}

How can I do so ?

Keivan Esbati
  • 3,376
  • 1
  • 22
  • 36
Navid Abutorab
  • 1,667
  • 7
  • 31
  • 58
  • Just like how you have a `NetManager`, make a `__Manager` that uses either SharedPreferences, or Context as its constructor arg. Then use application context for that. – EpicPandaForce Apr 23 '19 at 12:09
  • Hi, did you solve the problem? I would really appreciate if you share your solution. Cheers – Keivan Esbati May 07 '19 at 05:59

3 Answers3

2

First you have to change the Model class:

class CategoryModel(.. NetManager, private val sharedPrefences: SharedPrefences)

, now you can obtain applicationContext by using AndroidViewModel instead of ViewModel, don't be alarmed as obtaining a static reference to applicationContext is not necessarily a bad idea:

public class MyViewModel extends AndroidViewModel {

    private val preferences: SharedPreferences = 
        PreferenceManager.getDefaultSharedPreferences(getApplication())
    private val netManager: NetManager
    private val model = CategoryModel(netManager, preferences)

    ...
}           

Now a more elegant way would be using a Factory to inject dependencies into ViewModel like this:

public static class Factory extends ViewModelProvider.NewInstanceFactory {

    @NonNull
    private final SharedPreferences mPreferences;
    ...

    public Factory(@NonNull SharedPreferences preferences, ...) {
        mPreferences = preferences;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        //noinspection unchecked
        return (T) new MyViewModel(mPreferences, ..);
    }
}

P.S: Ideally I would create the model using a ServiceLocator or DependencyInjector and inject it directly into the ViewModel using the mentioned Factory.

Keivan Esbati
  • 3,376
  • 1
  • 22
  • 36
0

I need to access sharedPrefences in my model class, since we shouldn't access context and views from model class, How can I get a value from model ?

Wrap it and pass it as dependency to the constructor of your ViewModel.

Wrapping it, it is only a recommendation in case you want to switch from SharedPreferences to something else to store data (EG. a different library)

Blackbelt
  • 156,034
  • 29
  • 297
  • 305
0

I would wrap the sharedPreferences as a Repository so you model gets a dependency to this repository interface and the repository-implementation gets the ApplicationContext in it-s constructor

k3b
  • 14,517
  • 7
  • 53
  • 85