I am following the Now In Android sample for modularization and best practices with Jetpack Compose and I am a bit stuck with sharing a simple LocalDate state across several modules/features.
I want to be able to change a selected date in several places, and update the UI accordingly in several places (i.e. AppState, BottomSheet (BottomSheetState), Screen A (Viewmodel) / Screen B (Viewmodel), Dialog (if opened), etc.).
Now considering concepts like Ownership and Encapsulation, I am not sure which would be the preferred way to handle this. Or how I should rethink the logic.
So far I tried:
Hoisting the state in AppState and passing it onto Composables below;
Problem here was that I cannot pass the stateflow on to the VMs due to DI with hiltViewModel() and Compose Navigation. (Example of implementation:)
#OptIn(ExperimentalLifecycleComposeApi::class)
#Composable
internal fun ScreenARoute(viewModel: ScreenAViewModel = hiltViewModel()) {
ScreenA(/***/)
}
#HiltViewModel
class ScreenAViewModel #Inject constructor(
private val userPrefsRepository: UserPrefsRepository,
) : ViewModel() {
/***/
}
Using SavedStateHolder in ViewModels, including the MainActivityViewmodel where the date should be initialized (today);
Problem here was that the state was only updated in the respective VM/SavedStateHolder and not across all places. For example I was able to update the date and state in Screen A and the UI got updated accordingly, other places/screens remained without updates though. (Example of implementation:)
#HiltViewModel
class ScreenAViewModel #Inject constructor(
private val userPrefsRepository: UserPrefsRepository,
val savedStateHandle: SavedStateHandle,
) : ViewModel() {
/***/
val selectedDate: StateFlow<LocalDate> =
savedStateHandle.getStateFlow<LocalDate>(SELECTED_DATE_SAVED_STATE_KEY, LocalDate.now())
.stateIn(
viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = LocalDate.now()
)
fun updateSelectedDate(date: LocalDate) {
savedStateHandle[SELECTED_DATE_SAVED_STATE_KEY] = newDate
/***/
}
Other things I am considering:
Creating a Singleton object that holds the state, which is initialized in the MainActivityViewModel and implemented in the VMs via hilt DI.
Using Room or Preferences to save the state and share across the app per Singleton Repository and DI. But this might be overkill?
The state should survive configuration changes but can be disposed when the app gets killed. Any help will be much appreciated.
Related
Scenario: I retrieve data from a server via #ObservableObject/#Publish; within a tabview().
Data is displayed as expected.
However when I return to the tabView from another tab, I data is gone, requiring me to do another fetch which isn't needed.
Here's the subscriber:
struct NYTStatesView: View {
#ObservedObject var dataSource = NYTStatesModel()
...
}
Here's the publisher:
final class NYTStatesModel: ObservableObject {
#Published var revisedNYTStates: RevisedNYTStates!
// ...
}
dataSource.revisedNYTStates is empty upon return to the View.
Question: How do I RETAIN the data so I don't have to always access the server per View display?
When you're switching tabs, dataSource is recreated:
struct NYTStatesView: View {
#ObservedObject var dataSource = NYTStatesModel()
...
}
A solution may be to create dataSource outside the TabView.
For example on the app level:
#main
struct TestApp: App {
#StateObject private var dataSource = NYTStatesModel()
...
}
(or in the SceneDelegate for SwiftUI 1.0)
This is something that I struggled with for a while too. There are a couple of different ways to do what you want.
The first is creating a loader class from somewhere higher up in the view hierarchy and passing it down to where it is needed. For example, you might create the loader in the the app main struct and passing it in as an environment object. IMO, this method is the least scalable and the messiest.
The second option is to either use a singleton or a static property to persist your data. For example you could make your NYTStatesModel a singleton. (I don't condone this type of behavior)
The third (and best IMO) is creating a class that is responsible for caching your results and passing that into the view model from the environment. NSCache will actually dump results when your phone is running low on memory. Here's two articles that will help. The first is how to create a cache and the second is how to create an environment key.
https://www.swiftbysundell.com/articles/caching-in-swift/
https://swiftwithmajid.com/2019/08/21/the-power-of-environment-in-swiftui/
Just use dependency injection to check the cache before the network call. The cache is persisted in the environment.
I hope someone has already faced and solved this issue, and can point me to the correct direction.
So I have rest of my unit tests working: Core.Tests has tests for ViewModels to see they are working properly. Now I would like to set up a test project for Phone.Tests that would only test out the binding. So suppose on the login page, something get's entered into the username text box, and that value should be updated in ViewModel and vice-versa.
As a testing framework, I am using WP Toolkit Test framework, and not MS one; WP Toolkit framework runs on the phone itself, meaning it has access to the UI thread.
In theory a test is supposed to look like following:
[TestMethod]
[Asynchronous]
public void Username_Update_View_Should_Update_Model()
{
const string testUsername = "Testing";
var usernameTextBox = GetUiElement<PhoneTextBox>("UsernamePhoneTextBox");
// initial value
Assert.AreEqual(null, _viewModel.Authorization.Username, "Default value should be blank");
//
usernameTextBox.Text = testUsername;
//
Assert.AreEqual(testUsername, _viewModel.Authorization.Username, "Binding not set for {0}", "Username");
}
private T GetUiElement<T>(string name) where T : UIElement
{
return (T)_view.FindName(name);
}
Now, I need to somehow create the view in [TestInitialize] method, and this is what I think I have setup wrong.
I have tried creating the ViewModel manually; then I created the View manually, and binded both DataContext and ViewModel (just to be on safe side) to created viewModel.
At this point, I am expecting changing one property on any one should update the other.
Of-course the error is my test fails. I can't figure out if I should be looking at a custom presenter (all the examples seem to be for ios, droid.) I also tried the following:
public class TestAppStart : MvxNavigatingObject, IMvxAppStart
{
public void Start(object hint = null)
{
ShowViewModel<UserLoginViewModel>();
}
}
and then on my testInitialize I thought I could start it, but I guess I need to find RegisterAppStart and once that's done, try to get the view back from RootFrame.
There must be an easier way... anyone??
Thanks in advance.
Edited: I have got this following as Base Test
public abstract class BaseTest
{
private IMvxIoCProvider _ioc;
protected IMvxIoCProvider Ioc
{
get
{
return _ioc;
}
}
public void Setup()
{
ClearAll();
}
protected virtual void ClearAll()
{
MvxSingleton.ClearAllSingletons();
_ioc = MvxSimpleIoCContainer.Initialize();
_ioc.RegisterSingleton(_ioc);
_ioc.RegisterSingleton((IMvxTrace)new DebugTrace());
InitialiseSingletonCache();
InitialiseMvxSettings();
MvxTrace.Initialize();
AdditionalSetup();
}
private static void InitialiseSingletonCache()
{
MvxSingletonCache.Initialize();
}
protected virtual void InitialiseMvxSettings()
{
_ioc.RegisterSingleton((IMvxSettings)new MvxSettings());
}
protected virtual void AdditionalSetup()
{
_ioc.RegisterSingleton(Mock.Of<ISettings>);
_ioc.RegisterSingleton<IApplicationData>(() => new ApplicationData());
_ioc.RegisterSingleton<IPlatformSpecific>(() => new PlatformSpecific());
_ioc.RegisterSingleton<IValidatorFactory>(() => new ValidatorFactory());
//
_ioc.RegisterType<IMvxMessenger, MvxMessengerHub>();
}
}
On my TestClass initialize, I call base.Setup(), which does setup except the ViewDispatcher. Unfortunately I can't figure out how to use that dispatcher:
I guess really the question I am asking is: how do I get a View through MvvmCross.
PS: I am actually surprised that most don't test the bindings; isn't it where the most amount of mistakes is likely to happen? I am pretty sure the project compiles even if I had bad binding :) scary kind of reminds me of early asp days.
PS: I have actually got another testProject that tests the ViewModels; on that testProject I have managed to hookup following the guidelines at
http://blog.fire-development.com/2013/06/29/mvvmcross-unit-testing-with-autofixture/
Which works beautifully; and also uses autoFixture, NSubstitute and xUnit: and I can't use any of them in Phone Test project.
From my experience, testing the bindings themselves is pretty unusual - most developers stop their testing at the ViewModel and ValueConverter level.
However, if you want to test the bindings, then this should be possible. I suspect the only problem in your current tests is that you haven't initialised any of the MvvmCross infrastructure and so MvxViewModel isn't able to propagate INotifyPropertyChanged events.
If you want to initialise this part of the MvvmCross infrastructure, then be sure to initialise at least:
the MvvmCross IoC container
the MvvmCross main thread dispatcher
This is similar to what is done in the unit tests in the N=29 video - see https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-29-TipCalcTest/TipCalcTest.Tests/FirstViewModelTests.cs#L57
For your app, you can do this using something like:
public static class MiniSetup
{
public static readonly MiniSetup Instance = new MiniSetup();
private MiniSetup()
{
}
public void EnsureInitialized(Context applicationContext)
{
if (MvxSimpleIoCContainer.Instance != null)
return;
var ioc = MvxSimpleIoCContainer.Initialize();
ioc.RegisterSingleton<IMvxTrace>(new MvxDebugOnlyTrace());
MvxTrace.Initialize();
var mockDispatcher = new SimpleDispatcher();
Ioc.RegisterSingleton<IMvxMainThreadDispatcher>(simpleDispatcher);
}
}
where SimpleDispatcher is something like:
public class SimpleDispatcher
: MvxMainThreadDispatcher
{
public readonly List<MvxViewModelRequest> Requests = new List<MvxViewModelRequest>();
public bool RequestMainThreadAction(Action action)
{
action();
return true;
}
}
If you want further MvvmCross functionality available (e.g. ShowViewModel navigation), then you'll need to provide further services - e.g. things like IMvxViewDispatcher - as the number of these increases, you might be better off just running through a full MvxSetup process (like your main app's Setup does)
I have seen lots of posts (and debates!) about which way round UnitOfWork and Repository. One of the repository patterns I favor is the typed generic repository pattern, but I fear this had lead to some issues with clean code and testability. Take the following repository interface and generic class:
public interface IDataEntityRepository<T> : IDisposable where T : IDataEntity
{
// CRUD
int Create(T createObject);
// etc.
}
public class DataEntityRepository<T> : IDataEntityRepository<T> where T : class, IDataEntity
{
private IDbContext Context { get; set; }
public DataEntityRepository (IDbContext context)
{
Context = context;
}
private IDbSet<T> DbSet { get { return Context.Set<T>(); } }
public int Create(T CreateObject)
{
DbSet.Add(createObject);
}
// etc.
}
// where
public interface IDbContext
{
IDbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T readObject) where T : class;
int SaveChanges();
void Dispose();
}
So basically I am using the Context property in each pattern to gain access to the underlying context.
My problem is now this: when I create my unit of work, it will effectively be a wrapper of the context I need the repository to know about. So, if I have a Unit Of Work that declares the following:
public UserUnitOfWork(
IDataEntityRepository<User> userRepository,
IDataEntityRepository<Role> roleRepository)
{
_userRepository = userRepository;
_roleRepository = roleRepository;
}
private readonly IDataEntityRepository<User> _userRepository;
public IDataEntityRepository<User> UserRepository
{
get { return _userRepository; }
}
private readonly IDataEntityRepository<Role> _roleRepository;
public IDataEntityRepository<Role> RoleRepository
{
get { return _roleRepository; }
}
I have a problem with the fact that the two repositories I am passing in both need to be instantiated with the very Unit Of Work into which they are being passed. Obviously I could instantiate the repositories inside the constructor and pass in the "this" but that tightly couples my unit of work to a particular concrete instance of the repositories and makes unit testing that much harder.
I would be interested to know if anyone else has headed down this path and hit the same wall. Both these patterns are new to me so I could well be doing something fundamentally wrong. Any ideas would be much appreciated!
UPDATE (response to #MikeSW)
Hi Mike, many thanks for your input. I am working with EF Code First but I wanted to abstract certain elements so I could switch to a different data source or ORM if required and because I am (trying!) to push myself down a TDD route and using Mocking and IOC. I think I have realised the hard way that certain elements cannot be unit tested in a pure sense but can have integration tests! I'd like to raise your point about Repositories working with business objects or viewmodels etc. Perhaps I have misunderstood but if I have what I see as my core business objects (POCOs), and I then want to use an ORM such as EF code first to wrap around those entities in order to create, and then interact with, the database (and, it's possible, I may re-use these entities within a ViewModel), I would expect a Repository to handle these entities directly in the context of some set of CRUD operations. The entities know nothing about the persistence layer at all, neither would any ViewModel. My unit of work simply instantiates and holds the required repositories allowing a transaction commit to be performed (across multiple repositories but the same context/ session). What I have done in my solution is to remove the injection of an IDataEntityRepository ... etc. from the UnitOfWork constructor as this is a concrete class that must know about one and only one type of IDataEntityRepository it should be creating (in this case DataEntityRepository, which really should be bettered names as EFDataEntityRepository). I cannot unit test this per se because the whole unit logic would be to establish the repositories with a context (itself) to some database. It simply needs an integration test. Hope that makes sense?!
To avoid dependency on each repository in your Unit of Work, you could use a provider based on this contract:
public interface IRepositoryProvider
{
DbContext DbContext { get; set; }
IRepository<T> GetRepositoryForEntityType<T>() where T : class;
T GetRepository<T>(Func<DbContext, object> factory = null) where T : class;
void SetRepository<T>(T repository);
}
then you could inject it into your UoW that would look like this:
public class UserUnitOfWork: IUserUnitOfWork
{
public UserUnitOfWork(IRepositoryProvider repositoryProvider)
{
RepositoryProvider = repositoryProvider;
}
protected IDataEntityRepository<T> GetRepo<T>() where T : class
{
return RepositoryProvider.GetRepositoryForEntityType<T>();
}
public IDataEntityRepository<User> Users { get { return GetRepo<User>(); } }
public IDataEntityRepository<Role> Roles { get { return GetRepo<Role>(); } }
...
Apologies for the tardiness of my response - I have been trying out various approaches to this in the mean time. I have marked up the answers above because I agree with the comments made.
This is one of those questions where there is more than one answer and it's very much dependent upon the overall approach. Whilst I agree that EF effectively provides a ready-made unit of work pattern, my decision to create my own unit of work and repository layers was to be able to control access to the database entities.
Where I struggled was in the need to be able to inject a repository into a unit of work. What I realised though was that in the case of EF, my unit of work was effectively a thin wrapper around multiple repositories with a Commit (SaveChanges) method. It was not responsible for executing specific actions such as FindCustomer etc.
So I decided that a unit of work could be tightly coupled to its specific type of DataRepository pattern. To ensure I had a testable pattern, I introduced a service layer that provided the facade for executing particular actions such as CreateCustomer, FindCustomers etc. These services that accepted an IUnitOfWork constructor parameter which provided access to the repositories (as interfaces) as well as the Commit method.
I was then able to create fakes of both unit of work and/ or repositories for testing purposes. This just left me with the decision of what could be unit tested with fakes and what needed to be integration tested with the concrete instances.
And this also gives me the opportunity to control what actions are performed on the database and how they are performed.
I'm sure there are many ways to skin this particular cat but the goals of provided a clean interface that is testable have been just about met with this approach.
My thanks to g1ga and Mike for their input.
When using Entity Framework (EF) (which I assume you're using) you already have a generic repository IDbSet. It's useless to ad another layer on top just to call EF methods.
Also, a repository works with application objects (usually business objects, but they can be view models or objects state). If you're just using db entities, you kinda defeat the Repository pattern purpose ( to isolate the business bojects from the database). THe original pattern deals only with busines objects, but it is a useful pattern outside the business layer too.
The point is that EF entities are Persistence objects and have (or should have) no relation to your business objects. You want to use the repository pattern to 'translate' the busines objects to persistence objects and viceversa.
Sometimes it might happen that an application object (like a viewmodel) to be the same with a persistence entity (and in that case you can use directly EF objects) but that's a coincidence.
About Unit of Work (UoW), let's say that's tricky. Personally, I prefer to use the DDD (domain driven design) approach and consider that any business object (BO) sent to the repoistory is a UoW, so it will be wrapped in a transaction.
If I need to update multiple BOs, I'll use a message driven architecture to send commands to the relevant BOs. Of course, that's more complicated and requires to be at ease with the concept of eventual consistency but I'm not depending on a specific RDBMS.
If you know that you'll be using a specific RDBMS and that will never be changed, you could start a transaction and pass the associated connection to each repository, with a commit at the end (that will be the UoW). If you're in a web setting, it's even easier, start transaction when the request begins, commit when requests ends (you can use an ActionFilter for ASp.Net Mvc).
However this solution is tied up to one RDBMS, so it won't apply to a NoSql or any storage which doesn't support transactions. For those cases, the message driven way is the best.
I've been reading Misko Hevery's classic articles about Dependency injection, and basically 'separating the object graph creation code from the code logic'.
The main idea seems to be "get rid of the 'new' operators", put them in dedicated objects ('Factories') and inject everything you depend on."
Now, I can't seem to wrap my head about how to make this works with objects that are composed of several other components, and whose job is to isolate those components to the outerworld.
Lame example
A View class to represent a combination of a few fields, and a button. All the components depend on a graphical ui context, but you want to hide it behind the interfaces of each sub-component.
So something like (in pseudo-code, language does not really matter I guess):
class CustomView() {
public CustomView(UIContext ui) {
this.ui = ui
}
public void start() {
this.field = new Field(this.ui);
this.button = new Button(this.ui, "ClickMe");
this.button.addEventListener(function () {
if (field.getText().isEmtpy()) {
alert("Field should not be empty");
} else {
this.fireValueEntered(this.field.getText());
}
});
}
// The interface of this component is that callers
// subscribe to "addValueEnteredListener"..)
public void addValueEnteredListener(Callback ...) {
}
public void fireValueEnteredListener(text) {
// Would call each listeners in turn
}
}
The callers would do something like :
// Assuming a UIContext comes from somewhere...
ui = // Wherever you get UI Context from ?
v = new CustomView(ui);
v.addValueEnteredListener(function (text) {
// whatever...
});
Now, this code has three 'new' operators, and I'm not sure which one Misko (and other DI proponents) are advocating to rid off, or how.
Getting rid of new Field() and new Button()
Just Inject it
I don't think the idea here is to actually inject the instances of Field and Button , which could be done this way :
class CustomView() {
public CustomView(Field field, Button button) {
this.field = field;
this.button = button;
}
public void start() {
this.button.addEventListener(function () {
if (field.getText().isEmtpy()) {
alert("Field should not be empty");
} else {
this.fireValueEntered(this.field.getText());
}
});
}
// ... etc ...
This makes the code of the component lighter, surely, and it actually hides the notion of UI, so the MetaForm component has clearly been improved in terms of readability and testability.
However, the burden is now on the client to create those things :
// Assuming a UIContext comes from somewhere...
ui = // wherever ui gets injected from
form = new Form(ui);
button = new Button(ui);
v = new CustomView(form, button);
v.addValueEnteredListener(function (text) {
// whatever...
});
That sounds really troubling to me, espacially since the client know has to all the inners of the class, which sounds silly.
Mama knows, inject her instead
What the articles seems to advocate is instead injecting a Factory to create the components elements.
class CustomView() {
public CustomView(Factory factory) {
this.factory = factory;
}
public void start() {
this.field = factory.createField();
this.button = factory.createButton();
this.button.addEventListener(function () {
if (field.getText().isEmtpy()) {
alert("Field should not be empty");
} else {
this.fireValueEntered(this.field.getText());
}
});
}
// ... etc ...
And then everything gets nice for the caller, because its just has to get the factory from somewhere (and this factory will be the only to know about the UI context, so hurray for decoupling.)
// Assuming a UIContext comes from somewhere...
factory = // wherever factory gets injected from
v = new CustomView(factory);
v.addValueEnteredListener(function (text) {
// whatever...
});
A possible drawback is that when testing the MetaForm, you will typically have to use a 'Mock' Factory that ... create Mocks version of the Field & Button classes. But obviously there is another drawback ...
Yo' Factory so fat!!
How big will the Factory get ? If you follow the pattern rigorously, then every single frigging component you ever want to create in you application at runtime (wich is typically the case for UI, right) will have to get its own createXXXXX methods in at least one factory.
Because now you need :
Factory.createField to create the field
Factory.createButton to create the button
Factory.createMetaForm to create the field, the button and the MetaForm when a client (say the MetaEditPage wants to use one)
And obviously a Factory.createMetaEditPage for the client..
... and its turtles all the way.
I can see some strategies to simplify this :
As much as possible, separate the parts of the graph that are created at "startup" time from the parts that are created at runtime (using an DI framework like Spring for the former, and factories for the latter)
Layer the factories, or collocate related objects in the same factories (a UIWidgetFactory would make sense for Field and Button, but where would you put the other ones ? In a Factory linked to the Application ? To some other logical level ?)
I can almost hear all the jokes from C guys that no Java app can do anything without calling a ApplicationProcessFactoryCreator.createFactory().createProcess().startApplication() chain of nonsense...
So my questions are :
I am completely missing a point here ?
If not, which strategy would you suggest to make the things bearable ?
Addendum : why I'm not sure dependency injection would help
Assume I decide to use dependency injection, with a guice-like framework. I would end up writing code like this :
class CustomView
#Inject
private Field fiedl;
#Inject
private Button button;
public void start() {
this.button.addEventListener(....
// etc...
And then what ?
How would my "Composition Root" make use of that ? I can certainely not configure a "singleton" (with a lowercase 's', as in 'the single instance of a class) for the Field and the Button (since I want to create as many instances of them as instances of MetaForm ?
It would not make sense to use a Provider, since my problem is not which instance of buttons I want to create, but just that I want to create it lately, with some configuration (for example its text) that only makes sense for this form.
To me DI is not going to help because I am new-ing parts of my component rather than Dependencies. And I suppose I could turn any subcomponent into a dependency, and let a framework inject them. It's just that injecting subcomponents looks really artificial and couter-intuitive to me, in this case... so again, I must be missing something ;)
Edit
In particular, my issue is that I can't seem to understand how you would test the following scenario :
"when I click on the button, if the Field is empty, there should be an error".
This is doable if I inject the button, so that I can call its "fireClicked" event manually - but it feels a bit silly.
The alternative is to just do view.getButton().fireClicked() , but that looks a bit ugly...
Well, you can use some DI Framework (Spring or Guice) and get rid of factory method completely. Just put some annotation on the field/constructor/set method and DI Framework will do the work. At unit-test use mock framework.
How about not being overly obsessed with the "no new" dogma ?
My take is that DI works well for - well you know, Dependencies, or Delegates. Your example is about composition and IMHO it absolutely makes sense that the owning entity (your CustomView) creates explicitly its components. After all, the clients of the composite do not even have to know that these components exist, how they are initialized or how you use them.
I'm building a Silverlight app using Jounce for my MVVM. I have a CustomerListViewModel (plural) which has a collection of CustomerViewModel objects (single).
I'm using Ninject for dependency injection, because my ViewModels will be depending on other classes (i.e. repositories, services, etc.).
Using the dependency injection is fairly easy, but now I'm a little stuck. When the CustomerListViewModel loads, it will go to the database (it already has its repository through DI) and get the Customer objects. These should be passed on to a CustomerViewModel.
How would I go about constructing these CustomerViewModel objects? I've always read that the Service Locator pattern is an anti-pattern, so this feels wrong:
private void GetCustomerss()
{
var customers = _customerRepository.GetAll();
IList<CustomerViewModel> customerViewModels = new List<CustomerViewModel>();
foreach (var customer in customers)
{
var customerViewModel = ObjectFactory.GetInstance<CustomerViewModel>();
customerViewModel.Model = customer;
customerViewModel.Add(customerViewModel);
}
Customers = new ObservableCollection<CustomerViewModel>(customerViewModels);
}
How could I avoid this anti-pattern? Or is it really not that bad?
This also makes my unittesting a little harder, because I can inject a mock ICustomerRepository into the CustomerListViewModel (in the constructor), but the ObjectFactory.GetInstance<CustomerViewModel>() will work as it should, and also resolve underlying dependencies of CustomerViewModel. This will then fail, because I haven't set up Ninject for these underlying dependencies.
Here I described how I dealt with such scenario: http://pglazkov.blogspot.com/2011/04/mvvm-with-mef-viewmodelfactory.html. It is about MEF, but the idea is the same.
Basically you can have a separate service called IViewModelFactory, using which you will create child view models. For unit tests you will be able to mock that service.