Friday, 28 June 2013

iOS App Architecture and TDD #1: The UIViewController Spaghetti Monster

We made it, Trinity.
You said we would.
It's unbelievable Trin...
We finally made another blog post.

Agenda

I want to talk about iOS app architecture and Test Driven Development.

For the past couple of years I’ve been developing the feeling that there’s something seriously wrong with how a lot of people are approaching iOS development. Apple has made certain decisions with their framework design and tools which I believe contributes to this problem.

Specifically I’m talking about where the application and business logic for an app belongs, and how the overall architecture of an app should be constructed. I feel I need to slap some people across the face and bring them to their senses (including myself).

This is going to be the first in a series of posts. Today we’re going to find out why Apple has created a total abomination of design with UIViewController. Should be fun.

In future posts I will describe an Uncle Bob style “Clean Architecture” for iOS apps, driven by TDD. I haven’t seen anything like this from other writers/developers, so be sure to subscribe (or follow me on twitter) if that sounds interesting.

The Humble View Controller?

Way back in 2002, Michael Feathers (of “Working Effectively with Legacy Code” fame) wrote a piece titled The Humble Dialog Box. In it, he describes a particular approach to separating GUI-specific code from the actual application logic behind a simple dialog box.

We’ll revisit this piece sometime in the future, but for now I want to focus on the introductory problems he describes. Remember he is talking about building desktop applications here:

Let’s take a look at our friend the dialog box. […]

Tool vendors have made it easy to create dialog boxes. Nearly every IDE on the market has a GUI builder. You can drag and drop all sorts of components on dialogs and drill down to wire them up. […]

It is easy to just override an event from a component and drop your interaction logic right there in the dialog box class.

He goes on to describe the problems that typically arise with these dialog box classes:

  • It’s difficult to get these UI classes under a test harness because they are so tied to UI-specific frameworks. There is no separation between the UI and interaction/application logic.
  • The UI classes have a mixture of low level component API code as well as actual decision making code, making it hard to see what the code is doing or to share code between objects with similar behaviour.

These statements perfectly describe a typical iOS view controller. It’s easy to drop code into an IBAction method or a delegate call on the view controller, and do everything right there and then. This can lead to a mixture of “component API” (i.e. UIKit) and important decision making code.

Apple has made UIViewController the center of the iOS universe. When you add a new screen to your app, the only thing that Xcode gives you is a new UIViewController subclass. And Apple makes it so easy to do everything from this view controller. If you aren’t really careful, you can end up with an application consisting entirely of view controller classes and little else.

"iOS architecture, where MVC stands for Massive View Controller"

Now of course there are good developers out there who are creating nicely decoupled classes, but if we list out some of the responsibilities that a view controller typically ends up dealing with, we’ll see just how pervasive this problem is, even in code written by experienced developers.

Spotted in the wild

View setup and layout code

By default, when you hook up a new control to your view controller’s XIB or storyboard (for example a UIButton or UILabel), the IBOutlet connection is going to go to a property on your view controller. The whole assistant-editor workflow is designed to do this. All of the sample code, tutorials and books you will ever read will do this as well.

If you need to tweak that control’s settings in code, your first stop will probably be the trusty -viewDidLoad method:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.myButton.titleLabel.font = myCustomFont;
    UIImage *stretchableImage = /* ... blurgh ... */
    [self.myButton setBackgroundImage:stretchableImage
                      forControlState:UIControlStateNormal];
    /* ... more 'blurgh' ... */
}

Over time, your -viewDidLoad will likely grow. You may even create new controls in here that don’t exist in your XIB file. Even if you break things out into multiple methods, a significant part of your view controller’s code can end up dealing with view setup and creation. The situation is worse if you forgo XIB files entirely.

And what if you have custom layout requirements which can’t be solved in IB?

Well… you could create a UIView subclass and implement -layoutSubviews. But you already have all your IBOutlets going to the view controller. You would have to move all the IBOutlet connections and properties to this new view class — and why bother with all that when it’s so easy to add some code to -viewWillAppear::

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.myLabel.frame = /* ... */
    self.myButton.frame = /* ... */
}

Even though using a UIView subclass and overriding -layoutSubviews is a far superior way to layout your controls (and is in fact the correct way), people end up putting this stuff in their view controllers because that’s the easiest thing to do.

And then they have to deal with view rotation issues, and not being told when the view’s frame changes. Even worse, because this stuff is so hard to do properly from the view controller, people often end up hard coding interface sizes and frames rather than implementing truly dynamic view layouts.

I’ve seen people doing layout in the -viewDidLoad method, before the view is even part of the view hierarchy. And they wonder why things mess up. You can’t even blame them. After all, it is Apple who is encouraging people to connect their IBOutlets to the view controller.

It all becomes a hideous mess, and could easily be solved with UIView subclasses. But Apple doesn’t teach us to do this. I doubt many novice or intermediate iOS developers have even heard of -layoutSubviews or -sizeThatFits:. All Apple gives us by default is a view controller subclass.

Even with Autolayout the problem doesn’t go away. The view controller still ends up being the place to create custom constraints, for the same reasons as above. That’s just how people do it. Even Apple engineers do it this way in their WWDC videos.

Apple engineers teaching us bad habits.

So the Xcode/Apple-approved default is to have all of your view setup and layout code in the view controller. Why is this a bad thing? Quite simply, because view layout and setup code should (in my opinion) always go in UIView subclasses. That’s where it belongs, and life is a lot easier when you do it this way. Putting this stuff in the view controller is just contributing to an already bloated and confused class.

Core Data and NSFetchedResultsController code

All of the Core Data sample code that I’ve seen from Apple creates and sets up the NSFetchedResultsController in a -fetchedResultsController method on the view controller. It’s usually a huge long method which sets up the fetch request, configures the fetchBatchSize, predicate, sort descriptors etc.

Xcode screenshot showing Apple's CoreDataBooks sample project creating and setting up the fetched results controller in the view controller.

Unfortunately I’ve seen a lot of people who have copy/pasted this kind of code into their view controllers. As time goes on, the view controller ends up with a dozen methods just dealing with Core Data - fetching results, updating records, performing background operations, merging contexts, etc.

None of this stuff belongs in the view controller. You have business logic like sorting and filtering rules mixed in with the details of the persistence framework (Core Data), and all of this inside the view controller. A “view controller” should not be dealing with this stuff, and Apple unfortunately does not show new-comers how to do this properly.

Be the delegate for everything

Even if you pull your Core Data code out into a nice class or interface, your view controller will probably end up being the delegate and datasource for the table view displaying that data. It’ll contain the code to create and configure table view cells for a given item, define section titles and row heights, handle insertion and deletion operations, and respond to any of the dozen or more delegate/datasource callbacks that are available.

If your app is making use of CLLocationManager, your view controller will probably end up responding to and handling location events. It might even be the one that creates the location manager in the first place. Another of Apple’s tutorials does just this.

If your view controller makes use of MPMoviePlayerController or QLPreviewController or MFMailComposeViewController you’ll probably be the delegate for those as well. You can also throw in Twitter and Facebook API callbacks (and sharing in general), quick one-off AFNetworking or NSURLConnection web service requests, NSTimers doing periodic background work, SCNetworkReachability checks, and the list goes on.

Because UIViewControllers are such a central part of an iOS app, they end up being the delegate for everything. They end up accumulating all this crap. If the view controller was purely forwarding information and acting as an intermediary, this might not be so bad. But we all know that’s rarely the case.

You really have to be on the ball to keep this stuff nicely separated, and I’ve seen a lot of codebases which fail to do this.

Accessing global state

Because it’s so easy to drop code into a view controller, and because Xcode doesn’t give you any other place to start adding your code, inexperienced developers have trouble accessing the information they need.

They end up using hideous hacks like ((IHaveNoIdeaWhatImDoing*)[[UIApplication sharedApplicaton] delegate]).objectThatINeed, or singletons, or notifications, to access everything they need. Stackoverflow and internet forums get inundated with questions of the form “How to I pass information from one view controller to another”?

The reason this is so difficult is that they haven’t been taught to write higher-level objects which deal with the interactions and flow of information in their app.

So not only do many view controllers have dozens of delegate calls coming in to them, they also have hideous clawed tentacles reaching out all over the application, accessing global state and further coupling the view controller to other parts of the application.

Some people have tried to contain the UIViewController...
This usually doesn't end well.

You can argue that this is just a problem of inexperienced developers, but Apple really does not help people to develop good coding practices. I’ve seen Apple sample code using [[UIApplication sharedApplicaton] delegate] to access global state, and it’s really a terrible practice to encourage. In fact I already have a blog post half-written which deals precisely with this topic.

Navigation logic and other “decision making” code

When the user selects a cell from your table view, you are going to handle the tableView:didSelectRowAtIndexPath: method.

Your app now has to decide what to do. In a typical drill-down style of navigation, you want to push some sort of “details” screen for the selected item.

Apple makes this really easy with convenience methods on UIViewController like -navigationController, -tabBarController, -splitViewController, etc. Your view controller can directly grab a reference to the container that holds it.

You can then do something like this:

- (void)tableView:(UITableView *)tableView
    didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    id item = [self itemAtIndexPath:indexPath];
    DetailsViewController *detailsVC
        = [[DetailsViewController alloc] initWithItem:item];
    [self.navigationController pushViewController:detailsVC
                                         animated:YES];
}

Perfect! The navigation logic can be dropped right there in the view controller!

But what if you later make your app universal, and the details screen needs to appear in a split view controller on iPad devices?

Well, no problem. We’ll add a little runtime check:

DetailsViewController *detailsVC = /* ... */;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    // display details in the split view controller's details pane
} else {
    // push details to the current navigation controller
}

We also need to keep the cell selected on the iPad, but deselect it in viewWillAppear: on iPhone devices (this is what UITableViewController does by default). This adds a few more if statements to the controller.

