Basically what I am trying is to return an Observable<Array<T>>. I double-checked that getAllCategoriesFromDB() returns Array<Categroies>. categoryDao is a kotlin class.
I tried using this code:
fun getAllCategoriesFromDB(): Observable<Array<Categories>>
{
return categoryDao.selectAllCategories().toObservable()
}
However the returned type is Observable<Categories> instead of Observable<Array<Categories>>
I followed the example from here (scroll down to the repository example code)
My selectAllCategories() looks like this (keep in mind I'm using Room):
#Query("SELECT * FROM Categories")
fun selectAllCategories(): Array<Categories>
Help or advice would be much appreciated since it's the first time I'm working with RxJava2.
I solved it with this code. It might not look as clean or be the best solution but at least it works:
var categories = categoryDao.selectAllCategories()
var observable = Observable.fromArray(categories)
Related
I am new to Kotlin so please excuse this question, as it is probably pretty stupid...
So I followed the tutorial by Philipp Lackner to create a Todo-List as an android app if any of you know that. Now I tried to add a read and write functionality to this app by saving into a simple .txt-file for now.
For that I tried to follow this tutorial as much as possible, but now I am running into Problems when writing code to load the Todo-Items from a file.
I wrote this function to load Todo-Items from the .txt-file:
private suspend fun loadTodoItemsFromInternalStorage(): List<Todo> {
return withContext(Dispatchers.IO) {
val todoItemList: MutableList<Todo> = mutableListOf<Todo>()
var isEven = true
val files = filesDir.listFiles()
files?.filter { it.canRead() && it.isFile && it.name.endsWith(".txt") }?.map {
val lines = it.bufferedReader().readLines()
for (i in lines.indices) {
isEven = if(isEven) {
todoItemList.add(Todo(lines[i], lines[i+1].toBoolean()))
!isEven
} else {
!isEven
}
}
todoItemList
} ?: mutableListOf<Todo>()
}
}
Why do I get that type mismatch? I even initialize the list I want to return as a MutableList of type Todo, but I guess the type inference of Kotlin turns it into a MutableList of type Any?
So how do I fix this? And if you want to you could tell me better ways to do what I did (e.g. saving Todo-items (which consist of title and a boolean whether they are checked or not) to a file)
My plan to keep this as simple as possible as this is my first Kotlin project was to just use 2 lines for a Todo-item, where the first line is the title and the second line is the status whether it has been checked or not. I hope that makes my code easier to understand.
Thank you so much for your help in advance! I appreciate it a lot, as I have been struggling with coding in the past and really want to improve my coding skills.
If you cast your return statement, the code should work. Taking your code as a basis:
private suspend fun loadTodoItemsFromInternalStorage(): List<Todo> {
return withContext(Dispatchers.IO) {
...
} ?: mutableListOf<Todo>()
} as MutableList<Todo>
}
Some additional suggestions:
when dealing with file handling in general: error handling, check for correct format, empty entries, ...
the check for isEven/isOdd is obsolete and the code can be shortened to great extent when you use the step-option within the for-loop
for (i in lines.indices step 2) {
todoItemList.add(Todo(lines[i], lines[i+1].toBoolean()))
}
I am trying to migrate from LiveData to Kotlin Flow. Right now I am working on a project that has offline supports in Room.
I was looking through the documentation and I managed to write an observable query in coroutines with Flow. (see: here)
The problem that I am facing right now is that whenever I add the suspend keyword inside the DAO class and try to run the project, it fails with the following error:
error: Not sure how to convert a Cursor to this method's return type (kotlinx.coroutines.flow.Flow<MyModel>).
The code with the problem:
#Transaction
#Query("SELECT * FROM table WHERE status = :status LIMIT 1")
suspend fun getWithSpecificStatus(status: String): Flow<MyModel?>
I am calling the code like this:
val modelLiveData: LiveData<MyModel?> = liveData(Dispatchers.IO) {
val result = databaseService.getWithSpecificStatus(Enum.IN_PROGRESS.status).first()
result?.let {
emit(it)
}
}
I tried to keep things simple. why is my code failing?
You could directly initialise the value of modelLiveData as:
val modelLiveData=databaseService.
getWithSpecificStatus(Enum.IN_PROGRESS.status).first()
.asLiveData()
You have used Flow so asLiveData() is used to convert it to LiveData
Also suggestion , you do should not use the suspend keyword because when you are returning Flow, Room automatically does this asynchronously. You just need to consume the Flow.
Kotlin Android app using graphQl & ApolloClient.
I want to test this method:
fun myMethod(id: String): Single<List<MyEntity>> {
val call = Query(id)
return Rx2Apollo.from(apolloClient.query(call))
.map { modeCode() }.firstOrError().onErrorReturn { emptyList() }
}
Is there an elegant way to mock the response of apolloClient.query(call) so Rx2Apollo returns the response?
I tried with a RealApolloCall, with a custom ApolloCall, etc, nothing convinces me.
And I wouldn't want to extract and mock the whole Rx2Apollo.from(apolloClient.query(call)) part. To use mockWebServer doesn't apply, because I do not want to test apolloClient, I want to mock its response to test the rest.
There is a related issue that was closed without really mentioning the best way to solve:
https://github.com/apollographql/apollo-android/issues/1144
Thanks!
right now I am starting to use LiveData for the first time. First I put all of my code in the viewModel including the code to start a search in the server.
I used LiveData like this:
Fragment onViewCreated()
viewModel.changeNotifierContacts.observe(this, androidx.lifecycle.Observer { value -> value?.let {
recyclerViewAdapter.setData(value)
} })
This was working as expected. Now I add a repository layer following MVVM pattern. (For this I moved my contact search functionality to repository class)
First I implemented the connection between ViewModel and repository like this:
ViewModel code:
fun getContacts(): MutableLiveData<ContactGroup> {
return contactSearchRepository.changeNotifierContacts;
}
fun search(newSearchInput: String) {
contactSearchRepository.searchInRepository(newSearchInput)
}
Now I read this article that told us to not use LiveData like this: https://developer.android.com/topic/libraries/architecture/livedata#merge_livedata
Example from this page:
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
private fun getPostalCode(address: String): LiveData<String> {
// DON'T DO THIS
return repository.getPostCode(address)
}
}
Instead we should use something like this:
var changeNotifierContacts : LiveData<ContactGroup> = Transformations.switchMap(searchInput) {
address -> contactSearchRepository.getPostCode(address) }
Questions:
Did I understand this article correctly or can I use my first implementation?
In my constructor of the viewModel I am creating an instance of my repository object that is starting to observe server data and it is getting initial data. (For example I am getting a list of all my friends). I am getting this initial data if I am using my first implementation. If I am using Transformations.switchMap implementation I am not getting this initial data. I first have to start a search here to get updated data then. This is not what I want, I also need to display "my friends" list without doing a search.
Is there another approach I can use here? Maybe LiveData is not the best solution to connect ViewModel with Repository?
Thanks for responses and suggestions!
Did I understand this article correctly or can I use my first implementation?
I think you did, but I believe you have expanded the concept too much.
If you are expecting the user to enter a search to receive an answer, you should do like they said:
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
private val addressInput = MutableLiveData<String>()
val postalCode: LiveData<String> = Transformations.switchMap(addressInput) {
address -> repository.getPostCode(address) }
fun setInput(address: String) {
addressInput.value = address
}
}
However, if you are loading a default list, you should do it like you did in your first example:
val getContact = contactSearchRepository.changeNotifierContacts
In this case you will have to observe getContact and postalCode.
In my constructor of the viewModel I am creating an instance of my repository object that is starting to observe server data and it is getting initial data. (For example I am getting a list of all my friends). I am getting this initial data if I am using my first implementation. If I am using Transformations.switchMap implementation I am not getting this initial data. I first have to start a search here to get updated data then. This is not what I want, I also need to display "my friends" list without doing a search.
You can start your fragment/activity with a default search, like this:
MyViewModel.setInput("Friends")
This way you do not need to observe two objects as postalCode will provide all answers.
Is there another approach I can use here? Maybe LiveData is not the best solution to connect ViewModel with Repository?
I think live data is your answer. After done with the learning curve it becomes easier to deal with!
I hope it helps!
I'm working on an Android app and I want to implement the MVVM pattern, which is pretty much the standard pushed by Google, however, I'd like to avoid using Android Data Bindings library if possible, since I hate autogenerated XML magic.
I've tried to implement something essentially akin to databinding in RxJava (Kotlin) using Jake Wharton's data binding library, plus some helpful extension methods.
My question is, is this the right way to go about things? Is this good enough to use in production? Are there potential problems I'm not seeing with this approach that will pop up later?
Essentially, I've implemented it like this:
I have a MvvmFragment (there is a similar class for activities) which takes care of setting up and managing the lifecycle of a CompositeDisposable object.
Then, in my ViewModel (part of the android Arch ViewModel package) I have all of the fields that will be bound to declared like this:
var displayName = BindableVar("")
var email = BindableVar("")
var signInProvider = BindableVar<AuthProvider>(defaultValue = AuthProvider.PASSWORD)
(Side note - Since Rx doesn't allow null values, I'm not sure how to handle the case of defaults for objects where the concept of a default doesn't really make sense, such as the AuthProvider above)
The BindableVar class is implemented like this:
class BindableVar<T>(defaultValue: T) {
var value: T = defaultValue
set(value) {
field = value
observable.onNext(value)
}
var observable = BehaviorSubject.createDefault(value)!!
}
Using Jake Wharton's RxBindings library, I have created some helpful extension methods on top of that, such as:
fun Disposable.addTo(compositeDisposable: CompositeDisposable): Disposable {
compositeDisposable.add(this)
return this
}
fun TextView.bindTextTo(string: BindableVar<String>): Disposable {
return string.observable.subscribe(this.text())
}
fun View.bindVisibilityTo(visibility: Int) {
// ... not shown
}
fun ImageView.bindImageUriTo(
src: BindableVar<Uri>, #DrawableRes placeholder: Int? = null
): Disposable {
return if (placeholder == null) {
src.observable.subscribe {
GlideApp.with(context).load(it).into(this)
}
} else {
src.observable.subscribe {
GlideApp.with(context).load(it).placeholder(placeholder).into(this)
}
}
}
Using these extension methods, I then obtain the ViewModel instance on Fragment initialization, and call a method initBindings(), which looks something like this:
item_display_name_value.bindTextTo(viewModel.displayName).addTo(bindings)
item_email_address_value.bindTextTo(viewModel.email).addTo(bindings)
item_profile_picture_view.bindImageUrlTo(viewModel.avatarUrl).addTo(bindings)
I want to avoid getting a week into fleshing out this architecture and then suddenly realizing there is some critical problem that can't be solved easily, or some other hidden gotcha. Should I just go with XML based data binding? I've heard a lot of complaints about the difficulty of unit-testing it, and the difficulty of reusing code with it.