Here's an excerpt from a book I'm reading about application design with MVC:
Ideally, the view is so simple and
logic-free as to need virtually no
testing. Users (and developers before
users) can reasonably test the view by
simply looking at the pixels on the
screen. Anything else beyond pure
graphical rendering should ideally be
taken out of the view and placed in
the controller and model. This
includes, for example, the logic that
determines whether a certain button
should be enabled or grayed out at
some point.
what does the bold statement mean to you? what would this look like?
thanks,
rod.
The logic that decides when to enable or disable the button should be residing in the controller and simply calls a method e.g view.EnableContinueButton() to enable/disable the button on the page.
The actual code to enable/disable the button on the page itself should be implemented in the view e.g a EnableContinueButton() method then which calls something like btnContinue.Enable().
Simply put, the view should concern itself with the UI details (show/hide/enable/disable UI elements) and leave all business logic processing to the controller. In this way, the controller does not need to concern itself with the UI elements and the view works independently of the actual business logic.
e.g in the Controller,
public void ProcessOrder()
{
if (!controller.ValidateOrder(model.OrderNo))
view.EnableContinueButton(false);
else
// Process the order
...
}
and in the View
public void EnableContinueButton(bool enabled)
{
btnContinueButton.Enabled = enabled;
}
Frankly I haven't got much experience in MVC (implemented in one project a while back) but I hope the logic separation between controller and view is clear enough.
This is what that bold statement means to me:
The controller is going to be full of nested if statements
The model (or viewmodel) is going to be full of properties to help render the page specific ways, making the object graphs difficult to maintain.
While I think the analysis should not be made in the view, the condition should be set so the button only has to think - show or not show.
eg. only show the examinee details button if the examinee is male.
You either create a viewmodel property ShowExamineeDetails. The view will check if this is ture or not.
the ShowExamineeDetails = is examinee Male?
code should be in the controller.
As for testing, I am yet to find an app that "...needs virtually no testing..."
Related
I've been reading about Clean Architecture from Robert Martin and more specifically about VIPER.
Then I ran into this article/post Brigade’s Experience Using an MVC Alternative which describes pretty much what I'm currently doing.
After actually trying to implement VIPER on a new iOS project, I've ran into some questions:
Is it ok for the presenter to query information in the view or should the "information passing" always start from the view?
For example, if the view triggered some action in the presenter, but then, depending on the parameters passed through that action, the presenter might need more information.
What I mean is: the user tapped “doneWithState:”, if state == “something”, get information from the view to create an entity, if state == “something else”, animate something in the view. How should I handle this kind of scenario?
Lets say a "module" (group of VIPER components) decide to present another module modally. Who should be responsible for deciding if the second module will be presented modally, the first module's wireframe or the second module's wireframe?
Also, lets say the second module's view is pushed into a navigation controller, how should the "back" action be handled? Should I manually set a "back" button with an action in the second module's view controller, that calls the presenter, that calls the second module's wireframe that dismiss and tells the first module's wireframe that it was dismissed so that the first module's view controller might want to display something?
Should the different modules talk only through the wireframe or also via delegates between presenters? For example if the app navigated to a different module, but after that the user pressed "cancel" or "save" and that choice needs to go back and change something in the first module (maybe display an animation that it was saved or remove something).
Lets say a pin was selected on a map, than the PinEditViewController is displayed. When going back, the selected pin's color might need to change depending on use actions on the PinEditViewController. Who should keep the state of the current selected pin, the MapViewController, the MapPresenter or the MapWireframe in order for me to know, when going back, which pin should change color?
1. May the Presenter query information from the view
To answer this to your satisfaction, we need more details about the particular case. Why can't the view provide more context information directly upon callback?
I suggest you pass the Presenter a Command object so the Presenter doesn't have to know what to do in which case. The Presenter can execute the object's method, passing in some information on its own if needed, without knowing anything about the view's state (and thus introducing high coupling to it).
View is in a state you call x (opposed to y and z). It knows about its state anyway.
User finishes the action. View informs its delegate (Presenter) about being finished. Because it is so involved, it constructs a Data Transfer Object to hold all usual information. One of this DTO's attributes is a id<FollowUpCommand> followUpCommand. View creates a XFollowUpCommand (opposed to YFollowUpCommand and ZFollowUpCommand) and sets its parameters accordingly, then putting it into the DTO.
Presenter receives the method call. It does something with the data no matter what concrete FollowUpCommand is there. Then it executes the protocol's only method, followUpCommand.followUp. The concrete implementation will know what to do.
If you have to do a switch-case/if-else on some property, most of the time it'd help to model the options as objects inheriting from a common protocol and pass the objects instead of the state.
2. Modal Module
Should the presenting module or the presented module decide if it's modal? -- The presented module (the second one) should decide as long as it's designed to be used modally only. Put knowledge about a thing in the thing itself. If its presentation mode depends on the context, well, then the module itself can't decide.
The second module's wireframe will receive message like this:
[secondWireframe presentYourStuffIn:self.viewController]
The parameter is the object for which presentation should take place. You may pass along a asModal parameter, too, if the module is designed to be used in both ways. If there's only one way to do it, put this information into the affected module (the one presented) itself.
It will then do something like:
- (void)presentYourStuffIn:(UIViewController)viewController {
// set up module2ViewController
[self.presenter configureUserInterfaceForPresentation:module2ViewController];
// Assuming the modal transition is set up in your Storyboard
[viewController presentViewController:module2ViewController animated:YES completion:nil];
self.presentingViewController = viewController;
}
If you use Storyboard Segues, you'll have to do things a bit differently.
3. Navigation hierarchy
Also, lets say the second module's view is pushed into a navigation controller, how should the "back" action be handled?
If you go "all VIPER", yes, you have to get from the view to its wireframe and route to another wireframe.
To pass data back from the presented module ("Second") to the presenting module ("First"), add SecondDelegate and implement it in FirstPresenter. Before the presented module pops, it sends a message to SecondDelegate to notify about the outcome.
"Don't fight the framework", though. Maybe you can leverage some of the navigation controller niceties by sacrificing VIPER pure-ness. Segues are a step into the direction of a routing mechanism already. Look at VTDAddWireframe for UIViewControllerTransitioningDelegate methods in a wireframe which introduce custom animations. Maybe this is of help:
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [[VTDAddDismissalTransition alloc] init];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source
{
return [[VTDAddPresentationTransition alloc] init];
}
I first thought that you'd need to keep a stack of wireframes similar to the navigation stack, and that all "active" module's wireframes are linked to one another. But this isn't the case. The wireframes manage the module's contents, but the navigation stack is the only stack in place representing which view controller is visible.
4. Message flows
Should the different modules talk only through the wireframe or also via delegates between presenters?
If you directly send another module B's object a message from Presenter A, what should happen then?
Since the receiver's view is not visible, an animation cannot start, for example. The Presenter still has to wait for the Wireframe/Router. So it has to enqueue the animation until it becomes active again. This makes the Presenter more stateful, which makes it harder to work with.
Architecture-wise, think about the role the modules play. In Ports/Adapters architecture, from which Clean Architecture burrows some concepts, the problem is more evident. As an analogy: a computer has many ports. The USB port cannot communicate with the LAN port. Every flow of information has to be routed through the core.
What's at the core of your app?
Do you have a Domain Model? Do you have a set of services which are queried from various modules? VIPER modules center around the view. The stuff modules share, like data access mechanisms, don't belong to a particular module. That's what you may call the core. There, you should perform data changes. If another module becomes visible, it pulls in the changed data.
For mere animation purposes, though, let the router know what to do and issue a command to the Presenter depending on the module change.
In VIPER Todo sample code:
The "List" is the root view.
An "Add" view is presented on top of the list view.
ListPresenter implements AddModuleDelegate. If the "Add" module is finished, ListPresenter will know, not its wireframe because the view is already in the navigation stack.
5. Keeping state
Who should keep the state of the current selected pin, the MapViewController, the MapPresenter or the MapWireframe in order for me to know, when going back, which pin should change color?
None. Avoid statefulness in your view module services to reduce cost of maintaining your code. Instead, try to figure out whether you could pass a representation of the pin changes around during changes.
Try to reach for the Entities to obtain state (through Presenter and Interactor and whatnot).
This doesn't mean that you create a Pin object in your view layer, pass it from view controller to view controller, change its properties, and then send it back to reflect changes. Would a NSDictionary with serialized changes do? You can put the new color in there and send it from the PinEditViewController back to its Presenter which issues a change in the MapViewController.
Now I cheated: MapViewController needs to have state. It needs to know all pins. Then I suggested you pass a change dictionary around so MapViewController knows what to do.
But how do you identify the affected pin?
Every pin might have its own ID. Maybe this ID is just its location on the map. Maybe it's its index in a pin array. You need some kind of identifier in any case. Or you create an identifiable wrapper object which holds on to a pin itself for the duration of the operation. (That sounds too ridiculous for the purpose of changing the color, though.)
Sending Events to Change State
VIPER is very Service-based. There are lots of mostly stateless objects tied together to pass messages along and transform data. In the post by Brigade Engineering, a data-centric approach is shown, too.
Entities are in a rather thin layer. On the opposite of the spectrum I have in mind lies a Domain Model. This pattern isn't necessary for every app. Modeling the core of your app in a similar fashion may be beneficial to answer some of your questions, though.
As opposed to Entities as data containers into which everyone might reach through "data managers", a Domain protects its Entities. A Domain will inform about changes proactively, too. (Through NSNotificationCenter, for starters. Less so through command-like direct message calls.)
Now this might be suitable for your Pin case, too:
PinEditViewController changes the pin color. This is a change in a UI component.
The UI component change corresponds to a change in your underlying model. You perform the changes through the VIPER module stack. (Do you persist the colors? If not, the Pin Entity is always short-lived, but it's still an Entity because its identity matters, not just its values.)
The corresponding Pin has changed color and publishes a notification through NSNotificationCenter.
By happenstance (that is, Pin doesn't know), some Interactor subscribes to these notifications and changes its view's appearance.
Although this might work for your case, too, I think tying the edit
This answer may be a bit unrelated, but I'm putting it here for reference. The site Clean Swift is an excellent implementation of Uncle Bob's "Clean Architecture" in swift. The owner calls it VIP (it still contains the "Entities" and the Router/wireframe though).
The site gives you XCode templates. So let's say you want to create a new scene (he calls a VIPER modules, "scenes"), All you do is File->new->sceneTemplate.
This template creates a batch of 7 files containing all the headache of the boilerplate code for your project. It also configures them so that they work out of the box. The site gives a pretty thorough explanation of how every thing fits together.
With all the boiler plate code out of the way, finding solutions the questions you asked above is a bit easier. Also, the templates allow for consistency across the board.
EDIT -> In regards to the comments below, here's an explanation as to why I support this approach -> http://stringerstheory.net/the-clean-er-architecture-for-ios-apps/
Also this one -> The Good, the bad, and the Ugly about VIPER in iOS
Most of your questions are answered on this post: https://www.ckl.io/blog/best-practices-viper-architecture (sample project included). I suggest you pay special attention to the tips for Modules initialization/presentation: it's up to the source Router to do it.
Regarding back buttons, you can use delegates to trigger this message to the desired module. This is how I do it and it works great (even after you insert push notifications).
And yes, modules can definitely talk to each other by using delegates as well. It's a must for more complex projects.
I'm somewhat new to MVC and iOS development, and I can't seem reconcile how UI styling fits into this paradigm.
My view of MVC is built using storyboards, and I can apply primitive styling through Xcode's attribute inspector, but anything more complicated I have to use the Controller to style. For example:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated]; // required
// set background color of view
[[self view] setBackgroundColor:[UIColor darkGrayColor]];
}
This seems to be a clear violation of MVC, as I'm applying style logic inside of the controller's code. I find this analogous to writing an HTML app and instead of using style sheets, I write code to apply styles locally in JavaScript. Is this a weakness of iOS or am I just doing it wrong?
Taken from Apple's docs :
some controller objects might also tell a view object to change an
aspect of its appearance or behavior
And it does make sense as the view is supposed to be passive and only reflect the application state as a UI and the controller will "tell" the view if some of its content needs to be changed according to user actions. (e.g background change, visibility of controls etc...)
Actually the screen is your "view", and your "controller" is sending a message to your view to use a different color for the background.
If you had a data object that held the screen color, that could be your "model". In that case, you'd be passing the data from your model to the view through the controller.
You can mix and match the two within your code. If you want you can even manipulate Model details in the Controller as well. The MVC is not strictly enforced in the general guidelines of the pattern, but if you want to stay true to the paradigm you just have to refrain from using M or V in C.
View entails cosmetics and aesthetics of the forms as well as styling, but bottom line, you can manipulate these facets from the controller...
As others said, if you understand colors and styles as data, it is ok to let the controller take care for it.
But you also could subclass UIviews and internally set the style.
Let's say, you have a TrafficLightView : UIView with a property id trafficLight. you could overwrite the setter and set the background color of the view accordingly to the state of the object. trafficLight.
I'm learning iOS development stuff and what I have found in tutorials and books is that controller layer usually has access to the View's controls directly (textfields, labels etc.). Let's consider such an example:
Assume, that View has a label called lblResult and a textfield called txtDataToAnalyze. Than in controler interface we've got something like this:
#property (nonatomic, retain) IBOutlet UILabel* lblResult;
#property (nonatomic, retain) IBOutlet UITextField* txtDataToAnalyze;
and some #synthesize statements in the implementation file.
I have some experience with JavaSwing development, where most of thinks I'm writing manually without any GUI Builders, and what I usually do in MVC is to access the View's controls via getters/setter. For example: void setResult(String resString); or String getDataToAnalyze();. In that way, controller knows only what pieces of information are displayed in the view, and not how are they displayed. I think it is more flexible (it is easier to change the view layer later).
I know that iOS has some specific rules, has introduced XIB/NIB files etc so maybe my doubts are completely useless in case of iPhone/iPad development. But I am going to write some more serious application for iOS (actually "rewrite" it from Java Swing) and that's why I would like to ask you:
Do you think, I should change the way I am thinking and get accustomed to that new (for me) approach (xib files, creating GUI using drag&drop and providing controler with information about how data should be displayed in view) ?? Did you have similar doubts when starting with iOS?
Short answer:
Yes, I think you should definitely spend a little time getting accustomed to working with Interface Builder (IB) to make NIBs and storyboards and let IB create the IBOutlet and IBAction references for you for those controls with which you need to interact. Once you're proficient at it, you'll be impressed by your productivity in generating easily maintained code. Don't dismiss IB too quickly.
In terms of letting the controller interact directly with the IBOutlet and IBAction references, this is common practice for simple user interfaces. If you have some real world examples, post a new question with a screen snapshot and we can offer more practical guidance.
Long answer:
Part of your question seems to be driven by the apprehension in seeing view controllers that are doing detailed interaction with a view's controls. The thing is, if you want to isolate your controller from some of the implementation details of the view, then go ahead and subclass the view and put the view specific stuff in there. IB can interface with both view controller subclasses as well as view subclasses. So you can happily use IB and still isolate your view controller from some of these implementation details.
Personally, I only do this subclassing of UIView when the view hits some subjective complexity threshold (e.g. for me, that threshold is when I find myself doing some complicated animation, such as using CADisplayLink; complicated gesture recognizers, etc.). I also subclass those subviews that are logical entities of their own (e.g. UITableViewCell or UICollectionViewCell). But for simple views where I'm interacting with my model to setting a control's properties, interacting with text fields, etc., I think putting that in the view controller is fine. Having said that, if I have a lot of view-specific code in my controller which has nothing to do with the integration of my model with my view, then start subclassing the UIView and shifting the view-only code into that.
Implicit in your question is the notion of programmatically building view rather than using NIBs/storyboards. In my opinion, using Interface Builder (IB) to build your UI is much easier to develop and maintain. There might be some pedagogical value to doing a test project where you build your views programmatically, so you really understand what's going on, but after that, I think you'll find yourself quickly gravitating to storyboards. And you'll get plenty of chances to write your own non-IB code when you start doing things beyond the capabilities of the standard IB controls (e.g. complicated custom container views, etc.). There are definitely those who prefer to develop views programmatically, but I don't think you can beat the development speed and ease of maintenance of IB generated UIs.
I general, the controller does not know about the view, but the view knows about the controller.
The gang of four book says:
"MVC also lets you change the way a view responds to user input without changing its visual presentation. You might want to change the way it responds to the keyboard, for example, or have it use a pop-up menu instead of command keys. MVC encapsulates the response mechanism in a Controller object. There is a class hierarchy of controllers, making it easy to create a new controller as a variation on an existing one.
A view uses an instance of a Controller subclass to implement a particular response strategy; to implement a different strategy, simply replace the instance with a different kind of controller. It's even possible to change a view's controller at run-time to let the view change the way it responds to user input. For example, a view can be disabled so that it doesn't accept input simply by giving it a controller that ignores input events.
The View-Controller relationship is an example of the Strategy (315) design pattern. A Strategy is an object that represents an algorithm. It's useful when you want to replace the algorithm either statically or dynamically, when you have a lot of variants of the algorithm, or when the algorithm has complex data structures that you want to encapsulate."
I started working as a iOS developer about a year and a half ago, and I'm having some trouble with software architecture and organization. I use Apple's recommended Model-View-Controller paradigm, and my code is generally very hierarchical: if a screen has (for example) a HUD, a control panel, and a display area, I have a main controller for the screen and sub-controllers for the HUD, control panel, and display area. The sub-controllers generally have no knowledge of their neighboring controllers and use methods in the main controller to interact with them.
However, especially in games, I often run into hierarchy-breaking problems that just can't be elegantly solved with this model. For instance, let's say I have a coin in the control panel area that I want to animate flying to the HUD. I can either animate the original coin to the new position, which would require a method like animateCoinToPosition: in the control panel sub-controller and a method like getPositionForFinalCoinPositionInHUD in the main controller; or, I can hide the original coin and create a duplicate coin either in the main controller or the HUD controller, which would require a delegate method like animateCoinToHUDFromStartingPosition:. I don't like having such oddly-specific methods in my controllers, since they only really exist to solve one problem, and additionally expose the hierarchy. My ideal solution would be to have a single method called animateCoinToHUD, but this would require flattening the entire hierarchy and merging the three controllers into one, which is obviously not worth it. (Or giving the sub-controllers access to their siblings — but that would essentially have the same effect. The sub-controllers would then have dependencies with each other, creating a single messy spiderweb controller instead of a main controller and three mostly independent sub-controllers.)
And it often gets worse. What if I want to display a full-screen animation or particle effect when moving the coin? What if my coin is a lot more complicated than a simple sprite, with many subviews and details, to the point where creating a duplicate coin using animateCoinToHUDFromStartingPosition: is inefficient? What if the coin flies to the HUD but then returns to the control panel? Do I "lend" the coin view to the main controller and then take it back when the animation completes, preserving the original position/z-order/etc. in temporary variables so that they can be restored? And another thing: logically, code that concerns multiple sub-controllers belongs in the main controller, but if these interactions are common, the main controller grows to be many thousands of lines long — which I've seen happen in many projects, not just my own.
Is there a consistent way to handle these hierarchy-breaking effects and actions that don't require duplicate code or assets, don't bloat my controllers, and elegantly allow me to share objects between sub-controllers? Or am I using the wrong approach entirely?
So, I think you may be thinking about the "never go up" the hierarchy a little too literally.
I think the idea is that you don't know specifically what the parent is, but you can define a protocol and know that whatever your parent object is it responds to said protocol. You ideally test in code to confirm that it responds to that protocol. Then use the protocol to send the message in a generic way wherein you pass the coin object to the parent object and let the parent object animate it off the screen and into the HUD.
The sub-controllers then have a id<parent_protocol> parent; instance variable and their initialization method takes one of those as a parameter. Given your description you already have something like this in place, or at least enough to implement "sub-controllers generally have no knowledge of their neighboring controllers and use methods in the main controller to interact with them" as you say.
So the idea, from a design perspective is that the coin pickup happens in the Display panel and all it knows is that it's parent object has a pickupCoin: method that will do whatever is appropriate with a picked up coin. The Display panel doesn't know it goes to the HUD, or whatever, just that picked up coins get handled by the parent controller's pickupCoin: method.
The OOP design philosophy here is that all knowledge of the parent is encapsulated in the protocol definition. This makes the child & parent more loosely coupled so that you could swap in any parent that implemented that protocol and the children would still work fine.
There are looser couplings you could use (globally posted notifications say), but in the cases you describe I think something like what I've outlined is probably more appropriate & likely more performant.
does that help?
I am developing an iPad application that is essentially a sequence of user instructions to mimic a real life system test, with the ability to make modifications on each view if components were to fail (indicating issues that will need to be resolved).
The problem I am having is that the default behaviour of the views seems to be that as I progress forward through the hierarchy, it retains the state of each view, but if I progress back and then move forward again it will have reset the screen.
What I would like to do is have each view save its state, regardless of how the user leaves that screen, so that they can be confident that their work is preserved even if they need to return to a previous step.
Is there any way of doing this? Or do I need to fundamentally reconsider my design?
You need model objects for your views. These could be as simple as dictionaries or as involved as a custom class for each view.
Each view's controller must update its associated model with the changes made via its interface before the view goes off-screen. When it reappears, the VC will update the display with the information from the model.
This follows the dominant Cocoa paradigm of Model-View-Controller (see also: Cocoa Design Patterns); your views display information, your models store information, and the controllers mediate and translate between the two of them.
How to update a model from the view depends heavily on the design of your model. Here's a mockup that may or may not be helpful. All the things named xField are outlets to UITextFields.
// When the view is taken off screen
- (void) viewWillDisappear {
// Assume that when created, view controller is given a pointer
// to the relevant model object (probably by the previous view
// controller)
[model setNameOfHorse:[[self horseNameField] text]];
NSUInteger newBetValue;
newBetValue = [[dollarValueFormatter
numberFromString:[[self betField] text]]
unsignedIntegerValue];
[model setBet:newBetValue];
[model setNote:[[self noteField] text];
}