But what if you now want to present this view controller modally at some point in your application so the user can select one item for a task? In that case you need to dismiss the view controller and somehow notify the parent about which item was selected.

We can’t keep adding more and more if statements to our view controller. It’s pretty clear that this kind of navigation logic doesn’t belong in the view controller.

A view controller should not know about the high level work flow of your app. It shouldn’t know about its surroundings. It shouldn’t know that it’s being presented modally, or that it’s contained by a navigation controller, or a split view controller, or a tab bar controller. It shouldn’t even have references to these things, even though Apple decided to “help” us out by tacking on all these category methods. It shouldn’t even know about the DetailsViewController. That’s an entirely different screen of our app.

If our view controller is displaying a list of items (think a typical table view controller), that’s all it should be concerned with. It should provide some delegate callbacks for when a given cell or item is selected, and perhaps a few ways to customize the display or behaviour of the cells, for example to enable a check-multiple-items style of selection.

The same view controller being presented in different contexts, with different navigation items.

The view controller should only be concerned with the behaviour of the view that it presents. By putting navigation logic in the view controller, you are coupling it to the specifics of your app’s UI and navigation. If you want to change the workflow later (e.g. for other devices), or reuse that view controller in a different context (or even a different app), you are going to encounter a lot of pain.

So where should this navigation logic go? Well… somewhere else. We’ll get into that in a later post. All that matters for now is that the view controller should delegate UI actions like this to somebody at a higher level, who has access to and knowledge of the specifics of your app’s UI workflow.

This is also why I have a big problem with Storyboards. When you implement a Storyboard app, you are forced to put application-specific navigation logic in the view controllers by implementing the various UIViewController segue-related methods. There’s no other way to do it. And delegating this stuff out to somewhere else really confuses the matter because you have this extra level of indirection between the storyboard XIB and the segue code. Reusing view controllers in other apps or screens suddenly becomes impossible to do (or at least very difficult) without creating a total mess of things.

Official UIKit responsibilities

So far we’ve mostly looked at logic and code that could theoretically be pulled out of the view controller and put somewhere else. You’ll probably be doing this already to a greater or lesser extent.

But what about the stuff that a UIViewController must deal with? What about it’s official UIKit responsibilities?

In fact, what the hell is a “view controller” anyway? Does anybody actually know? Why do we now have view controller containment hierarchies as well as regular view hierarchies? How does it differ from a UIView in this case? What does this all have to do with MVC?

What is a view controller?

To figure this out, we are going to start pulling code out of some hypothetical view controller and see what’s left at the end.

First we’ll move all that horrible view setup and layout code into a UIView subclass. Done.

Then we’ll move all the core data and model code into some nice protocols/classes, which are injected into the view controller through public properties or an -init method. Done.

We’ll move the table view delegate/datasource code into dedicated classes. Done.

Any miscellaneous framework code, url requests, etc. will also be pulled out behind nice interfaces, and our view controller will simply funnel data back and forth between the view and these interfaces. Done.

Finally, let’s delegate navigation events like certain button presses and cell taps to somebody else, so the view controller’s behaviour can be customized easily for use in different contexts. Done.

What are we left with at the end of all this?

We’re left with a class that:

  • Lazily loads a view (by calling -loadView when the -view property is accessed for the first time)
  • Can load its view from a NIB file in a convenient manner.
  • Provides access to various events like -viewDidAppear:, -didRotateFromInterfaceOrientation:, and -didReceiveMemoryWarning.
  • Deals with state restoration to restore state between launches of your application.
  • Handles storyboard segues to pass data to the view controller that is being transitioned to.
  • Can provide accessibility information about the content it displays.
  • Allows you to define which interface orientations are allowed when its view is at the top of the view hierarchy.
  • Can be inserted into UIKit’s standard containers like UINavigationController and UITabBarController (also UIWindow), with a bunch of properties like navigationItem and tabBarItem to customise its appearance in these.
  • Can be nested inside another view controller in a UIKit-safe way.
  • Happens to be part of the responder chain, so can respond to and handle certain types of input events.

Wow. That’s a lot of stuff, and I’ve probably forgotten some things. It also sounds a hell of a lot like a view to me. The lines are starting to blur. It certainly doesn’t sound like a place for important business or application logic.

If you look at the actual interface for UIViewController — the methods and properties it declares, it’s almost all view/presentation related. Go on, pop open Dash and take a look right now. I can only count 5 methods out of 86 that are not presentation related. These are -didReceiveMemoryWarning and the 4 state restoration methods that came with iOS 6. Everything else is related to the view. And that’s not even including all the category methods that get added on by other UIKit headers.

An identity crisis

Who *are* you, UIViewController?

What if we continue to pull stuff out of UIViewController?

-viewDidAppear: and all the other view-related events would be much happier sitting on the UIView class instead, for instance.

Lazy loading also seems to be a detail that belongs elsewhere. Don’t we just want something like this?

UILazyLoadedView *lazy = [[UILazyLoadedView alloc]
    initWithLoadViewBlock:^{
        MyView *v = [[MyView alloc] init];
        // restore view state
        return v;
}];

