I work on a project with retrofit and rxjava in MVVM architecture that contains a repository and repositoryImpl package. I want to remove rxjava and just use Kotlin coroutine for this project.
but after search and read articles for migrating from rxjava to coroutine I did not found a right and clean way. may please help me to implement this section that implemented with rxjava implement with coroutines
MyProjectService.kt:
#POST("/user/login")
fun login(#Body agentModel: AgentModel): Observable<APIResultModel<AgentModel>>
Repository.kt :
interface Repository {
fun login(
context: Context,
userModel: AgentModel,
callback: (Result<APIResultModel<AgentModel>, RaakebException>) -> Unit
RepositoryImpl.kt :
class RepositoryImpl : Repository {
private val api = RaakebApi.instance
private val prefs by lazy {
UserPreferences()
}
override fun login(
context: Context,
userModel: AgentModel,
callback: (Result<APIResultModel<AgentModel>, RaakebException>) -> Unit
) {
val error = showError(callback)
val result = Consumer<APIResultModel<AgentModel>> { agent ->
prefs.token = agent.apiObjects.token.toString()
callback(Result.Success(agent))
}
val hashMap = HashMap<String, Any>()
val deviceModel = DeviceModel()
deviceModel.uniqueID = DeviceInfoHelper.getDeviceUniqueID(context)
deviceModel.appVersion = DeviceInfoHelper.getAppVersion()
deviceModel.platform = "ANDROID"
deviceModel.sdkVersion = DeviceInfoHelper.getSDKVersion()
deviceModel.phoneBrand = DeviceInfoHelper.getPhoneBrand()
deviceModel.phoneModel = DeviceInfoHelper.getPhoneModel()
//deviceModel.notificationID = Rakeeb.prefs.notification_id
hashMap["user"] = userModel
hashMap["device"] = deviceModel
api.login(userModel)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result, error)
}
I tried to make it but It's wrong. and I don't know how I must fix it.
MyProjectService.kt :
interface MyProjectService {
#POST("/user/login")
suspend fun login(#Body agentModel: AgentModel): Result<APIResultModel<AgentModel>, RaakebException>
companion object {
private val prefs by lazy {
UserPreferences()
}
val instance: RaakebApi by lazy {
val builder = OkHttpClient.Builder()
builder.addInterceptor {
val requestBuilder = it.request().newBuilder()
if (!it.request().url().url().path?.contains("/user/login")!!) {
requestBuilder.addHeader("auth", prefs.token)
}
requestBuilder.addHeader("Content-Type", "application/json")
it.proceed(requestBuilder.build())
}
builder.readTimeout(30, TimeUnit.SECONDS)
builder.connectTimeout(30, TimeUnit.SECONDS)
val client = builder.build()
val retrofitBuilder: Retrofit.Builder = Retrofit.Builder()
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
return#lazy retrofitBuilder.baseUrl(ConstHelper.SERVER_URL)
.addConverterFactory(GsonConverterFactory.create()).build()
.create(RaakebApi::class.java)
}
}
}
Repository.kt :
interface Repository {
suspend fun login(
context: Context,
userModel: AgentModel
) : Result<APIResultModel<AgentModel>, RaakebException>
companion object {
val instance: Repository by lazy {
RepositoryImpl()
}
}
RepositoryImpl.kt :
class RepositoryImpl : Repository {
private val api = RaakebApi.instance
private val prefs by lazy {
UserPreferences()
}
override suspend fun login(
context: Context,
userModel: AgentModel
): Result<APIResultModel<AgentModel>, RaakebException> = withContext(Dispatchers.IO) {
when (val response = api.login(userModel)) {
is Result.Success -> {
/* val hashMap = HashMap<String, Any>()
val deviceModel = DeviceModel()
deviceModel.uniqueID = DeviceInfoHelper.getDeviceUniqueID(context)
deviceModel.appVersion = DeviceInfoHelper.getAppVersion()
deviceModel.platform = "ANDROID"
deviceModel.sdkVersion = DeviceInfoHelper.getSDKVersion()
deviceModel.phoneBrand = DeviceInfoHelper.getPhoneBrand()
deviceModel.phoneModel = DeviceInfoHelper.getPhoneModel()
//deviceModel.notificationID = Rakeeb.prefs.notification_id
hashMap["user"] = userModel
hashMap["device"] = deviceModel*/
return#withContext response.success
}
is Result.Error -> {
}
}
}
thanks for your help.
You can return from a retrofit api function your type with "suspend" keyword. If you want to remove rx Observables from repositories. But you need to remember: "coroutine = Single". You can't use it like Flowable. If you want I can share my project for you. I've changed rx to coroutines there.
Related
I am trying to write test code for a ViewModel.
and the ViewModel uses this repository, so I'd like to mock it.
class ServiceRepository #Inject constructor(
private val service: MyService,
private val dao: MyDao
) {
suspend fetchMenus(filter: FilterData) = flow{
val insertList = mutableListOf<MenuData>()
val deleteList = mutableListOf<MenuData>()
val response = try {
decodeMenus(filter)
} catch(e: CustomIOException) {
// catch
}
val result = response.getOrNull()?.results?.map{ menu ->
menu.isRead = isRead(menu)
}
}
private suspend fun decodeMenus(): Result<MenuListData> {
return try {
val response = service.fetchMenus()
when{
response.isSuccessful -> Result.success(resonse.body()!!.data)
else -> Result.failure(CustomException())
}
}catch (e: Exception){
Result.failure(e)
}
}
private fun isRead(menu: MenuData): Boolean {
val ret = if(menu.status == MenuType.yesterday.index){
true
}else{
val checkedList = dao.getChecked(menu.key)
if(checkedList.isEmpty()){
false
}else{
checkedList.first().isChecked
}
}
return ret
}
}
And here's my test code that I am trying to write.
#ExperimentalCoroutinesApi
#ExtendWith(CoroutinesTestRule::class)
class MainViewModelTest {
private val instantExecutorRule = InstantTaskExecutorRule()
private val mockWebServer = MockWebServer()
#get:Rule
val rule: RuleChain = RuleChain
.outerRule(instantExecutorRule)
.around(mockWebServer)
private val retrofit by lazy {
val client = OkHttpClient.Builder().apply {
connectTimeout(5, TimeUnit.SECONDS)
callTimeout(5, TimeUnit.SECONDS)
readTimeout(5, TimeUnit.SECONDS)
writeTimeout(5, TimeUnit.SECONDS)
retryOnConnectionFailure(true)
}
val gson = GsonBuilder()
.setLenient()
.setDateFormat("yyyy-MM-dd'T'hh:mm:ssZ")
.create()
Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.client(client.build())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
private val mService by lazy {
retrofit.create(MyService::class.java)
}
#MockK
private lateinit var mDao: MyDao
// no mock
private lateinit var serviceRepo: ServiceRepository
// should mock?
private lateinit var mainViewModel: MainViewModel
#Before
fun setUp() {
Dispatchers.setMain(Dispatchers.Unconfined)
MockKAnnotations.init(this, relaxed = true)
serviceRepo = ServiceRepository(mService, mDao)
mainViewModel = MainViewModel(serviceRepo, userRepo, prefRepo)
}
#Test
fun loadMenuList_returnMenuList(){
val fakeResJson = loadJson("/example_success.json")
val fakeFilter = FilterData("sunday")
val mockResponseBody = MockResponse().setBody(fakeResJson).setResponsCode(200)
val mockWebServer.enqueue(mockResponseBody)
val responseBody = mService.fetchMenus(FilterData("yesterday"))
coEvery { mService.fetchMenus("yesterday") } return responseBody
// coEvery { serviceRepo.fetchMenus("yesterday") } return ???
}
Since I need to get response from api, I used MockWebServer and created responseBody. And then I mock mService to return responseBody when fetchMenus() is called.
this is what I tired in other way:
#Mockk
private lateinit var mService: MyService
// TODO: without MockWebServer
#Test
fun loadReceptionList2_returnReceptionList() = runTest {
// given
coEvery {
mService.fetchMenus("yesterday")
} returns // How can I create return value...?
}
But I don't know how to create the return value there.
So, when serviceRepo calls fetchMenus("yesterday"), it must be called. However, since ServiceRepository is very complicated and it has flow. I don't know how to achieve this. How can I do that?
I am developing news app but TopHeadlinesFragment loading progress bar not showing response from server
what I want to know where I am making mistake what I have to do in order to show response from server. Maybe something wrong with my observer in topHeadlinesFragment.kt or koin network module
below my screenshot of the app
loading progress
below my TopHeadlinesFragment.kt
class TopHeadlinesFragment : Fragment() {
private lateinit var binding: FragmentTopHeadlinesBinding
private val viewModel by viewModel<MainViewModel>()
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
// private val newsRepository: NewsRepository by inject()
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding =
DataBindingUtil.inflate(inflater, R.layout.fragment_top_headlines, container, false)
binding.lifecycleOwner = this
topHeadlinesAdapter = TopHeadlinesAdapter()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViewModel()
// loadingAds()
}
private fun initViewModel() {
viewModel.sportList.observe(this, Observer { result ->
when (result) {
is Results.Success -> {
val newList = result.data
if (newList != null) {
topHeadlinesAdapter.updateData(newList)
}
binding.recyclerView.adapter = topHeadlinesAdapter
topHeadlinesAdapter.notifyDataSetChanged()
viewModel.showLoading.observe(this, Observer {showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
}
is Results.Failure -> {
viewModel.showLoading.observe(this, Observer {showLoading ->
pb.visibility = if (showLoading) View.INVISIBLE else View.GONE
})
}
}
viewModel.loadNews()
})
}
}
below NewsRepository.kt
class NewsRepository(
private val sportNewsApi: SportNewsInterface,
private val sportNewsDao: SportNewsDao
) {
companion object{
const val TAG= "Error"
}
val data = sportNewsDao.getAllData()
suspend fun refresh() = withContext(Dispatchers.IO) {
val articles = sportNewsApi.getNewsAsync().body()?.articles
if (articles != null) {
sportNewsDao.addAll(articles)
Log.e(TAG,"Error")
Results.Success(articles)
} else {
Results.Failure("MyError")
}
}
}
below My MainViewModel.kt
class MainViewModel(val newsRepository: NewsRepository) : ViewModel(), CoroutineScope {
// Coroutine's background job
val job = Job()
// Define default thread for Coroutine as Main and add job
override val coroutineContext: CoroutineContext = Dispatchers.Main + job
private val _showLoading = MutableLiveData<Boolean>()
private val _sportList = MutableLiveData<Results>()
val showLoading: LiveData<Boolean>
get() = _showLoading
val sportList: LiveData<Results>
get() = _sportList
fun loadNews() {
// Show progressBar during the operation on the MAIN (default) thread
_showLoading.value = true
// launch the Coroutine
launch {
// Switching from MAIN to IO thread for API operation
// Update our data list with the new one from API
val result = newsRepository.refresh()
_sportList.value = result
_showLoading.value = false
}
}
override fun onCleared() {
job.cancel()
}
}
below my KoinNetworkModule.kt
const val BASE_URL = "https://newsapi.org/"
val netModule = module {
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
client.readTimeout(5 * 60, TimeUnit.SECONDS)
return client.addInterceptor {
val original = it.request()
val requestBuilder = original.newBuilder()
requestBuilder.header("Content-Type", "application/json")
val request = requestBuilder.method(original.method, original.body).build()
return#addInterceptor it.proceed(request)
}.build()
}
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(okHttpClient)
.build()
return retrofit.create(T::class.java)
}
I fixed problem by changing my code followingly.
private fun initViewModel() {
viewModel.sportList.observe(this, Observer { result ->
when (result) {
is Results.Success -> {
val newList = result.data
if (newList != null) {
topHeadlinesAdapter.updateData(newList)
}
binding.recyclerView.adapter = topHeadlinesAdapter
topHeadlinesAdapter.notifyDataSetChanged()
}
}
})
viewModel.showLoading.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel.loadNews()
}
Got your Problem. If you seriously want to show the response, whatever you are getting use this code in the Retrofit Instance. The role of Intercepter is used to show the request and response at the Log level. You can findout the URL of API, Request and resonse in the Log window.
Now modify KoinNetworkModule.kt like this
const val BASE_URL = "https://newsapi.org/"
val netModule = module {
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client1 = OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES) // write timeout
.readTimeout(2, TimeUnit.MINUTES) // read timeout
.addInterceptor(interceptor)
.build()
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(client1)
.build()
return retrofit.create(T::class.java)
}
With the help of dagger and rxJava I update the list in a RecyclerView. Everything works well, the list is displayed. But the problem is that in the logs I see how this list is updated every second. What could be the problem? In a similar project but in Java everything works correctly, the list is updated once at startup.
My Network Module:
#Module(includes = [ViewModelModule::class])
class NetworkModule {
companion object {
const val KEY = "key"
const val BASE_URL = "base_url"
}
#Provides
#Singleton
fun provideOkHttp(): OkHttpClient {
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(object : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val original = chain.request()
val originalHttpUrl = original.url
val url = originalHttpUrl.newBuilder()
//.addQueryParameter("apikey", KEY)
.build()
val requestBuilder = original.newBuilder()
.url(url)
.header("apikey", KEY)
val request = requestBuilder.build()
return chain.proceed(request)
}
})
// logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
httpClient.addInterceptor(logging)
return httpClient.build()
}
#Provides
#Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(provideOkHttp())
.build()
}
#Provides
#Singleton
fun provideContactsService(retrofit: Retrofit) : ContactsService{
return retrofit.create(ContactsService::class.java)
}
}
My ViewModel:
class ContactsViewModel #Inject constructor(private val contactsRepository: ContactsRepository) :
ViewModel() {
var mutableLiveData = MutableLiveData<List<ContactsModel>>()
private val disposable = CompositeDisposable()
fun getContactMutableLiveData(): MutableLiveData<List<ContactsModel>> {
loadData()
return mutableLiveData
}
fun loadData() {
disposable.add(contactsRepository.modelSingle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<ContactsModel>>() {
override fun onSuccess(t: List<ContactsModel>) {
getContactMutableLiveData().value = t
}
override fun onError(e: Throwable) {
}
})
)
}
}
And my Activity:
contactsViewModel.getContactMutableLiveData().observe(this#ContactListActivity, Observer {
mAdapter = ContactsAdapter(this#ContactListActivity, it as ArrayList<ContactsModel>)
recycler_contacts.layoutManager =
LinearLayoutManager(applicationContext, OrientationHelper.VERTICAL, false)
recycler_contacts.adapter = mAdapter
recycler_contacts.setHasFixedSize(true)
mAdapter.sortByName()
})
Okay if you only want to update your data list only once... I would recommend you look into a single live event that would trigger the reloading of your recycler view
As such
//in repo
private SingleLiveEvent<Boolean> listHasBeenUpdate=new SingleLiveEvent<>();
//setItsGetterInTheRepo
public SingleLiveEvent<Boolean> getListHasBeenUpdated(){
return listHasBeenUpdated();
}
//uponSucessfuly fetching your list from retrofit
listHasBeenUpdated=true;
//pass list to viewmodel
then in the ViewModel, I would set the list to be an Observable Data which would be updated once it's fetched from retrofit (Consider using room db to store this)
//use a setter to set the list from Repo
ObservableField<List<Contacts>> list=new ObservableField<>();
public SingleLiveEvent<List<Contacts>> fetchContacts(){
return myRepo.getListHasBeenUpdated();
}
In your activity class now observe the single live event like so
viewModel.fetchContacts().observe(this,contacts->{
if(contacts){
//update Recycler
}
});
hope this helps you.
It was a logical error. You need to rewrite the loadData function as shown below
class ContactsViewModel #Inject constructor(private val contactsRepository: ContactsRepository) :
ViewModel() {
var mutableLiveData = MutableLiveData<List<ContactsModel>>()
private val disposable = CompositeDisposable()
fun getContactMutableLiveData(): MutableLiveData<List<ContactsModel>> {
disposable.add(contactsRepository.modelSingle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<ContactsModel>>() {
override fun onSuccess(t: List<ContactsModel>) {
mutableLiveData.value = t
}
override fun onError(e: Throwable) {
}
}))
return mutableLiveData
}
}
I am developing a new android app but I am getting the following exception Unable to invoke no-args constructor for kotlinx.coroutines.Deferred>. Registering an InstanceCreator with Gson for this type may fix this problem.
below my MainViewModel.kt
#Suppress("UNCHECKED_CAST")
class MainViewModel(val newsRepository: NewsRepository) : ViewModel(), CoroutineScope {
// Coroutine's background job
val job = Job()
// Define default thread for Coroutine as Main and add job
override val coroutineContext: CoroutineContext = Dispatchers.Main + job
val showLoading = MutableLiveData<Boolean>()
val sportList = MutableLiveData <List<Article>>()
val showError = SingleLiveEvent<String>()
fun loadNews() {
// Show progressBar during the operation on the MAIN (default) thread
showLoading.value = true
// launch the Coroutine
launch {
// Switching from MAIN to IO thread for API operation
// Update our data list with the new one from API
val result = withContext(Dispatchers.IO) {
newsRepository?.getNewsList()
}
// Hide progressBar once the operation is done on the MAIN (default) thread
showLoading.value = false
when (result) {
is UseCaseResult.Success<*> -> {
sportList.value = result.data as List<Article>
}
is Error -> showError.value = result.message
}
}
}
override fun onCleared() {
super.onCleared()
// Clear our job when the linked activity is destroyed to avoid memory leaks
job.cancel()
}
}
below appmodules.kt my network logic implementation
const val BASE_URL = "https://newsapi.org/"
val appModules = module {
// The Retrofit service using our custom HTTP client instance as a singleton
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
// Tells Koin how to create an instance of CatRepository
factory<NewsRepository> { (NewsRepositoryImpl(sportsNewsApi = get())) }
// Specific viewModel pattern to tell Koin how to build MainViewModel
viewModel { MainViewModel (newsRepository = get ()) }
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
client.readTimeout(5 * 60, TimeUnit.SECONDS)
return client.addInterceptor {
val original = it.request()
val requestBuilder = original.newBuilder()
requestBuilder.header("Content-Type", "application/json")
val request = requestBuilder.method(original.method, original.body).build()
return#addInterceptor it.proceed(request)
}.build()
}
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(okHttpClient)
.build()
return retrofit.create(T::class.java)
}
below SportNewsResponse.kt
data class SportNewsResponse(
val articles: List<Article>,
val status: String,
val totalResults: Int
)
below SportNewsInterface where I have implemented my ending points
interface SportNewsInterface {
#GET("v2/top-headlines?country=us&apiKey=da331087e3f3462bb534b3b0917cbee9")
suspend fun getNews(): Deferred<List<SportNewsResponse>>
#GET("/v2/top-headlines?sources=espn&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getEspn(): Deferred<List<SportNewsResponse>>
#GET("/v2/top-headlines?sources=football-italia&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getFootballItalia(): Deferred<List<SportNewsResponse>>
#GET("/v2/top-headlines?sources=bbc-sport&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getBBCSport(): Deferred<List<SportNewsResponse>>
}
I'm new in architecture component and kotlin.
I've written and app that works fine one android pre orio but it returns nothing on android orio and above .
these are my codes, this is my code for retrofit connection
object ApiConnection {
val BaseUrl ="http://site.ir/"
val client: Retrofit
get() {
val gson = GsonBuilder()
.setLenient()
.create()
///for Logging
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
var retrofit = Retrofit.Builder()
.baseUrl(BaseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build()
return retrofit
}
}
this is my code for activity :
lateinit var rc_peyk:RecyclerView;
lateinit var peykViewModel:PeykHistoryViewModel
lateinit var peykHistAdapter: PeykHistoryAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.peyk_history)
declare();
peykViewModel= ViewModelProviders.of(this).get(PeykHistoryViewModel::class.java)
peykViewModel.getPeyks(Func.getUid(this)).observe(this
, Observer<MutableList<Peyk_item>>{
if(it==null){
MyToast.makeText(this#PeykHistory, "null 1");
}
if(it!=null && it.size==0){
MyToast.makeText(this#PeykHistory, "size 0");
}
if(it!=null && it.size>0){
peykHistAdapter= PeykHistoryAdapter(this#PeykHistory,it)
rc_peyk.adapter=peykHistAdapter
}
})
actionbar()
}
private fun declare() {
rc_peyk=findViewById(R.id.rc_peyk_history)
rc_peyk.layoutManager=LinearLayoutManager(this)
}
this is the code view viewModel :
class PeykHistoryViewModel(application:Application):AndroidViewModel(application){
private val peykRepository:PeykHistoryRepository= PeykHistoryRepository()
private lateinit var peykHistory:MutableLiveData<MutableList<Peyk_item>>
fun getPeyks(uid:String):MutableLiveData<MutableList<Peyk_item>>{
if(!::peykHistory.isInitialized){
peykHistory=peykRepository.getPeyks(uid)
}
return peykHistory
}
this is the code for repository :
class PeykHistoryRepository {
private lateinit var getPeykHistory: getPeykHistoryApi
private lateinit var peykList: MutableLiveData<MutableList<Peyk_item>>
fun getPeyks(uid: String): MutableLiveData<MutableList<Peyk_item>> {
if (!::peykList.isInitialized) {
peykList = MutableLiveData()
}
getPeykHistory = ApiConnection.client.create(getPeykHistoryApi::class.java)
getPeykHistory.getPeykHistory(uid)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ result ->
if(result.peyks!=null){
peykList+=result.peyks
}else{
Log.v("this","nothing");
}
}, { error ->
Log.v("this",error.localizedMessage.toString())
})
return peykList;
}
operator fun <T> MutableLiveData<MutableList<T>>.plusAssign(values: List<T>) {
val value = this.value ?: arrayListOf()
value.addAll(values)
this.value = value
}
there is no error and the api is fine and return the correct json .
could you help me ? I've tried too many things but doesn't help me