Nick

    Nick

    1 year ago
    We have a custom view that is used in a lot of places. We want to tie this to a viewmodel and keep all the business logic there. A coworker suggested just passing in the ViewModel and lifecycle owner to the custom view and then observe on the properties. This doesn’t feel right. What do you guys do?
    s

    streetsofboston

    1 year ago
    Don’t make a (custom)
    View
    dependent on a ViewModel… Instead, think about how Fragments and Activities handle this. A Fragment or Activity receive a ViewModel and a View as input/dependency and this Fragment/Activity then observes properties on the ViewModel and manipulates the View accordingly.
    View ------+
               +--> Fragment/Activity
    ViewModel -+
    Make a similar type of class (a ‘controller’?) that will take (or get injected) a custom View and your custom-view related ViewModel like a Fragment or Activity.
    MyCustomView      --+
                        +--> MyCustomController
    MyCustomViewModel --+
    a

    allan.conda

    1 year ago
    This doesn’t feel right.
    What makes you feel so?
    s

    streetsofboston

    1 year ago
    class SomeActivity : AppCompatActivity() {
        ...
        
        val myCustomController: MyCustomController by lazy {
            MyCustomController(findViewById(R.id.my_custom_view), MyCustomViewModel())
        }
        ...
    }
    You could finagle the creation of the MyCustomController and its dependencies (MyCustomView and MyCustomViewModel) through dependency injection (dagger/koin/etc), if you want to remove some boiler plate.
    @allan.conda You should avoid making an Android View being dependent on a ViewModel. E.g TextViews, RecyclerViews, any other View don’t rely on ViewModels, including your own custom Views.
    a

    allan.conda

    1 year ago
    Why not? 🙂 I can imagine a full Custom View approach using libraries such as Conductor. Where would you put the ViewModel in such case?
    Their idea doesn’t break the MVVM pattern
    s

    streetsofboston

    1 year ago
    You put the ViewModel in a controller (like the example ‘MyCustomController’ above). True, it doesn’t break the MVVM pattern. But makes MyCustomView dependent on MyCustomViewModel which is not necessary, and it would break the rendering of the custom view in the IDE’s layout-editor (the layout-editor wouldn’t know how to provide an instance of MyCustomViewModel).
    Instead of
    class MyCustomView(private val viewModel: MyCustomViewModel): View() {...}
    it would be
    class MyCustomView : View() {…}
    and a
    class MyCustomController(private val view: MyCustomView, private val viewModel: MyCustomViewModel)
    where the MyCustomController plays the same role as a Fragment or Activity with respect to hooking up a ViewModel to a View. You could use dagger/koin to reduce the boilerplate instantiations of MyCustomControllers.
    gildor

    gildor

    1 year ago
    Using completely custom views as replacement for fragment/activity is a valid thing, but it requires quite a lot of custom logic and essentially own framework, and very easy to implement it in a wrong way It's more flexible approach I. Theory, but it easy becomes a huge mess. We used to use fully custom views as controller, but eventually decide that it doesn't worth it, and now it's our biggest legacy, it painful every time to touch this code, none of new features of androidx (lifecycle, viewmodels, backstack dispatchers) works there
    j

    Joost Klitsie

    1 year ago
    @Nick would it be possible to refactor this view to become a fragment containing the view?
    Nick

    Nick

    1 year ago
    @Joost Klitsie That could work too. But I always feel like fragments contain so much overhead with the extra lifecycle.
    s

    streetsofboston

    1 year ago
    @Nick Yup, a lot of overhead. That is where that ‘controller’ type of class comes in, like a very very lightweight fragment 🙂
    Nick

    Nick

    1 year ago
    Are there any examples that you’ve seen online that show what you describe?
    gildor

    gildor

    1 year ago
    the situtation with fragments lifecycle may be improved, there. are plans to follow lifecycle of view (so no more detached fragment). So, when it will happen, this “lightweight controller view” becomes “heavyweight” 🤷
    I mean, yeah, lifecycle of fragment is more complicated, but View API is kinda terrible itself (3 required constructors, attributes, injection is hard), and if you also have fragments it become problem. All this can be mitigate, but I really think it makes sense only if you know what you are doing
    Nick

    Nick

    1 year ago
    The majority of our crashes and problems comes from lifecycle issues related to fragments. I want to avoid them if I can.
    a

    allan.conda

    1 year ago
    but it requires quite a lot of custom logic and essentially own framework,
    I know 🙂 , that’s why I mentioned using something like Conductor to facilitate this, because this wasn’t how the Android Views were originally designed for. Additionally, Jetpack Compose is just around the corner. It doesn’t need Fragments or multiple Activities, and you can provide the viewModel from the top level Composable function
    You put the ViewModel in a controller
    That could work also, but that’s no longer simple MVVM. It’s MVVM+Controller.
    gildor

    gildor

    1 year ago
    The majority of our crashes and problems comes from lifecycle issues related to fragments. I want to avoid them if I can.
    Really? what kind crashes? i
    Nick

    Nick

    1 year ago
    so @streetsofboston, the controller will take the viewmodel as a param (that’s already created using the fragments lifecycleowner), and inside the controller i’d observe on the properties?
    gildor

    gildor

    1 year ago
    that’s why I mentioned using something like Conductor
    I’m not a big fan of conductor, for me it looks that they took all bad things from fragments, but yeah, made lifecycle more simple
    a

    allan.conda

    1 year ago
    @Nick there was a huge refactoring of Fragments, and part of that is fixing lifecycle issues and trying to align with the Activities’ lifecycle. You can consider using them again.https://medium.com/androiddevelopers/fragments-rebuilding-the-internals-61913f8bf48e Alternatively, LifecycleOwner is supposed to be a simpler way to have lifecycle-aware classes as well. I asked why your colleague’s idea doesn’t feel right, since I think there is a case specific to your project that make that a bad idea. 🙂 As you can see here everyone has their opinions based from their experiences, so I guess that answers your question as you were simply asking how the others are doing. There is no right single answer I think
    gildor

    gildor

    1 year ago
    I just want to point out, that using conductor it’s not the same as using custom view for controller, it’s essentially the same approach as use fragments, just different implementation
    a

    allan.conda

    1 year ago
    I’m not a big fan of conductor, for me it looks that they take all bad things from fragments, but yeah, make lifecycle more simple
    me neither 😄 I don’t like to depend on 3rd party libraries for something so important unless it’s really mainstream
    And of course, controller is a responsibility, it’s not tied to any library nor required by MVVM. It’s something you could separate further from the MVVM to adhere to SRP. Same as having other patterns such as UseCases, repositories
    f

    Fatih

    1 year ago
    Sounds like MVP not MVVM.