Then we just change the UINavigationController and other container classes to accept a UILazyLoadedView where applicable. When our view needs to be displayed or loaded, UIKit can invoke the block and get a view returned to it. Any view state can be captured/restored in the block, making it work well when the view is unloaded/reloaded (prior to iOS 6 anyway).

Let’s look at the navigationItem/tabBarItem/etc. properties now.

These properties are used to customise the way the view controller appears in navigation controllers, tab bar controllers, etc. These customizations are always going to vary depending on the context in which the view controller is presented.

So do these properties really belong on the view controller? They’re actually part of the interfaces for the various container controllers that Apple gives us. But they’ve been “helpfully” tacked on to UIViewController via categories. Is this really a good idea? Didn’t we already establish that a view controller should only know about the content it displays and manages?

If a view controller represents a screen of your app, why does it need to know the title of its back button when another screen is being displayed? Why does it need to have access to the icon image in its tab bar button? Shouldn’t that be managed by the class that set up the tab bar controller, or that pushed it onto the navigation controller?

When we consider the multitude of responsibilities a view controller already has, does it really need to deal with the specifics of all these container controllers as well?

Couldn’t we remove these category methods entirely and provide the information when we do the actual push? Something like this:

UILazyLoadedView *lazyView = /*...*/;
UINavigationControllerItem *item = [[UINavigationControllerItem alloc]
          initWithLazyView:lazyView
                     title:NSLocalizedString(@"foobar", nil)
                     backButtonTitle:/*...*/
                     /* etc */];
                                 
[navController pushItem:item];

I don’t know. We’re getting dangerously close to the realm of pure fantasy here. But we need to think about these things to figure out exactly what a view controller is, and what it’s main roles should be. Everything we’ve seen so far seems to belong somewhere else.

The other thing view controllers can do is “view controller containment”. This can only be described as a complete (and unnecessary) mess.

Because a view controller is just a wrapper around a view, people ended up needing to nest or compose view controller views in a single screen. But things like rotation events, -viewDidAppear: etc. get all screwed up if you do this by manipulating the view hierarchy directly. This wouldn’t even be a problem if these methods were part of UIView instead.

So to make this work nicely, Apple has given us the view controller containment APIs, with weird-ass requirements in the order you call the various willMoveToViewController/didMoveToViewController/addChildViewController/etc. methods.

And now that we have this view controller containment, a view controller no longer represents an entire screen of content. It represents a portion of the screen and can be nested arbitrarily with other view controllers. So… doesn’t that just make it a view? A view with some additional UIKit details hacked on?

Somehow this all feels like a big mess to me. I’ve only covered a few items from UIViewController’s interface, but it’s already starting to look like this weird “miscellaneous crap that UIKit needs” class with a lazy loaded view inside it. It’s a confused mess of responsibilities.

It also happens to contain the majority of our application code. This is a problem.

A black hole of bad design.

Let’s summarise the key points so far.

UIViewController is the centre of the iOS world. Apple’s tools like Xcode and Interface Builder, and the workflow they encourage, put the view controller at the centre of everything. The default place to start when you’re adding new code or features is the view controller. When you add a new screen to your app, all you’re given is a UIViewController subclass and a lot of helpful methods to shoot yourself in the foot.

Because of this, code and logic tend to gravitate towards the view controller. The view controller ends up being the delegate for a large number of different objects. It ends up doing view setup and layout, it ends up dealing with core data, table view datasource/delegate methods, and a huge number of other responsibilities.

Apple has tacked on a large number of “convenience” methods which let you directly access the enclosing containers like navigation controllers or tab bar controllers. This makes it very easy to couple the view controller to the specifics of your app’s navigation, and to the other view controllers it interacts with. Storyboards also contribute to this problem. This makes it difficult to reuse the view controller in other contexts or applications.

Even if you manage to keep your own interfaces/APIs nicely decoupled and separated into other classes, UIViewController is the only place to deal with certain UIKit details, like: lazy-loading of views, configuring the display of tab bar items and navigation controller elements, view events like viewDidAppear, rotation events, and view controller containment when you need to nest multiple view controllers inside one another.

If you have any kind of decision making logic in your view controller, it is completely coupled to the UIKit framework. As we’ll see in my next post, trying to get these things into a test harness is a disaster waiting to happen.

UIViewController is just a detail of UIKit’s implementation. It’s more of a view than a controller, and certainly isn’t anywhere close to being a Controller in the classical MVC sense, despite what Apple tries to tell us:

WWDC Slide: View controllers are just controllers - the "C" in MVC.

“Controllers” are supposed to be nice thin objects funnelling data and events between the model and the view. They shouldn’t contain details of a specific UI framework. iOS’ View controllers are nothing like this. They sit firmly in the presentation layer.

So why are we putting so much of our important application and workflow logic into our view controllers? I feel like the iOS developer community has forgotten everything we have learnt about good OOP design in the last 20 years or so. Remember the Single Responsibility Principle? One Reason To Change? Mixing application logic in amongst the spaghetti of UIKit APIs is just plain wrong.

