Skip to content

Instantly share code, notes, and snippets.

@maciejpigulski
Created March 25, 2023 22:24
Show Gist options
  • Save maciejpigulski/9a6cda0cef7c8ce9c84dc6948feed8b9 to your computer and use it in GitHub Desktop.
Save maciejpigulski/9a6cda0cef7c8ce9c84dc6948feed8b9 to your computer and use it in GitHub Desktop.
Hilt custom component with ViewModel
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import arrow.core.Either
import com.mpigulski.sample.models.User
import com.mpigulski.sample.models.error.ApiError
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.DefineComponent
import dagger.hilt.EntryPoint
import dagger.hilt.EntryPoints
import dagger.hilt.InstallIn
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.components.SingletonComponent
import dagger.hilt.internal.GeneratedComponentManager
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Scope
import javax.inject.Singleton
@Scope
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class UserScope
@DefineComponent(parent = SingletonComponent::class)
@UserScope
interface UserComponent {
@DefineComponent.Builder
interface Builder {
fun build(): UserComponent
}
}
@Singleton
class UserComponentManager @Inject constructor(private val userComponentFactory: UserComponent.Builder) :
GeneratedComponentManager<UserComponent> {
var userComponent: UserComponent? = null
private set
fun initialise() {
userComponent = userComponentFactory.build()
}
fun release() {
userComponent = null
}
override fun generatedComponent(): UserComponent {
return userComponent!!
}
}
@EntryPoint
@InstallIn(UserComponent::class)
interface UserComponentEntryPoint {
fun provideUserRepository(): UserRepository
}
interface UserRepository {
suspend fun fetchUsers(): Either<ApiError, List<User>>
}
class MyViewModel @AssistedInject constructor(
@Assisted private val userRepository: UserRepository,
) : ViewModel() {
@AssistedFactory
interface Factory {
fun create(
userRepository: UserRepository
): MyViewModel
}
companion object {
fun provideFactory(
assistedFactory: Factory,
userRepository: UserRepository
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T =
assistedFactory.create(userRepository) as T
}
}
fun fetchUsers() {
viewModelScope.launch {
val result = userRepository.fetchUsers()
}
}
}
@AndroidEntryPoint
class MyFragment : Fragment() {
@Inject
lateinit var myViewModelFactory: MyViewModel.Factory
private val viewModel: MyViewModel by viewModels {
MyViewModel.provideFactory(
myViewModelFactory,
userRepository
)
}
@Inject
lateinit var userComponentHandler: UserComponentManager
private val entryPoint: UserComponentEntryPoint
get() = EntryPoints.get(userComponentHandler, UserComponentEntryPoint::class.java)
private val userRepository: UserRepository
get() = entryPoint.provideUserRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
userComponentHandler.initialise()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.fetchUsers()
}
override fun onDestroy() {
userComponentHandler.release()
super.onDestroy()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment