hello developer I am new in jetpack compose, I hadn't problem
on controlling the state with coroutines and XML, then I tried to do the same thing in jetpack compose, but it did not work .my question is how to collect data with sealed state from view model class in jetpack compose.this the function that i want to collect data on my UI compose
fun getData()= flow<Resource<List<Task>>>
{
val accessToken=savedTokenClass.getSaveToken()
emit(Resource.Loading())
val result = authRepository.getUserTask()
if (result.code() == 201) {
emit(Resource.Success(result.body())
}
else {
emit(Resource.Error(result.message()))
}
}
this my sealed class
class Sealed open class Resource<T>() {
class Success< T>(val data: T) : Resource<T>()
class Loading<out >: Resource<out>()
data class Error< T>(val message: String, ) : Resource<T>()
}
Try this in Composable Function:
Note: resources over here refers to the object of Resource sealed class that will be declared in viewModel itself.
private val viewModel: ViewModelClassName by viewModels()
val resourceState by remember(viewModel) { viewModel.resources}.collectAsState()
when(resourceState){
is Resource.Loading->{/*TODO*/}
is Resource.SUCCESS->{/*TODO*/}
is Resource.Error->{/*TODO*/}
}
Related
I would like to make a generic class which only accepts Lists as a type parameter. But I also want the type parameter of the List. Something like this:
class MyClass<L extends List<T>> {
T foo() {
// ....
}
}
The problem is that that does not work. T is not found. But this does:
class MyClass<L extends List<T>, T> {
T foo() {
// ....
}
}
My only issue with this is that I have to always pass in the extra parameter T which should be inferred from the List type.
var instance = MyClass<List<int>>();
var instance = MyClass<List<int>, int>(); // Extra int kind of redundant
Is there any workaround to this?
The solution is similar to the one provided in this question (the same problem, but in Java): basically, you can't do that in Dart. What you can do is
create a new subclass:
class MyClass2<T> extends MyClass<List<T>, T> { ... }
or
create a factory method:
class MyClass<L extends List<T>, T> {
static MyClass<List<T>, T> fromList<T>(List<T> list) {
return MyClass(...);
}
}
In Kotlin, I create generic classes to handle data fetching network states as following:
sealed class Resource<T>(
val data: T? = null,
val error: String? = null
) {
class Success<T>(data: T?) : Resource<T>(data)
class Loading<T> : Resource<T>()
class Error<T>(error: String) : Resource<T>(error = error)
}
Whenever I want to create an object of one of the available states, I can do that using Resource.Success<T> for example, not just Success<T>. I think this is a cleaner way to make my code easier to follow up with and understand.
I tried to achieve the same using dart, but I am stuck. I only created class the following way which is not really the same as what I want.
Can someone guide me on how to achieve my target using best practices?
abstract class ResourceState<T> {
T? data;
String? error;
ResourceState({this.data, this.error});
}
class ResourceLoading<T> extends ResourceState<T>{
ResourceLoading() : super();
}
class ResourceError extends ResourceState {
ResourceError(String error) : super(error: error);
}
class ResourceSuccess extends ResourceState {
ResourceSuccess(dynamic data) : super(data: data);
}
You will want to use named constructors:
class Resource<T> {
T ? data;
String? error;
Resource({this.data, this.error});
Resource.success(this.data);
Resource.error(this.error);
Resource.loading();
bool get isLoading => error == null && data == null;
bool get isSuccess => data != null;
bool get isError => error != null;
}
This allows you to create a success as Resource<T>.success(value) and an error as Resource<T>.error("Badness overwhelming!").
The type argument to the error constructor doesn't matter if you'll never add a data value. You could make it Resource<Never>.error("badness!").
I left the unnamed constructor public, in case you want to use it, but it's not necessary and can be removed.
If you want the class to really be sealed, you won't want any public generative (non-factory) constructors.
In that case, I'd do it as:
class Resource<T> {
T ? data;
String? error;
Resource._({this.data, this.error});
factory Resource.success(T data) => Resource._(data: data);
factory Resource.error(this.error) => Resource._(error: error);
factory Resource.loading() => Resource._();
// ...
}
This class cannot be extended because it has no public generative constructor available to do the super-constructor call that a subclass is require to do.
Its interface can still be implemented, because any Dart interface can.
I unable to access the sealed class data observed in a Flow from Swift ..
sealed class NetworkStatus<T> {
data class Loading<T>(var loading: Boolean) : NetworkStatus<T> ()
data class CustomSignal<T>(var signal: String) : NetworkStatus<T>()
data class CustomSignalDetailed<T>(var signal: ErrorCaseData) : NetworkStatus<T>()
data class Failure<T>(val e: Throwable) : NetworkStatus<T>()
data class Data<T> (val data: T ) : NetworkStatus<T>()
companion object {
fun <T>loading(isLoading: Boolean): NetworkStatus<T> = Loading(isLoading)
fun <T> customstatus(signal: String): NetworkStatus<T> = CustomSignal(signal)
fun <T> customStatusDetailed(signals: ErrorCaseData): NetworkStatus<T> = CustomSignalDetailed(signals)
fun <T> failure(e: Throwable): NetworkStatus<T> = Failure(e)
fun <T> data(data: T): NetworkStatus<T> = Data<T>(data)
}
}
https://gist.github.com/RageshAntony/a4fd357973485b5fb8aef0e189ee9e7e
In the above gist ....
In MainActivity.kt , I used sealed class in flow for Data
Now I need the same in Swift but it's confusing
Even I used something as CFlow wrapping.. still confusion
In Swift , I can't get object type only as nsarray and also NetworkStatus.data is not accessible
.. I tried manual typecast (in line 8 , contentview.swift )
Still data object don't have any type ...
Please help me how to implement the same flow in Swift like in MainActivity.kt
Start from this sealed class declared in the shared code:
sealed class KMMResult<out Value>
data class SuccessKMMResult<Value>(
val value: Value
): KMMResult<Value>()
data class ErrorKMMResult<Value>(
val throwable: Throwable
): KMMResult<Value>()
and this method that randomly returns you a KMMResult with Success or Failure:
fun getRandomIntWrappedInResult(): KMMResult<Int> {
val isSuccess = Random.nextBoolean()
return if(isSuccess) {
SuccessKMMResult(Random.nextInt(until = 10))
} else {
ErrorKMMResult(RuntimeException("There was an error, Int not generated"))
}
}
You can use it like this on Android:
val randomInt: KMMResult<Int> = getRandomIntWrappedInResult()
val randomIntText: String = when (randomInt) {
is KMMResult.ErrorKMMResult -> {
"Error: ${randomInt.throwable.message}"
}
is KMMResult.SuccessKMMResult -> {
"Success: ${randomInt.value}"
}
}
and like this on iOS:
let randomInt: KMMResult<KotlinInt> = RandomNumberGeneratorKt.getRandomIntWrappedInIntResult()
let randomIntText: String
switch randomInt {
case let error as KMMResultErrorKMMResult<KotlinInt>:
randomIntText = "Error: \(error.throwable.message ?? error.throwable.description())"
case let success as KMMResultSuccessKMMResult<KotlinInt>:
randomIntText = "Success: \(success.value)"
default:
randomIntText = "This never happens"
}
This is not the best solution, I suggest you to create a Swift enum like:
enum SwiftResult<Value> {
case error(String)
case success(Value)
}
and convert KMMResult to SwiftResult using:
func toSwiftResult<Value>(kmmResult: KMMResult<Value>) -> SwiftResult<Value> {
if let successResult = kmmResult as? KMMResultSuccessKMMResult<Value> {
return SwiftResult.success(successResult.value!)
}
if let errorResult = kmmResult as? KMMResultErrorKMMResult {
return SwiftResult.error(errorResult.throwable.message ?? errorResult.throwable.description())
}
return SwiftResult.error("Unexpected error converting to SwiftResult")
}
Unfortunately, you need to do this manually for each sealed class, so I suggest you to use the library moko-kswift that does this job for you.
P.S. Avoid use List<Something> as a generic type because Something will be erased and when seeing the type from Swift you will just see NSArray. You can use two classes, one for single item and one for List, still generic on Something, or you can create a wrapper data class around your list, say ListWrapper, so the type won't be erased, because your original List<Something> would be inside ListWrapper which will be the type for NetworkStatus.
So use NetworkStatus<ListWrapper<Something>> instead of NetworkStatus<List<Something>>
Another P.S. Your function getAllCategories() in the gist is returning a flow, so it shouldn't be a suspend function.
I'm able to do something like the following in TypeScript
class Foo {
private constructor () {}
}
so this constructor is accessible only from inside the class itself.
How to achieve the same functionality in Dart?
Just create a named constructor that starts with _
class Foo {
Foo._() {}
}
then the constructor Foo._() will be accessible only from its class (and library).
A method without any code must be something like this
class Foo {
Foo._();
}
Yes, It is possible, wanna add more information around it.
A constructor can be made private by using (_) underscore operator which means private in dart.
So a class can be declared as
class Foo {
Foo._() {}
}
so now, The class Foo doesn't have a default constructor
Foo foo = Foo(); // It will give compile time error
The same theory applied while extending class also, It's also impossible to call the private constructor if it declares in a separate file.
class FooBar extends Foo {
FooBar() : super._(); // This will give compile time error.
}
But both above functionality works if we use them in the same class or file respectively.
Foo foo = Foo._(); // It will work as calling from the same class
and
class FooBar extends Foo {
FooBar() : super._(); // This will work as both Foo and FooBar are declared in same file.
}
you can create following class in order to get a singleton instance
class Sample{
factory Sample() => _this ??= Sample._();
Sample._(); // you can add your custom code here
static Sample _this;
}
Now in the main function you can call the sample constructor
void main(){
/// this will return the _this instace from sample class
Sample sample = Sample();
}
just use abstract class.
Because you can't instantiate abstract class
I'm writing a JavaFX application in Kotlin with the following controller definition:
class MainController {
#Inject private lateinit var componentDescriptors: List<ComponentDescriptor>
/* More code goes here */
}
I'm using Guice for Dependency management. And I'm trying to inject the list of class instances loaded via java.util.ServiceLoader. My problem is to define a binding that will inject the list of loaded object instances into the declared field. I tried annotation based provisioning:
internal class MyModule: AbstractModule() {
override fun configure() { }
#Provides #Singleton
fun bindComponentDescriptors(): List<ComponentDescriptor> =
ServiceLoader.load(ComponentDescriptor::class.java).toList()
}
and multibinding extension (switched List to Set in field definition of corse):
internal class MyModule: AbstractModule() {
override fun configure() {
val componentDescriptorBinder = Multibinder.newSetBinder(binder(), ComponentDescriptor::class.java)
ServiceLoader.load(ComponentDescriptor::class.java).forEach {
componentDescriptorBinder.addBinding().toInstance(it)
}
}
}
but both of these approaches leads to the same error:
No implementation for java.util.List<? extends simpleApp.ComponentDescriptor> was bound.
while locating java.util.List<? extends simpleApp.ComponentDescriptor>
for field at simpleApp.MainController.componentDescryptors(MainController.kt:6)
while locating simpleApp.MainController
1 error
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
at com.gluonhq.ignite.guice.GuiceContext.getInstance(GuiceContext.java:46)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:929)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
... 12 more
I'm starting to suspect that it somehow related to Kotlin gerenic variance and Guice strict type checking. But I don't know how to declare the binding so Guice will know what to inject into this field.
Yes, it happens because of variance but there's a way to make it work.
class MainController {
#JvmSuppressWildcards
#Inject
private lateinit var componentDescriptors: List<ComponentDescriptor>
}
By default Kotlin generates List<? extends ComponentDescriptor> signature for the componentDescriptors field. The #JvmSuppressWildcards makes it generate a simple parameterized signature List<ComponentDescriptor>.
#Michael gives the correct answer and explanation. Here's an example of one strategy for unit testing a Set multibinding for those that like to test their modules:
class MyModuleTest {
#JvmSuppressWildcards
#Inject
private lateinit var myTypes: Set<MyType>
#Before fun before() {
val injector = Guice.createInjector(MyModule())
injector.injectMembers(this)
}
#Test fun multibindings() {
assertNotNull(myTypes)
assertTrue(myTypes.iterator().next() is MyType)
}
}
#Michael comment is working. If you want to do the injection in constructor, you need do something like
class MainController #Inject consturctor(
private var componentDescriptors: List<#JvmSuppressWildcards ComponentDescriptor>
) {}