Isn’t there a better way? Isn’t there an escape from this madness?

I’m happy to tell you that there is a better way.

I’m going to take you on a journey, Dear Reader. A journey of self-discovery and enlightenment. Or… something like that. We’ll figure it out as we go.

We’re going to develop a Clean iOS Architecture together, driven by TDD. We’re going to find out why so many people are doing iOS TDD wrong, and how to do it right. And we’re going to see some really nice benefits from this new approach.

Won’t you join me?


PS: I’m currently looking for new job opportunities, preferably in the UK. London would be ideal. Please contact me using the email address at the top of this page if you have any offers.

70 comments:

  1. Interesting and enlightening article :) I must confess that I am one of the people who overburdens the view controller like you pointed out. Most of the tutorials that I looked at when I began iOS development used that method and so I went with it without pausing to think about it. But I see the reasonableness of your arguments and am looking forward to the rest of the articles in the series so that I can see how you go about this from beginning to end.

    If it's a workable solution, I'd definitely want to switch to what you propose since that sounds more logical :)

    ReplyDelete
  2. Food for thought. I agree on some points (Core Data absolutely does not belong in a VC class. Storing globals in the App Delegate - punishable by death) but Apple is clearly pushing a design that you don't like. Whether or not you agree with it, it's the way it is. If you build your own road, nobody but you will be able to work on your iOS projects because it will be incomprehensible to anyone schooled in The Apple Way. To paraphrase Douglas Adams, if it's wrong, at least it's definitively wrong.

    For what it's worth, I find the concept of view controller code reuse to be completely overrated. How often have you been able to reuse code without any modifications? How often have you wanted to?

    ReplyDelete
  3. @jsd The kind of thing I've been doing doesn't actually depart from "The Apple Way" that much. There are still views and view controllers, we just move some logic into real controller objects (NSObject subclasses) which can be unit tested without pulling in entire UI frameworks and requiring an iOS simulator to run. As for view controller reuse, I think it depends on the kind of work one does. Doing Contract/Agency work, we have a huge number of projects and there are always things we can reuse between projects. This may not be relevant to others.

    ReplyDelete
  4. Thanks a lot for this blog post. Really made me think about myself and how I develop applications. I also found out the those pesky ViewControllers really slow you down because you aren't flexible at all regarding your navigation and ViewController hierarchy. Also unit testing is really hard to do up to impossible due to wrong/non existing encapsulation. It really becomes a huge moloch. I can't wait for the next article in the series :)

    ReplyDelete
  5. I agree with most of your points, but I don't see a sane way out. I wrote an iPhone application for some friends and am very happy with it. I have been going crazy trying to convert it to an universal application. The interface that seems most natural to the end-user has a search bar, dynamic table and webview on the same screen (not to mention a custom button bar at the top). How do you let each of these components update the others? The architecture that Apple presents, ends up with a mess of cross messaging. Everything got hopelessly coupled. I'm very much looking forward to see how you clean up the ViewControllers.

    ReplyDelete
    Replies
    1. Bill, you should be able to do it without cross messaging if you implement proper MVC separation. In this case, the model is application state rather than traditional user data. The way I do it is with an ApplicationState singleton (you can make this a normal object that is pushed into all the view controllers if you are opposed to singletons on philosophical grounds). Usually you just need a bunch of state properties. Using KVO your view controllers can respond to changes in the app state without having to wire them all up as delegates of each other. This decouples the view controllers from each other and makes it trivial to add new ones. The other bonus is you can make your ApplicationState class save itself on any state change, and restore when the app restarts. Makes it a lot easier to remember user preferences and is much cleaner than just jamming everything into NSUserDefaults (although you could do that behind the scenes).

      So let's say I have button that changes the sort order of a tableview. All I have to do is something like [[ApplicationState sharedState] setSortMode:AppSortModeByAuthor] from the button view, and the tableview's controller which is observing sortMode gets the message via KVO. It can then re-sort its rows and reloadData.

      Delete
    2. @jsd I actually considered something along those lines a couple of times. I'm going to first try NSNotificationCenter. If the GUI response times are poor, then I will implement a singleton with global state and probably KVO. My data model is fairly well developed and only needed a few changes for the iPad. But I spent a lot of time trying to follow Apple's SplitView controller approach to migrating iPhone apps to the iPad - doesn't work for me. The good news is that I have something that works; just ugly dependencies everywhere and too many differences between the "controllers" for iPhone and iPad. Thanks for the useful suggestions.

      Delete
    3. Interesting, my feeling is that it is largely due to the delegation structure - the view controllers handle features by implementing other view controllers thus introducing more unrelated methods to the controllers. But then the models should be outside which will at least allow seperation of the data. But yes another layer for business logic sounds sensible

      Delete
  6. More more more!

    It's so frustrating hearing from fellow iOS developers the phrase "I usually don't Unit Test my UIViewControllers, it's just too much effort" over and over again while them keeping 300+ lines of untested code in these classes. We need a way out of this mess!

    Motivated by Uncle Bob Martin and his great book "Clean Code", I've increasingly looked into clean ways to decouple business logic and UIViewController business.
    Some general rant from Bob Martin: http://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html
    Ron Lisle attempts to do so with a thing he calls "VIP" architecture: http://iosunittesting.com/category/architectures/


    @Mike Weller: You're looking for a job in London, you wrote. Incidentally I've read about a job offer in London today - no idea if there still is an open position, as the blog post was from March.
    Source (at the bottom): http://www.merowing.info/2013/03/overlaying-application-version-on-top-of-your-icon/

    ReplyDelete
  7. iOS development is not a piece of cake! It requires a lot of creativity and new ideas. Many developers have tried their hands in development but there are only a few who have been successful in the field. ~keep posting~

    ReplyDelete
  8. Even Apple states that a view controller is not a pure controller as in MVC:

    "One can merge the MVC roles played by an object, making an object, for example, fulfill both the controller and view roles—in which case, it would be called a view controller. In the same way, you can also have model-controller objects. For some applications, combining roles like this is an acceptable design."

    http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html

    ReplyDelete
  9. Cocoa for OSX has bindings which allow to tie a model to a view and thus encourages better separation of the model from the controller.

    Searching for an alternative in the iOS world, I came across BlocksKit. One of its categories, NSObject(BlockObservation), could potentially result in a much thinner controller which observes its model and notifies its view.

    ReplyDelete
  10. MVVM with ReactiveCocoa also looks delicious for separating concerns:
    http://cocoasamurai.blogspot.co.at/2013/03/basic-mvvm-with-reactivecocoa.html

    ReplyDelete
  11. At last I'm not the only one thinking that iOS architecture is a big mess which tends to be really difficult to maintain because even Apple does not show best practises to developpers :)
    Thanks for this great article !

    ReplyDelete
  12. Waiting for new posts!

    ReplyDelete
  13. PLEASE hurry and write the next post.

    ReplyDelete
  14. Sorry guys. I start a new job back in London in a month, so things are a little hectic at the moment. I will try and find the time to finish the next post in the coming weeks.

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. You're killing us! I got all the way to the end to find out the next post isn't ready yet :(

    You did an outstanding job expressing the flaws in the UIViewController madness that I'd always felt but couldn't properly articulate.

    Great work.

    ReplyDelete
  17. Please tell me if you'll publish the next post about this problem?

    ReplyDelete
  18. Great article. I am somewhat new to iOS so I wasn't sure if my apprehension about the "view controllers do everything" pattern was just a matter of me not fully drinking the Kool-Aid. Good to hear I'm not the only one looking for a better way. Looking forward to the follow-up article.

    ReplyDelete
  19. Interesting stuff! Please release the next post soon!

    ReplyDelete
  20. Hey! Great article. I've been thinkin the same way for 2 years of my iOS developer practice. What about next part?

    ReplyDelete
  21. Here in my company, there're 2 (and more to come) exact cases,
    hijacked by a self-proclaimed iOS guru who happens to be a Senior Programmer,
    so you can imagine my disappointment when I reached the end
    and realized part 2 has yet to come.

    Really anticipating the follow up!

    ReplyDelete
  22. Very nice post. Looking forward for the follow-up article.

    ReplyDelete
  23. This post is a mind blow, please add some comment to briefly explain how to solve this. I think it would be very useful to many many developers out there.

    ReplyDelete
  24. Thanks a lot, changes my way to implement VC.

    ReplyDelete
  25. Where is the second part, we want it!

    ReplyDelete
  26. Coming from the java world, I always thought there was a bit of a problem with this. Never thought it was great in terms of separation of concern. I'm just facing problem regarding the others who have been taught the "messy" way. I'm trying to be an evangelist but it's not an easy one. Carry on the good work.

    ReplyDelete
  27. Loved your post. But I too am anxious to see this explained more fully. However, I would certainly settle for a sample project using tables, data, business logic- simple, but with enough code to see how it's done. Do you have a sample project? Or could you whip one up fairly quickly as a show and tell? Would be pretty cool to see this in action.

    ReplyDelete
  28. "So where should this navigation logic go? Well… somewhere else. We’ll get into that in a later post. All that matters for now is that the view controller should delegate UI actions like this to somebody at a higher level, who has access to and knowledge of the specifics of your app’s UI workflow."

    really interested to see how achieve this

    ReplyDelete
  29. When is the next post due to come out? Really eager to gain some insight into the "right way"!

    ReplyDelete
  30. After six month, where is the next post?

    ReplyDelete
  31. I also would like to see a modest app done the right way.
    I already knew shoving everything in a View Controller was wrong...

    ReplyDelete
    Replies
    1. I would like to see a modest app done this way, too. However, I would say that it would have to have at least 3 different screens, and perhaps some network access as well. A tableview would be nice. Maybe something like the BrowseOverflow app done in Test Driven iOS Development.

      Too many of these examples have only the one screen, and so they really fall flat on demonstrating that they're usable for more complex apps.

      Delete
  32. OK, sounds good. I totally agree. We're all awaiting the next post!

    ReplyDelete
  33. Interesting. I did a talk on the exact same subject here: https://speakerdeck.com/smorel/how-to-avoid-the-spaghetti-code-monster

    I manage an open source framework that helps dealing with this kind of problems. you can have a look at it here: https://github.com/wherecloud/AppCoreKit

    ReplyDelete
  34. Found a similar article in german about the whole stuff... brought me to this post

    www.ir-technology.net/blog/2014/01/uiviewcontroller-problem-ios-architektur

    ReplyDelete
  35. This comment has been removed by the author.

    ReplyDelete
  36. You can't just leave us hanging, makes me wonder if you have any valid answers to your valid questions, or are just blowing off steam! Come on, next post or fess up that you have no answers!

    ReplyDelete
  37. im grateful for your ideas too and have often thought and discussed the same things. The way forward is clear tho people, you needn't be bugging the author for more material, what do you want him to write your projects too? Its simple, stop thinking UIViewController is the 'C' in 'MVC' and start thinking of it as the top of the food chain in the 'V' hierarchy.... And go write some 'C' classes!! (which I would say need to be fired off from -(BOOL)applicationFinishLaunchWithOptions...... i expect)

    ReplyDelete
  38. we have some world class mobile experts. if you wanna develop your iOS Apps, please visit http://www.taoteapps.com, By visiting the website you can develop your iOS Apps.

    ReplyDelete
  39. I think you see all these problems because most people (including you) have fundamentally misunderstood what MVC means in the Apple world. I recommend reading "MVC as a Compound Design Pattern" in the Apple documentation to clear up how Cocoa MVC is different from classical MVC.

    The clearest indication IMO that you have misunderstood the purpose of controllers is that you state:

    "Controllers are supposed to be nice thin objects funnelling data and events between the model and the view. They shouldn’t contain details of a specific UI framework. iOS’ View controllers are nothing like this. They sit firmly in the presentation layer."

    But this is wrong. Cocoa Controllers ARE supposed to know the details of a specific UI framework. In fact that is the whole point of a view controller. The philosophy of Cocoa is that you are supposed to be able to buy an UIView and a Model object from two different vendors who have nothing to do with each other. In other words model and view objects are supposed to be completely reusable. But somehow you need to make both of these classes who know nothing about each other talk to each other. You need some glue. This glue needs to know about both the model and and the view. This glue is the controller. The UIViewController is not a reusable component. The fact that you write so much about the inability to reuse UIViewController if you do this or that, indicates that you have missed this important part of Cocoa design philosophy. Controllers is where you dump all your non reusable, application specific code.

    But this does not mean that you need to make every UIViewController a huge bloated mess. You should split it into multiple controllers. A controller can be made up of multiple controllers. Your business logic should be moved into model classes. If you got a table view create a specific controller just for implementing the DataSource protocol.

    I think the real problem here is that a lot of people have only ever done iOS development and UIViewController is pretty nifty. But if you come from OS X Cocoa development you would be used to making controllers from scratch. Back in the day a controller was usually just a subclass of NSObject. Don't forget this is still valid to do. Not every controller needs to subclass some specific class like UIViewController.

    I see your point with the horrible Apple example code though. That is a pitty. I never thought of example code as something to look at for the purpose of good application architecture. The Apple documentation does in fact give quite good explanations for how to structure your application. But nowadays it seems people learns almost exclusively by example. I am not sure what the solution to this is. If example applications showed good software design that would increase the size and complexity of the example and take focus away from the specific API being taught.

    Perhaps Apple should have specific example programs just to show good Cocoa Software design.

    ReplyDelete
    Replies
    1. Totally agree with you, controllers are the glue and should now about the model and the UI so that UI can be reusable and model is reusable.

      The business logic should not go into the controllers of course. Business logic only deals with the model layer and should be hosted in a service layer.

      To sum up, the model contains the state, the service manipulates the model, and the controllers watch the model to propagate changes to the UI and listens to the UI to modify model state or call service methods.

      Delete
    2. Can not agree more. Using tranditional MVC you can still create good architecture. The point is making viewcontroller small and simple. Experienced developers know how to separate functions into different models which could be viewcontrollers or regular controllers.

      Delete
  40. We have lived with VC the monster for years. Would love an alternative suggestion.

    ReplyDelete
  41. This comment has been removed by the author.

    ReplyDelete
  42. This comment has been removed by the author.

    ReplyDelete
  43. This is great stuff, Mike. Growing up as a developer through Smalltalk, all of this is part of my DNA, but I, too, have succumbed to Apple's pressures and muddied my code by bloating ViewControllers (I hate VCs... and don't even get me started on ModelControllers...). Reading this has reminded me of my roots and inspired me to refactor my current project, and for that I thank you. Keep posting in this series, please!!

    For those advocating the virtues of VCs, while I agree that we need to constantly be evolving as developers and scientists, this experiment has proven more disastrous than advantageous, as evidenced by the proliferation of outright bad codebases written by so-called experience OO developers, and the number of apps that crash and fail for, apparently, no reason whatever.

    MVC as a design archetype was created when things were much simpler (it was forged in the fire that gave us Smalltalk), admittedly, however, it has persisted for its simplicity, and most of the so-called enhancements or evolutions of it (MVVC, VCs, MCs, etc.) are nothing more (IMHO) than the hackings of folks who are not getting the essence and beauty of true MVC.

    As a technology consultant who has interviewed over 800 technology applicants I can attest to the sparse few who could articulate the actual responsibilities of the constituents of the MVC pattern, and many of these folks had Master's degrees from prestigious universities. The teachings now are based on faulty thinking and shallow understanding of OO fundamentals, as they have become dilute over the generations of developers such that the collective thinking is flawed at its heart, yet is propagated as correct and accepted.



    So, thank you Mike, for casting a harsh light on a dark shadow.

    That's just my opinion... I could be wrong. ;)

    ReplyDelete
    Replies
    1. ** steps off soapbox ** and runs from tomato-hurlers...

      Delete
  44. Where is the continuation post, did you get a chance to write it?

    ReplyDelete
  45. Great article, much appreciated as I thought I was just being mad. One concise question for people out there - IBOutlets and IBActions: I can wire them in to my view or my controller (or both). Logic says they should be in the view but then one must implement a protocol/delegate to interact with the controller/model. This seems like a lot of effort for nothing much. Alternative is wiring all the IBOutlets/IBActions into the viewcontroller which seems a problem if you need different views for iPad/iPhone ... thoughts/opinions?

    ReplyDelete
  46. Hey, this is a good article you wrote there. Since I am one of these devs (1 year exp.) I am looking forward to reading your articles about solving these problems. But when will you write them?

    ReplyDelete
  47. Mike! I really wanted to read the rest of the series you decided to begin a year ago (but didn't). I can glean your intent from reading the post, but it would have been cool to see where it actually leads.

    I'm guessing you'd replace all of the business logic common to View Controllers with bite size objects that can be unit tested? I have done this before when implementing UITableView Datasource protocols that started to get out of hand when everything was tossed into a single View Controller. Totally makes sense.

    Anyway, please post more articles. I want to see your unique solution to the problem.

    ReplyDelete
  48. Thanks for this great article. I am very disappointed that you didn't make the next one with your solutions. I'd be glad to read it.

    ReplyDelete
  49. Please finish the series! I was nodding the whole way through your post. I'd love to see the next one. It's never to late to resurrect the series!

    ReplyDelete
  50. The first issue of objc guides you on how to make your view controllers cleaner and lighter: http://www.objc.io/issue-1/

    ReplyDelete
  51. 2015 is here, and we're still waiting. Please teach us the right way!

    ReplyDelete
  52. Hey! Couldn't you at least post a few more pics of kittens?

    ReplyDelete
  53. Mike, all we are looking forward for you to continue!
    Are you alive?

    ReplyDelete
  54. What's the alternative? It's easy to criticism.

    ReplyDelete
  55. Here is the good solution

    http://www.teehanlax.com/blog/model-view-viewmodel-for-ios/

    ReplyDelete
  56. Nice post! Please keep working on new articles!

    ReplyDelete
  57. This is awesome article. It made me actualize many past errors and i do still have some I'm working on to reduce. Definitely a wonderful article on why View Controller go beyond the point of frustration and you don't want to touch it again.

    ReplyDelete
  58. Excellent post by Mike on the problem with massive view controllers.

    I've studied different iOS architectures such as MVC, MVVM, ReactiveCocoa, for the last two years. I found Uncle Bob's Clean Architecture the best way to decouple classes and make your dependency graph very simple. I've organized our code into 3 main components - view controller, interactor, and presenter, and abstract away model data by creating request, response, and view model objects to pass through their boundaries.

    The result is amazing. With multiple developers on the project, we are able to know exactly which file and method to look when finding things. The other day, I had to fix a bug on a profile screen that was developed by another developer. I didn't write one line of this code but I know exactly where to find it. The methods are also very short as a result of apply Clean Architecture. I also added a router component to it and now we can use multiple storyboards with ease. This also allows us to write tests much easier as we only need to test methods at the boundaries.

    I wrote about my experiences with Clean Architecture at http://clean-swift.com/clean-swift-ios-architecture/

    ReplyDelete
  59. TechnoSoftwar having several years experience working with global customers, connecting our professionals to their business needs, as their IT Development & Support Partner. TechnoSoftwar having a team of dedicated and experienced softwares developers that works for your all business needs.TechnoSoftwar deals in web design and development, Customized ERPs, CRMs, Web & Mobile Applications, eCommerce platforms etc.

    ReplyDelete
  60. This is an excellent post. I am still holding out hope for a continuation. Thanks, hope you are doing well.

    ReplyDelete
  61. I would love a follow-up post! This was a great read.

    ReplyDelete
  62. Here is also a little article to help you get your gps under control

    ReplyDelete