I'm using iOS 5 storyboard for the view control hierarchy. The problem with this is that I have a few shared controller classes that handle the business logic that I would need to inject to the view controllers. These controllers are initialized and kept in the app delegate.
For instance, I have a controller encapsulating Dropbox interaction that I'm using in some of my view controllers. I could pass the reference on with each prepareForSegue:sender: but this makes the whole view controller hierarchy dependent of the controllers. Also, I could use the singleton pattern but I'm not really a big fan of it.
What are my options, if I want the controllers loosely coupled? I've heard of Objection, and I'll be looking into that, but any input would be very welcome
I know this is a bit late but hopefully it can help others.
I solved this problem using protocols and checking the object to be injected responds to certain selectors.
In the prepareForSegue method I do one of two things:
id object = segue.destinationController;
if([object conformsToProtocol:#protocol(HasApplicationManager) ]){
[(id<HasApplicationManager>)object setApplicationManager:_applicationManager];
}
if([object respondsToSelector:#selector(setViewDelegate:)]){
[object performSelector:#selector(setViewDelegate:)withObject:self];
}
The first bit checks if the destination controller conforms to a specified protocol, this is still loosely coupled because you build your application with the composition pattern.
And secondly i check for a selector, this is more informal than protocols and probably more coupled and presents more problems. But it is a way of getting around having hundreds of 'IHave...' protocols.
Related
I am currently making up a directory structure, which is supposed to enable me to work clearer with the MVC model in my iOS-projects. I therefore created a folder for my Models, Views, Controllers and integrated APIs, but wonder where to put custom segues. I know that they do not belong to the API directory or to the model, but I would neither add them to the Controllers as segues do not coordinate the workflow and not to the Views as they are not visible or contained in a UIViewController. Are there any suggestions where to put the UIStoryboardSegue files? Thanks.
Since Segues are meant to perform transition between controller I would put it with the Controllers.
But note that iOS framework combines view and controller. This approach is called MVVM, hence the name ViewController. This means it will be tricky to treat it as pure MVC.
For instance what would be in the view?
Should it be storyboards, or custom view classes?
I am trying to develop a simple app to understand MVVM but I don't get it really well. I've read some articles, but they are a bit different:
View in different class MVVM
In this example the view is not the ViewController itself, it is a class that contains all the details of the view and it is an abstraction for the VC.
The ViewController creates the ViewModel with some data the it retrieve from DB, Network..
The ViewController talks with the model and manage the ViewModel to update the view.
ViewController is the view MVVM
In this example, the viewController is the view, it does not need other class. The viewModel is not created by the viewController, and the ViewModel of the next ViewController is created by a ViewModel. The ViewModel also has an instances of the Model, so if it needs to retrieve some data, it has instances of the DB, Network..
I don't understand well some things. If the viewModel is retrieving data from Network with asynchronous tasks and I want to display an spinner and get data for fulfil the view. Should I user blocks, KVO or delegates? If I use delegates, the view implements ViewModelDelegate Methods, would be that correct? Am I coupling view with viewModel? I am confused at this point.
If I have a firstViewModel that creates a new secondViewModel for a new pushed SecondViewController,and FirstViewController has to update its view before the user pops the pushed SecondViewController. In this case firstViewModel has to observe secondViewModel and notify the changes to firstViewController once popped, doesn't it?
I also have a question about ViewController creation. ViewControllers are the attendants of next ViewControllers creation? If I create an object that manages ViewControllers creation and navigation should I pass this object over viewModels?
I know they are very similar, but I am confused about which way I should implement and how. There isn't much information and examples about iOS MVVM.
I am not using Reactive Cocoa yet because I want to understand this pattern well first. Though it is in my TO-DO list.
Thank you!
It really depends on why you're using MVVM. I'm doing it right in the middle of the two cases you mention, actually. Here's the two reasons for MVVM usage:
First, you may be using MVVM because you want to organize your code in a way that pulls the logic out of the VC and keeps it clean. In this case, you're using a ViewModel in good OOP form and it's perfectly find to have the VC create it.
On the other hand, you may be intending on UI testing your VC's and therefore want to introduce mock ViewModels. In this case, you want to be handing the ViewModel to the VC. (i.e. dependency injection)
In my view, either is justifiable. You might even have multiple needs in one app. Therefore, I keep things flexible. My approach is to create the VM in the viewDidLoad() method of the VC only if it doesn't exist yet. This allows me to use my normal VM and to do create it (neatly) in my VC, but if I ever need to override it (unit testing, UI testing, alternate view style), I can, too.
The issue comes from the fact Cocoa apps, are by definition using MVC, and you have to try and make MVVM work with the underlying ViewController structure.
I haven't looked at MVVM in iOS, but have done a bit using Xamarin.Forms, where the View is just the XAML code, the view model interacts with the View and the Model. Under Cocoa apps, it doesn't really matter too much how you do the ViewModel part, but looking at your links, I think the second approach is closer to a purer MVVM approach.
I suggest you download the code in the link and have a look to get a greater understanding?
I was left with continuing the code of a senior developer, where I came across a coding pattern that was not only bizarre, but got me curious about a lot of things. The pattern, however, that I spoke about had something like this:
There is a UIViewController the view of which has an instance of extended UIView attached to it as a subview.
This custom UIView class has a reference of the above-stated UIViewController.
There are a series of methods defined within the UIViewController that are responsible for handling events at the UIView.
Since this UIViewController exists as a reference, our custom view calls those event-handling methods through this reference!
In such a system of code, what are the memory implications? How is this any different from the delegate pattern? Under what circumstances using this sort of coding okay?
While this pattern is a little curious, I would hesitate to condemn this without more information about what this child view is doing and what it needs to inform the view controller about. There is, admittedly, a faint code smell here, and if I were to hazard a guess, I'd bet that this view is likely doing stuff that one would now generally put in a view controller.
Often, when adding a subview that has any significant complexity (or is likely to be reused in different views), one would consider an iOS 5 feature, "view controller containment" (see the Creating Custom Container View Controllers section of the View Controller Programming Guide or WWDC 2011 video Implementing UIViewController Containment).
If using storyboards, you can achieve much of this using the special "Container View" control introduced with iOS 6, found in Interface Builder's "Object Library" (in the bottom of the right panel in the standard Xcode layout). If doing this programmatically, just make sure to call the appropriate methods listed in the "Managing Child View Controllers in a Custom Container" section of the UIViewController Class Reference.
When using view controller containment, you have a parent view controller (the main view controller) and the child view controller (the view controller that is managing the subview). And in this scenario, it's very common to design a custom protocol by which a child view controller notifies its parent view controller of particular events. But rather than adding your own custom delegate property, you can use the built-in parentViewController property which is automatically populated when you adopt the above "view controller containment" pattern.
Having said all of this, I might let practical concerns drive whether the existing code base needs to be refactored or not. Perhaps the code predates iOS 5, but is a solid implementation of what we might have done back in the day. Bottom line, if it works, is otherwise well written, and has the delineation of responsibilities clearly defined, you might decide to just leave well enough alone. And if it's a little ambiguous (as the absence of a discussion of a protocol might suggest), perhaps just start by introducing a formal protocol between the child view and the view controller to make the interface explicit. Whether a more radical refactoring of the code (to use something like view controller containment) is called for is hard for us to advise on the basis of the limited information provided thus far.
The way I am designing my code, with several view controllers, that interact, I have the need to pass data between the view controllers. The way that I am doing it is to "pass a reference", make a pointer, to the target view controller in as a method argument like below:
-(void)aMethodToSetUpInterfaceElements:(UIViewController*)targetCV
Is there anything wrong with this, or do I need to watch out? It works well on a functional level, but what about the design?
There's nothing really wrong about this.
If it works for you, than it's ok. Just make sure to have a weak reference to the view controller, otherwise you could have a memory leak with retain cycle. Read this for more information: http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html
Speaking about design, it's really depends of each case. I could't say preciselly because you didn't give more informations about the funcionality that you are trying to achieve.
Maybe if you want to make your code more generic, to be used in other places or projects, you might consider to perform a delegate to your view controller, but this depends of your views hierarchy and its design. To see more information about passing data between viewControllers, please see this: Passing Data between View Controllers
I don't think it is a good idea to pass a view controller in order to transfer its data if you don't have enough reasons. I tend to exposing the minimal knowledge to another object.
For example, say you have two view controllers controllerA and controllerB.
If all you need is just to pass some data from controllerA to controllerB, you pass the data, which might be a NSData, a NSArray, a NSDictionary or your custom data structure. You should not pass the whole controllerA as an parameter to controllerB. Although you can access the data by using the controllerA.data getter, you have exposed too much knowledge about controllerA to controllerB, which may unconsciously ruin your design by increasing the coupling between controllerA and controllerB.
If you are aiming at handling the view transitions, that may be a good reason to pass a view controller as the parameter. So that you can make good use of its -presentViewController:animated:completion: to present a modal view, or push it into a navigation controller, or you probably want to get a reference to its view property and customize your own UI presentation.
I have a parent class DocViewController (inheriting from UIViewController) and 2 subclasses:
PhotoViewController
MapViewController
In the parent class (DocViewController) I have the following code:
if ([previousView isKindOfClass:[PhotoViewController class]] || [previousView isKindOfClass:[MapViewController class]]) {
[viewControllers removeObjectAtIndex:[viewControllers count] - 2];
self.navigationController.viewControllers = viewControllers;
[viewControllers release];
}
That I'm using to delete the children classes from UINavigationView stack. (It is not about the question anyway: I have a segmented control and I'm pushing such classes, but I still want my "Back" button to ignore them).
It works. My only problem is that is not very object-oriented since the parent class import the children for the if statement. Right ?
thanks
Yes, it's not very object oriented. Would be better to implement a method on the classes. Maybe something like
-(BOOL)shouldPopTwo;
and then
if ([previousView shouldPopTwo]) { ... }
And then each subclass can implement as it needs to.
BUT
In most cases I think you should avoid the temptation to build class hierarchies with UIViewControllers. Controllers are typically the least reusable objects in an MVC setup as they are designed to very specifically define behavior and map it to specific Model objects. Certainly hierarchies can be built (As they have been in the SDK), but the structure, function and abstraction needs to be very carefully planned and constructed.
Instead, you should probably create reusable UIViews and then implement different UIViewControllers to define how the application should use and respond to those views.
In response to comments below:
Sounds like you might be trying to use a navigation controller in a way it wasn't intended for and actually your last two view controllers should be a single view controller. A view controller is primarily intended to manage a full screen's worth of content. If I understand correctly, in your case you have a segmented control that should stay on screen and be responsible for switching the content in the rest of the available space. I would suggest you have a single view controller and it's view would contain the segmented control and a 'canvas' which would be used to display alternate views. The view controller would hold references to the views it is managing so that it could switch them in and out (if you want UINavigationController-style animations you'll have to implement that yourself).
Finally, you would need to decide: Can this single view controller act as the controller for the two subviews? Or should each view have its own controller? If this UIViewController subclass is a one-off, and it is only intended for these two views (say PhotoMapViewController) you could choose the first, easier option. If you want this to work with any UIView (say SegmentedControlViewController), then each view should have its own NSObject controller (not UIViewController). From the apple docs:
Note: If you want to divide a view
hierarchy into multiple subareas and
manage each one separately, use
generic controller objects (custom
objects descending from NSObject)
instead of view controller objects to
manage each subarea. Then use a single
view controller object to manage the
generic controller objects.
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html
But that would require substantially more abstraction and setup.
If the overall goal is to (I'm guessing) map a collection of photos, in the first case in the calling view controller you might have code like:
NSArray *photoCollection = model.myPhotos;
PhotoMapViewController *pmViewController = [[[PhotoMapViewController alloc] initWithPhotoCollection:photoCollection] autorelease];
[self.navigationController pushViewController:pmViewController];
and PhotoMapViewController : UIViewController would be responsible for creating and initializing the two views its segmented control manages.
I won't go into the code of the second case because it's much more elaborate.
Finally, look to the OOP principle of favoring composition over inheritance to see a broader perspective of why you might do things this way instead of your first implementation.
Any time you start to set up a class hierarchy you should ask yourself - Is it really a subclass? and then stop and ask yourself - No wait, is it REALLY a subclass?? :) Good luck!