ViewModel is recreated when coming back from navigation in compose - android-jetpack-compose

I'm refactoring original project xml -> compose, and got some issues.
When I navigate A -> B and Back to A, the A's viewmodel is recreated.
What I expect is to follow the original lifecycle (onDestroyed, not onDispose).
I already know how to implement lifeCycleObserver in compose, but don't understand the lifeCycle of viewmodel in compose.
I want to use Compose's viewmodel like a fragment's viewmodel for lifecycle, not recreating when recomposed.
Is this thinking anti-pattern in compose? Then, How to remain the viewModel' lifecycle?
I don't want to pass a viewmodel as a parameter Because I have a nested navigation for same compose screen. And don't use same lifecycleOwner(MainActivity's lifeCyclerOwner)

Related

Passing data down the hierarchy

I read a lot of topics about principles for writing testable code. I'm working on using Dependency Injection in my application and I'm facing a very big problem.
In the top ViewController, I'm loading an array of objects and the user can select on of them to go deeper in the hierarchy. The thing is that I need the selected object in some controllers (including the leaf controller) and for some I don't need.
In picture, it can be like this :
NeedObject -> don't need -> don't need -> need
TopController -> otherController1 -> otherController2 -> leafController
For now, I'm passing the selected object through all the hierarchy, but I can feel how it's bad as it does not respect the principle stating that an object should only know about what it needs. But I can't figure out how to respect this principle.
Note that the objects are all children of ManagedObject and are stocked in CoreData.
I thought about adding like a boolean isSelected in the class definition and then use an object that would request CoreData for the object with selected = YES, but I'm not sure about it as the "selectability" of the object should not be something persistent, right ?
Thank you for your help
I think what you are doing already is fine.
As you have described it, otherController1 and otherController2 'need' a reference to the user selected object in order to pass that reference to a lower level controller.
Passing info down the hierarchy is the way to do dependency injection, even if some intermediate layers have no use for that information other than passing it on further.
This is preferred over having an object deep in the hierarchy "reaching back up" to a top-level object to request the information. Lower-level objects should know little about the higher-level objects, which gives you the most flexibility to evolve your hierarchy over time.
Apple recommends defining a protocol for passing dependency injection data this way, which further isolates dependencies between classes.

Using BIND framework for two way data-binding in iOS

I'm trying to use BIND framework to bind a UITableView with UITextfield's which can edit the content.
I'm trying to achieve something very similar to how binding is done in Mac OSX. Bind a datasource with view, let the user make changes, on save the data is saved after validations.
Typically this is done by subscribing the delegate or observing the valueChanged event from textfield. I wanted to try out a new way to reduce this, that's how I came across BIND framework.
It encourages to use an MVVM framework, usually seen in .NET. Binding is as simple as mapping the keypath of model with the view component. But I'm finding it difficult to achieve to two way binding, from model to component and back.
BINDINGS(MHPersonNameViewModel,
BINDViewModel(name, ~>, textLabel.text),
BINDViewModel(ID, ~>, detailTextLabel.text),
nil);
Could anyone point me in the right direction.
This is a late answer, and not even really an answer to your question, but I hope it's still useful.
Take a look at https://github.com/mutech/aka-ios-beacon. This is a binding framework that integrates into Interface Builder and (by default) uses the view controller as root view model.
You don't have to write any code to initialize bindings. In your example, assuming that the view controller has (KVO compliant) properties "name" and "ID", you would just need to set the UILabel "text binding" properties to name and ID (you find the in the property panel in interface builder and enable bindings for the view controller (also in the properties panel).
And that should be all you have to do to establish bindings.
In versions up to 0.1.1 of AKABeacon, "enable bindings" is not yet there. In this case your view controller would have to inherit from AKAFormViewController.

MVVM, dependency injection and too many constructor parameters

I have been doing iOS development using MVVM and dependency injection for a couple of months and I am really happy with the results. The code is so much clear and easier to test. But I have been stragling with a problem which I haven't found a solution that I felt really confortable with.
In order to understand the problem I want to give you a little bit of context. The last app that I have been working was architectured in the following way / layers:
Model
View models
View / View Controllers
Services: Classes that know how to deal with external services like Twitter, Facebook, etc.
Repositories: A repository is class that knows how to interact with a resource of the application's REST API. Lets say that we have a blog application, we could have the users resources and the posts resources. Each of thoses resources have several method. There is a 1-to-1 relation between the resources and the repositories.
When the applications starts we have a Bootstrap class that initializes the app and creates the main view model. We have a restriction that only view models can create other view models. For example in the case of having a view that contains a list of elements (in iOS it will be represented with a UITableView) and the detail view for each of thoses elements that is presented by pushing it to the navigation stack after tapping on the element in the list. What we do is make the view model that is attached to the table view controller create the detail view model. The table view controller listens to the table view model and then presents the detail view model by creating the detail view controller and passing it its view model. So the view controller does not know how to create a view model it only knows how to create a view controller for that view model.
Is the responsability of the parent view model to the pass all the dependecies to the child view model.
The problem comes when a view model that is very deep in the view hierachy needs dependencies that its parent controllers does not require. For example a service to access some external web service. Because its parent does not have that dependency it will have to add it to its dependecy list, thus adding a new parameter to the constructor. Imagine how this goes if the grand parent does not have the dependecy either.
What do you think is a good solution? Possible solutions:
Singletons: Harder to test and they are basically gloabl state
A factory class: We could a set of factory that knows how to create certain types of object. For example a ServiceFactory and RepositoryFactory. The service factory could have method to create services like: TwitterService, FacebookService, GithubService. The repository factory could know how to create a repository for each of the API resources. In the case of having a few factories (2 or 3) all the view models could dependent on this factories.
For now we have chosen the factory class solution because we don't need to use singletons and we can treat the factory as any other dependecy which makes it relatively easy to test. The problem is that it kind of feels like a good object and by having a factory you don't actually know which is the real dependecy that needs the view model, unless you look inside the constructor's implementation to check which factory methods are being called.
In our application, we have chosen to have our view models access their dependencies via dependency lookup rather than dependency injection. This means the view models are simply passed a container object which contains the necessary dependencies, and then "looks up" each dependency from this container object.
The major advantage of this is that all objects in the system can be declared up front in a container definition, and it is very simple to pass around the container, compared to the seventy-eight or so dependencies that might be needed.
As any dependency injection fan will tell you, dependency lookup is certainly its inferior cousin, largely because dependency lookup requires the object to understand the idea of a container (and therefore usually the framework that provides it), whereas dependency injection keeps the object blissfully unaware of where its dependencies came from. However, in this case I believe the tradeoff is worth it. Note that in our architecture, it's just the view models that make this tradeoff - all other objects such as your "models" and "services" still use DI.
It's also worth noting that many basic implementations of dependency lookup have the container as a singleton, but that does not have to be the case. In our application, we have multiple containers that simply "group" related dependencies together. This is particularly important if different objects have different lifecycles - some objects may live forever, while others may only need to live while a certain user activity is in progress. This is why the container is passed from view model to view model - different view models may have different containers. This also facilitates unit testing by allowing you to pass a container full of mock objects to the view model under test.
To provide some concreteness to an otherwise abstract answer, here's how one of our view models might look. We use the Swinject framework.
class SomeViewModel: NSObject {
private let fooModel: FooModel
private let barModel: BarModel
init(container: Container) {
fooModel = container.resolve(FooModel.self)!
barModel = container.resolve(BarModel.self)!
}
// variety of code here that uses fooModel and barModel
}
What you need to do is move the instantiation of all your objects to a Composition Root. Instead of parents passing down dependencies they don't even necessarily need to their children, you have a single point of entry at the start of your program where all of your object graph is created (and cleaned up, should you have Disposable dependencies).
You can find a good example here, by the author of the Dependency Injection in .NET book (highly recommended to understand concepts like the Composition Root) - notice how it frees you from having to pass dependencies 5 or 6 levels deep for no reason:
var queueDirectory =
new DirectoryInfo(#"..\..\..\BookingWebUI\Queue").CreateIfAbsent();
var singleSourceOfTruthDirectory =
new DirectoryInfo(#"..\..\..\BookingWebUI\SSoT").CreateIfAbsent();
var viewStoreDirectory =
new DirectoryInfo(#"..\..\..\BookingWebUI\ViewStore").CreateIfAbsent();
var extension = "txt";
var fileDateStore = new FileDateStore(
singleSourceOfTruthDirectory,
extension);
var quickenings = new IQuickening[]
{
new RequestReservationCommand.Quickening(),
new ReservationAcceptedEvent.Quickening(),
new ReservationRejectedEvent.Quickening(),
new CapacityReservedEvent.Quickening(),
new SoldOutEvent.Quickening()
};
var disposable = new CompositeDisposable();
var messageDispatcher = new Subject<object>();
disposable.Add(
messageDispatcher.Subscribe(
new Dispatcher<RequestReservationCommand>(
new CapacityGate(
new JsonCapacityRepository(
fileDateStore,
fileDateStore,
quickenings),
new JsonChannel<ReservationAcceptedEvent>(
new FileQueueWriter<ReservationAcceptedEvent>(
queueDirectory,
extension)),
new JsonChannel<ReservationRejectedEvent>(
new FileQueueWriter<ReservationRejectedEvent>(
queueDirectory,
extension)),
new JsonChannel<SoldOutEvent>(
new FileQueueWriter<SoldOutEvent>(
queueDirectory,
extension))))));
disposable.Add(
messageDispatcher.Subscribe(
new Dispatcher<SoldOutEvent>(
new MonthViewUpdater(
new FileMonthViewStore(
viewStoreDirectory,
extension)))));
var q = new QueueConsumer(
new FileQueue(
queueDirectory,
extension),
new JsonStreamObserver(
quickenings,
messageDispatcher));
RunUntilStopped(q);
Doing this is pretty much a prerequisite to do proper Dependency Injection, and it will allow you to very easily transition to use a container if you want to.
For the instantiation of objects that must be created after startup, or depend on data available long after startup, what you want to do is create Abstract Factories that know how to create these objects and take as constructor parameters all needed stable dependencies. These factories are injected as normal dependencies in the composition root, and are then called upon as needed with the variable/unstable arguments passed in as method parameters.
Here are a couple of suggestions.
Best coding practices suggests that if you are using more than 3 parameters, then you should use a class to host the parameters.
Another approach is to separate the data services [repositories] out, so that they line up to a task based service. Mainly to in line with the ViewModel (or Controller) So if you ViewModel uses Customers and Orders, most would use two services - one for CRUD operations on Customers, and one for CRUD operations on Orders. You could, however, use a service that will deal with all the operations needed for your ViewModel. This is a task based approach used in designing Windows Communication Foundation Services and Web Services.
It looks like you need to make use of the Managed Extensibility Framework (MEF), you can find more information here.
Essentially, what this will allow you to do is to use [Export] and [Import] attributes. This will allow your class' dependencies to be injected, without having to worry about massive constructors on your parent view models.

Why use instance variables to "connect" controllers with views?

This is a conceptual question and I haven't been able to find the answer in SO, so here I go:
Why instance variables are used to connect controllers and views? Don't we have two different objects of two different classes (Controller vs Views). So, when the view is rendered we are in a different context, but we are using instance variables of another object? Isn't this breaking encapsulation in somehow?
How does Rails manage to do that matching from one object to another? Does it clone all the instances variables of the controller to the view?
In a sense, you could say that it is breaking encapsulation. I have found that if you are not careful, it is easy to get your business/presentation logic mixed together in Rails. It usually starts when I am writing a view template, and discover that I need some value which I didn't pass from the controller. So I go back, and tweak the controller to suit what I need in the view. After one tweak, and another, and another, you look at the controller method, and it is setting all kinds of instance variables which don't make sense unless you look at the view to see what they are for. So you end up in a situation where you need to look at both controller and view to understand either, rather than being able to take one or the other in isolation.
I think that using instance variables (together with the Binding trick) is simply a way to pass whatever values you need from controller to view, without having to declare parameters in advance (as you would when defining a method). No declarations means less code to write, and less to change when you want to refactor and reorganize things.
Rails uses eval and Binding to pass controller instance variables to views. See this presentation from Dave Thomas, there's a small example at minute 46' that explains how this is done.

Is there a Django's context processor like in Grails?

I want to output a value which is global in all the templates or even layout in Grails, like Django's context processor where you could render the context and use it as global variable in the templates.
Is there a concept like this in Grails? And, how can I use that in the layout?
I am not familiar with Django at all. Looked up Django's context processor in google, I think I get it. Basically it configures reusable data that gets injected into every template? Anyway, as far as I know nothing like that exists in Grails. You can try the following as a workaround.
Use ApplicationContext
Every view has access to the applicationContext. So make a service that holds all the data you need, let's say it is called fooService, and the data item you want is a field in the service called bar (could be a method too of course). Then in your view do ${applicationContext.fooService.bar}. Resource for accessing applicationContext in view: http://mrhaki.blogspot.com/2011/11/grails-goodness-get-grailsapplication.html.
Use your layout
I am not sure about this one, so use at your own risk. The top one is of course extremely verbose. It would be annoying to call that over and over again in different views. So instead, call it once and make it a variable in your layout with g:set. I think the variable will be available in every view that uses that layout.... but not sure. Here are the docs for g:set -> http://grails.org/doc/latest/ref/Tags/set.html.
If I didn't get what context processors do in python I am happy to try again...

Resources