So I'm currently developing an application with Xamarin.Forms. Xamarin Forms uses the MVVM pattern, and I feel somewhat comfortable using this pattern, but I do have some issues. To keep this simple, I'll use an example of a single-page application. This is how I've structured it so far:
MainView.xaml //View
MainView.xaml.cs //Code behind
MainViewModel.cs //ViewModel
DataAccessHelper //DAL layer helping me communicate with a REST-API & DB
Models
Other util classes
So for stuff like where to put the logic for populating the collections used in the view with data from the REST-API; that's pretty clear. I use the ViewModel for this, and the ViewModel communicates with my DataAccessHelper to do stuff. While a ViewModel should contain presentation logic, it's only natural to do this. However, what about authentication? This is not, in any way, related to the data presented on the screen. But it's a required step that has to be done before I can do any other requests, obviously.
So this is my question:
Should the view communicate directly with my DataAccessHelper? Say, in the constructor in the code-behind I do a direct call to my DataAcess helper to authenticate, then if that goes alright, I proceed to use the ViewModel's methods to fetch the data and populate my components? Or should I also place a method in the ViewModel that the view uses to authenticate?
I.e. this:
public partial class MainPage
{
private MainPageViewModel ViewModel
{
get { return BindingContext as MainPageViewModel; }
}
public MainPage()
{
ViewModel.Authenticate();
ViewModel.LoadCountries();
ViewModel.LoadCities();
InitializeComponent();
}
}
Versus this:
public partial class MainPage
{
private MainPageViewModel ViewModel
{
get { return BindingContext as MainPageViewModel; }
}
public MainPage()
{
var dataAccessHelper = new DataAcessHelper();
dataAccessHelper.Authenticate();
ViewModel.LoadCountries();
ViewModel.LoadCities();
InitializeComponent();
}
}
Or neither? What would be best practice here? Maybe even create an Authentication-object that can be used. This object would contain wrapper-methods for communicating with the DAL, and stuff like persisting/getting your user credentials used in the authentication-request?
Thoughts?
Defininately option A. Always have your VM talk to your services/repositories through commands/methods. Actually, you should be injecting your AuthenticationService into your ViewModel but thats another subject.
This is less of a factual answer and more of an opinion on app architecture using MVVM, but here goes:
First, it goes against MVVM to have the View talking directly to anything that's not purely UI-related. No app logic, no backend calls, nada.
I would instead recommend a healthy dose of Separation of Concerns; have a login View/ViewModel that handles the process of gathering and verifying credentials, and the result of a successful validation would be a navigation to the view(s) that you are trying to gate access to. If there is a token or some other object that you need to supply to your backend services, supply that to the ViewModel's constructor (ideally using an IoC container coupled with a ViewModelLocator), and it will in turn hand that to whatever backend services it consults for Model data.
Related
I'm building an app using ionic and angularfire and I didn't understand the logic behind the maintenance of sessions using the $onAuth. I know how the function works, but I do not know exactly where to use it..
Do I need to inject it the $onAuth in every controller and veirfy if the authData is null or not(to then use $unauth and redirect the user properly) OR there is a way to have this function($onAuth) centralized in an other file and let ir listining and only then do something if the authData is null?
Feel free to ask if the question it's not clear!
Thanks in advance to all who help! :)
There are many possible ways you can take to your goal. The worst one would be to take the way you described and have it in every controller, because you should keep your code DRY. Your goal clearly is to only write the method for $onAuth once.
One easy approach is to have either a MainController that is instanciated at the very top of your dom (like on of the outermost html elements, e.g. the body tag). You can then use your child controllers to access data from the parent controller like explained e.g. here. You would inject the $onAuth service in the main controller, and let it do your logic, and you could access the main controller's $scope with your authData from all its child controllers.
A more reusable part would be to write a service that you can access from all of your controllers. Find a great tutorial here.
The easiest way is to have the $onAuth in your module's run block and save it in your $rootScope that can also be accessed later from all controllers. Here is an auth example. You would write
angular.module('your-module').run(function($rootScope, Auth) {
$rootScope.auth = Auth;
$rootScope.auth.$onAuth(function(authData) {
$rootScope.authData = authData;
//do anything you want here, e.g. redirect
});
}
In an MVVM LOB app, say I have a ViewModel that allows the user to launch a long-running business process, let's pretend it's the workflow of creating an order.
When the CreateOrder command executes on the ViewModel, how does the UnitOfWork object (DbContext in EF) get created and managed throughout its lifetime? Is the ViewModel responsible for managing its lifetime, passing it off to some wizard dialog service, and eventually committing it to the database? Seems like a violation of SRP. But if the ViewModel doesn't manage this process, who/what does? Some kind of OrderManagerService?
Also, where does IoC/Dependency Injection fit into this picture? For unit testing obviously I don't want the ViewModel to instantiate a new UnitOfWork that's coupled to the database. But if this business process only launches if/when a user requests it, obviously a UnitOfWork can't be injected into the ViewModel upon app startup.
Thanks
I think you nailed it with the OrderManager service. You really don't want the accumulation of this change occurring in a view layer. Create a PendingOrder object to accumulate your UnitOfWork pattern. Put in in a memory-store, or an external data store (probably memory).
This keeps your view layer clean, and makes testing easier.
It kind of dissolves your IOC/testing issue. Unit test your PendingOrder code independently of your UI. Then you can mock/stub it for your UI testing.
I have a GWT MVP application using Activities and Places. This is inspired by Mauro Bertapelle's sample (in this thread), apparently based on some of Thomas Broyer's work.
Here's the problem: I have LoginActivity make an RPC call, which for a successful login, returns a User. This user has a role (e.g., admin, regular user, guest). Several Views and Activities, including a NavigatorView, depend on this role for what they show or do. How do I get this User instance to the other Activities?
I do not have a ClientFactory; injection (Gin) is used for instantiating the Views in the ActivityProviders which provide my Activities/Presenters, and the ActivityProviders are injected into my ActivityMapper. So this may reduce to a Gin question: how do I get the user reference where it's needed? This seems to be similar to this SO question about global references in MVP.
Consider me a Gin newbie, this is my first attempt at using it. I'm guessing there is a "Gin way" to make this happen, but I don't know Gin well enough to know the best way to do this (if Gin should be used at all).
Much thanks.
Edit 1: Despite my best efforts searching SO for a similar question, I just found this question which is pretty much identical to mine (is the SO algorithm for finding "Related" links better than the search?). I'm thinking that the Gin answer by David is on the right track.
I don't think that an EventBus solution is possible. I'm following the Google guidelines which involve instantiating Activity at every Place change, so a single Event by itself will not suffice.
Something that I'm using on the server-side with Guice, and would work just as well on the client-side, is to bind to a custom Provider. In your case though, you'd have to make the provider a singleton and push the value into it from your RPC callback (rather than pulling it from some context).
You'd first need a specific provider:
#Singleton
public class CurrentUserProvider implements Provider<User> {
private User currentUser;
public User get() { return currentUser; }
public void setCurrentValue(User currentUser) {
this.currentUser = currentUser;
}
}
You'd bind User to the provider: bind(User.class).toProvider(CurrentUserProvider.class)
In your RPC callback you'd inject a CurrentUserProvider so you can setCurrentValue but everywhere else you'd inject Provider<User> to keep CurrentUserProvider as an implementation detail. For very short-lived objects, you could directly inject a User value rather than a Provider<User>.
If you need to notify objects of the value change, you could dispatch an event on the global event bus.
Alternately, you could always use the concrete CurrentUserProvider type (which wouldn't have to implement Provider anymore) and possibly make it a HasValueChangeHandlers so you could register listeners on it rather than on the event bus (but you'd have to clean-up after yourself in your activities' onStop and onCancel to avoid memory leaks, whereas it's taken care of automatically if you register handlers on the event bus in onStart).
(if you ask me, I'd rather go away with authenticating from within the app whenever possible)
I had similar requirements on a recent project.
When I get a reply from login (or logout) RPC I send a custom AuthenticationEvent on EventBus. All activities that are interested in this listen for this event. AuthenticationEvent has a reference to AppUser object which is null if user just logged out. AppUser contains all necessary data (privileges, groups, etc..) so that activities can inspect it and act upon it.
About global references: you can have a class with static methods providing data that you need. This class internally holds singleton references to needed instances. In my example I have static method AppUtils.getCurrentUser(). Internally it holds a reference to AppUser and also listens to AuthenticationEvent to set/reset this field.
As a side note: don't rely on client side to enforce access restrictions - you should separate your RPC servlets into two groups: public and private. Public can be accessed by anybody (this is basically login/logout RPC and some other public info RPC), while private RPC requires user to be authenticated. Access restrictions can be set per path/servlet: http://code.google.com/appengine/docs/java/config/webxml.html#Security_and_Authentication
Update:
As you noted, class with static methods is not advisable in this setup, because it is not replaceable and this prevents testing (which is the whole point of using GIN).
The solution is to inject a utility class holding globals (AppUtils) into activities that need the globals. AppUtils should be declared singleton in GIN configuration as one instance is enough for the whole app.
To use Provider or not is just a question if you want to delay the initialization of dependencies (AppUtil is dependency). Since AppUtils is a singleton for the whole app it makes no sense to have it lazy initialized.
Sometimes you will have a situation where you have multiple Activities shown on screen (in my case it was MenuBar and InfoBar). In this case, when user logs in you will need a way to notify them of the change. Use EventBus.
I have noticed that with iPhone programming you kind of need to retrieve your data from within the ViewController because of the way the data is retrieved.
e.g.: ViewDidLoad is called. You start retrieving the data and then when its finished a message is sent to your viewcontroller e.g. requestFinished and this is where you configure/refresh your UI.
The problem that I have with this approach is that I have a bunch of web service code and XML building and parsing all in my view controller.
Does anyone know if this is the correct approach or is there a better way to do this.
In .NET I would have classes specifically for retrieving data from webservices and I would simply call the web service to fetch the data and I could use the same web service at various places inside my app.
There is no reason to do that different in Objective-C/Cocoa. You should create a class that handles the web service and notifies the view controller when data is available.
No, it absolutely isn't the correct approach. The key to this is the MVC paradigm, - model, view, controller - your data classes are perfectly suited to being the M = model so put all your data handling code inside a dedicated model class.
Because the url handling is hopefully asynchronous, your model will still need to inform your view controller when various events have taken place. You have a couple of choices here but the most appropriate is probably to use a delegate pattern so that the model can basically initiate a call back to the view controller when it has data that needs displaying etc.
(The other approach would be to use notifications which is less tightly coupled, and perfectly viable in this scenario, but delegates would be more appropriate).
Well you could create a parse that will parse your XML in a seprate class, and even your http request can be in a seprate class.
There is no need to do every thing in the the on viewcontroller.
Just be sure to create delegate or use the notification center if you are using threads. Set the delegate on either the request or parse to nil if the view controller get unloaded.
I'm working on a largely navigation-based iPhone app that communicates with a REST API using OAuth, and I'd like to know how my classes should best communicate with each other. I've got multiple UITableViews, multiple UITableViewDataSources, an API class, and a view controller that handles authentication in a web view.
Here's how I have things structured now:
My UIApplicationDelegate owns an instance of the class that knows how to communicate with the REST API
I can ask the API class to call a REST method, and it returns with the data (it wraps ASIHTTPRequests to handle OAuth transparently, so it doesn't currently know which object asked for the data)
If the user isn't authenticated, I can ask the API class to initiate the OAuth process
The OAuth dialog can be presented, and the application granted access to the API on the user's behalf
The trouble I'm running into is that my UITableViewDataSources need to communicate with the API class to fetch their data, but authentication might need to happen first, which involves a modal authentication view presented by a view controller.
Would it be better to have each UITableViewDataSource model own its own instance of the API class, or should the API class be a singleton?
If each model owns an instance of the API class, how should it communicate to the view controller that authentication needs to happen?
If the API class is a singleton, how should it interact with multiple models and view controllers to present the authentication dialog? Delegates for each? Post NSNotifications?
Some other way I haven't thought of?
Really, I think the core of the problem is that I have one class that's primarily used for data fetching purposes, but it might need user interaction to do so successfully.
I typically use a singleton exactly in the way you describe and it works well. Here is how I would answer your questions.
Singleton
N/A. Use a singleton.
Notifications work well, but I tend to prefer to pass a delegate with each request and then keep a hold of it inside the singleton until the request has finished at which point I just call back to the delegate with a success or failure message. Notifications can get pretty messy if you have multiple view controllers that are live in your navigation stack all potentially listening and responding depending. I've seen that introduce some really weird bugs. If you're careful, notifications work just fine, but passing a delegate allows you to associate a specific delegate, typically the current view controller, with a specific request which is often ideal.
I think you're on the right track.