Let's say we have the following classes:
View
#interface ArticleView : UIView
#property IBOutlet UILabel *titleLabel;
#property IBOutlet UILabel *bodyLabel;
#end
Model
#interface Article : NSObject
#property NSString *title;
#property NSString *body;
#end
Controller
#interface ArticleViewController : UIViewController
#property Article *myArticle;
#property ArticleView *myArticleView;
- (void)displayArticle;
#end
#implementation
- (void)displayArticle {
// OPTION 1
myArticleView.titleLabel.text = myArticle.title;
myArticleView.bodyLabel.text = myArticle.body;
// ... or ...
// OPTION 2
myArticleView.article = myArticle;
}
#end
OPTION 1
PRO: Both the view and model aren't coupled to each other.
CON: The controller needs to know the details of both the model and view.
OPTION 2
PRO: The controller code is light and flexible (if the view or model change, the controller code stays the same.
CON: The view and model are coupled and therefore less reusable.
In OPTION 2, ArticleView will have to be changed to hold a reference to the model:
#interface ArticleView : UIView
#property IBOutlet UILabel *titleLabel;
#property IBOutlet UILabel *bodyLabel;
#property Article *article;
#end
The article setter can then be overwritten to update the view accordingly, like so:
- (void)setArticle:(Article *)newArticle {
_article = newArticle;
self.titleLabel.text = _article.title;
self.bodyLabel.text = _article.body;
}
So my question is, which one of these two options is best in terms of OO and iOS/MVC best practices?
I've certainly seen both being used. I've seen OPTION 2 used commonly in UITableViewCell subclasses.
I've also read that views and models should be designed to be reusable and not depend on anything whereas view controllers are meant to be the least reusable of the bunch.
My gut feeling is to use OPTION 1 simply because I'd rather the view controller do the dirty work of binding the model to the view and let the model and view stay independent and unaware of each other. But since some views are designed to do one thing only then perhaps it's not so bad to have them tied to a specific model directly.
I'd love to hear your opinions on this.
It's not always an either/or decision. Your first option is more typical of Apple's version of MVC; it's generally true in iOS that the model and view don't talk to each other directly, and the controller does most of the coordinating between them. However, it's not unreasonable to have a custom view that knows about specific classes within the larger model. You might well have an ArticleView that knows what to do with an Article, but the ArticleView should still receive any Article that it displays from the controller instead of getting it directly from the larger model.
One of the goals of MVC is to make model and view classes more reusable. If you keep your ArticleView very simple, so that the controller has to do all of the work of interpreting an Article, then you may actually be losing reusability -- every time you want to reuse ArticleView with a new view controller, you have to reproduce all the code that interprets the article for the ArticleView. Alternately, you always use the same view controller class to manage an ArticleView. Neither of these is really "reusable."
At the same time, you have to acknowledge that there's some natural coupling between Article and ArticleView simply by virtue of the fact that ArticleView is designed to display all the relevant details of an Article, whether it gets them directly from the Article or has them set by the view controller. If Article changes, there's a strong probability that ArticleView will have to change whether it knows about Article or not.
So, even though Article may be a model class, it can be okay to have a view that knows about it. What you don't want is an ArticleView that knows about the larger model and tries to fetch articles from the model itself. That limits the articles that you can display in ArticleView to just those that can be found where ArticleView looks -- it prevents you from displaying articles from other sources.
Yes, you have demonstrated understanding of the subject.
OPTION 1 is the classical structure of MVC, and I recommend it as your default route of the two presented.
Just because OPTION 1 is a purer definition, it doesn't mean that it need be applied everywhere possible.
The most frequent deviation I make is when an implementation is so specific and not reused that introducing a dedicated controller type just results in more code to write and maintain when the implementations are tiny, very specialized, not reusable, and will not grow substantially. If the program lends itself well to folding the V and the C into a single implementation and fits in something small (e.g. tens of lines of code), I don't worry about using OPTION 2.
Reality is more complicated than that, but the gist is: don't feel like you need to adhere to #1, although you probably won't understand how using #2 can introduce maintenance issues until you have written and maintained some moderate sized programs for a few years. Moving from one to the other can introduce many changes in a shipped program -- which could have been avoided.
Use of OPTION 2 should be the minority. If in doubt, use OPTION 1.
Either way, I believe the model should not know about the view or the controller.
To me, strict adherence is more important when writing reusable components.
Frankly, sometimes I do Option 2 myself. But Option 1 is 'out of the book'.
As others have said, Option 1 is the purer approach. The controller should be a "junction box" between the view and the model.
However a number of implementations of this type of approach (for example the framework that Microsoft call MVC) plump for Option 2. Certainly in Microsoft's case, the fact that the View and Model know about each other allows the IDE to create lots of boilerplate code, and save you the "trouble". The advantage of this is that you spend your time writing "function" code rather than "wiring" code. So, pure or not, you can appreciate where they're coming from.
As so often in software development, the choice between Option 1 and Option 2 boils down to purity versus pragmatism.
In terms of Apple's official guidance on this topic, the MVC as a Compound Design Pattern section of the Concepts in Objective-C Programming document discusses both approaches, but clearly articulates Apple's preference of your option 1 over your option 2. In fact the Cocoa Core Competencies only lists option 1.
I've never regretted implementing the option 1 approach, but when I've cut corners and tried to have models and views interact directly, I've often regretted it when I've had to go back and modify a system at later date.
Option 1 is MVC. Option 2 is not.
OO is at a different level really, you have objects for the Model, View and Controller so you can't do much more to be OO.
Both options will of course work, but MVC exists for a reason (I'm sure you've already done some general reading like this) and you'll find that your code is easier to read, understand and reuse if you follow the principles of MVC.
Related
As I was thinking about all the incarnations of MVC in Cocoa, I figured I could make a custom class for every View in the application and fill it with a datasource and delegate - stuff primarily considered for Controller.
This way, instead of having the infamous Massive-View-Controller, I could chop off pieces of code and put them in separate files - one class for one View - along with their datasource and delegates.
Is it a good idea, or what are the downsides?
I'm afraid that your idea sounds like you will end up with a bunch of bloated views instead of a bunch of bloated controllers.
What I'd suggest is to consider the Single Responsibility Principle: an entity should have exactly one purpose or function. What's a view's function?
It's a representation, in code, of a region of the screen. That means it needs to do two things: draw to its area and register interactions with the area. Anything that's not absolutely essential to those two subtasks shouldn't be in the view class.
This is the idea of the "dumb view". It has no logic, no decisions to make. It just gets handed some data to render. And when it gets a tap or a click, it doesn't know what the input represents, or try to figure out what to do about it. It just knows the type of the interaction and tells another object.
That other object is the view's controller. A view controller's responsibility is to mediate between the view and the rest of the system. It gives the view its data. It also accepts messages from the view about input, and then reconfigures the view based on the result of those messages.
The view controller doesn't necessarily need to compute the result on its own, however. That's usually where view controllers start getting into "massive" trouble. The view controller should pick another object to help it get the new values that the interaction produces.
One possibility for this other object is the view model, in the MVVM structure. The view model is a display-focused representation of the raw data for the view. It transforms the information in the model into whatever format the view needs, and re-transforms or updates the data in response to input from the view controller.
Another idea is to split that responsibility even more finely, using a VIPER arrangement. Here the formatting of the data is handled by a "Presenter", and the transformation of the data is the job of an "Interactor".
It's possible to get into architecture astronaut territory here; blindly applying a complex structure can bite you if a view's needs are inherently very simple. But even if you choose not to formally apply one of these alternative patterns, a view controller needs other objects. You will want "controllers" with other specific jobs, that get messages from the view controller and pass data back.
The important thing is to keep in mind the original idea I mentioned: strive to make each type do one thing and do it well. That will keep your classes focused; easy to read, understand, and think about; and testable.
View doesn't compute it's data, it just displays them. However if you have custom control it can have some logic to compute it's inner data to trigger value change inside model.
Your approach is an overkill with unnecessary code.
So you are facing a problem where you need to set some values of your custom NSView or custom NSControl (e.g. NSButton title).
Some of available MVC solutions:
set the value inside controller manually and call setNeedsDisplay method inside within view on the changed property.
set the model object as the property of view. However, this introduces tight coupling but still is ok (so the view has to
know/import model class). +Include update/refresh method within view
use bindings to nsobjectcontroller. You don't need to set Class of nsobjectcontroller (that is needed only if you need additional
functionality for it like creating object automatically on add
method).
MVC pattern reminder
View has target action mechanism which is triggered in controller. Controller updates model (nothing else!). Model then has to propagate that it has changed and controller should react to it. It shouldn't react in the target action.
With bindings you skip the target action but the latter remains
You can combine 2 and 3.
If you are beginner forget about VIPER pattern. MVVM can help you to reduce size of your controllers.
How to use binding with NSObjectController:
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "Value.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSObjectController *objectController;
#property (strong) Value *value;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.value = [[Value alloc] init];
self.objectController.content = [self value];
}
#end
#interface Value : NSObject
#property NSString *value1;
#property NSString *value2;
#property NSString *value3;
#end
#import "Value.h"
#implementation Value
- (instancetype)init
{
self = [super init];
if (self) {
[self setValue1:#"Value1"];
[self setValue2:#"Value2"];
[self setValue3:#"Value3"];
}
return self;
}
#end
There is also an option to unload View Controllers with Coordinator objects. Shortly: each task in the app is managed by a Coordinator object, which manages its View Controllers.
There is the main Coordinator object, that is retained by application delegate and that retains all other Controllers. All the logic that doesn't belong in View Controller, is moved up to the Coordinator.
I have a UIView I need to access the properties of from all around my app. I know you can't create a Singleton around a UIView object, so what might be a good way of doing similar?
E.g. The view has a label. From any view controller in my app I want to be able to change the text of this view (sitting in a parent view controller).
Thanks.
EDIT:
Success! Using KVO to track changes in my Singleton object worked a charm, and a very simple solution.
I think what you’re trying to do violates the separation of concerns of the MVC pattern: The only thing that should interact with a view is its controller. In your case, you should probably be creating a model that is watched by your view controller (maybe through key–value observing), and then the controller can propagate the necessary changes to your view.
If you know (read: you really know for now and forever!) that there will be at most one instance of that view alive at one point in time, you can just use a global variable to store that. Or use a class property on that view - which is as close as being a singleton as possible.
Or you might just fix your design, which has proven to be the better choice in every case I can remember. :) Just add some forward and backward references in your classes (and stick to MVC principle). It takes much less time to implement that worrying about those awkward workaround, and it will pay of rather sooner than later.
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 have read in multiple places that only one UIViewController should be on screen at a time. But I don't quite understand how to accomplish what I need otherwise.
Imagine the weather app. There is a single view controller with a scroll view, to which multiple views are added (the weather panels). Presumably these are all added and managed by the main UIViewController, which is also responsible for scrolling, etc.
But imagine each of those weather panels was instead a CarView, each with data about a specific type of car, and some controls to edit that data.
Wouldn't it make sense to have a series of CarViewControllers, each with a Car property that they can manipulate? Each CarViewController would be responsible for it's car data object, it's view, and glueing them together, and the main view controller would simply be responsible for adding each carViewController.view to its scrollview.
Isn't this better for re-usability and encapsulation? Not to mention more convenient.
I think it just comes down to whether it will make your life easier or not. One alternative I like to do is to just write composite UIView subclasses specifically for displaying a "conceptual" model - like a car - and then write categories on them to populate the view information using a specific model implementation. That way you can re-use the view when changing your model but still keep some of the logic from cluttering up the view controller. I try to reserve a view controller for something like radically different views toggled with a UISegmentedControl or something.
Edit: an example of a UIView and its populating category.
#interface CarView : UIView
#property (strong) UILabel *modelLabel;
#property (strong) UILabel *makeLabel;
#property (strong) UILabel *yearLabel;
//etc
#end
Then you have a model-specific category, that while a category on the view really fits more into the controller layer; while a little bit of a breach of MVC I think it works nicely from a responsibility assignment standpoint, doesn't couple your main view implementation to any data implementation, and keeps your view controllers leaner, so I think it's worth the tradeoff.
#interface CarView (CarEntityPopulating)
- (void)populateFieldsWithEntity:(NSManagedObject *)entity;
#end
#implementation CarView (CarEntityPopulating)
- (void)populateFieldsWithEntity:(NSManagedObject *)entity
{
self.modelLabel.text = [entity valueForKey:#"name"];
self.makeLabel.text = [[entity valueForKey:#"make"] valueForKey:#"name"];
self.yearLabel.text = [[entity valueForKey:#"year"] stringValue];
//etc....
}
It seems like this now explicitly supported in iOS 5 using a container view controller.
There are not a lot of resources yet that discuss it, but this WWDC session is useful.
This question also provides some resources.
I tend to agree with Joe Conway’s and Aaron Hillegass’s analysis, as reported today by Ole Begemann in http://oleb.net/blog/2012/01/initWithNibName-bundle-breaks-encapsulation/
Basically, they state that the NIB's filename is an implementation detail of the corresponding UIViewController class, and that it is not the business of the calling class to pass in the NIB's filename in the init method.
I was wondering if there was any particular reason for this design choice from the creators of AppKit/UIKit, or if it is merely a mistake — and, in the latter case, why it wasn't corrected when UIKit came out, which would have been a good opportunity.
If any Objective-C old-timer could provide the historical background to this, that would be nice to get a better understanding of the framework we use every day.
I suspect it was done this way so that a UIViewController could have basic functionality as a controller without requiring any subclassing. For example, if you're just pushing a "Credits" view on a navigation controller, and the view has nothing but static text, you could get away with not creating a UIViewController subclass. You could simply create a UIViewController directly and pass it the nib that contains your static text.
Most of the time, of course, you're going to want some level of interaction with the presented content, in which case a custom controller is necessary. But in theory, it's not always required.
It has come to my mind today that maybe Storyboards are precisely a response to that issue. Because Storyboards are defined at application-level, there is no more violation of encapsulation, merely a change of level. UIViewController subclasses become the detail implementation of the Storyboard as a whole.
It still doesn't explain the historical reasons behind the original design, but at least they have done something to address the issue — and, as often with Apple, in a very elegant way.