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.

350 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. This comment has been removed by a blog administrator.

    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. This comment has been removed by a blog administrator.

    ReplyDelete
  63. This comment has been removed by a blog administrator.

    ReplyDelete
  64. This comment has been removed by a blog administrator.

    ReplyDelete
  65. Apple has made certain decisions with their framework design and tools which I believe contributes to this problem.click here

    ReplyDelete
  66. Though cool and hip, these cell phones are extremely sensitive. They have internal elements very delicate that a single drop might render the phone dead. So with this, having a cellular phone is synonymous to having cellphone cases.
    How to fix a dead phone

    ReplyDelete
  67. If you have your mind set on an iPhone app, then I urge you to also consider what happens when it is a success? Yes, I am assuming that your iPhone application will be a success because they are growing so rapidly.
    Mac Repair Bangor Maine

    ReplyDelete
  68. Established in 2013 to provide exciting, effective design solutions. Since its inception, Globalwebsolution has grown considerably into a recognised brand design and digital marketing innovator. Rewarding our clients with compelling visual solutions that create value and recognition in their marketplace.

    ReplyDelete
  69. he clients can exploit the component to flip between light foundation and dull foundation consistently. my review here

    ReplyDelete
  70. Good article knowledge gaining article. This post is really the best on this valuable topic. Mobile App Developers

    ReplyDelete
  71. Excellent website you have here: so much cool information!.. 24 hour truck tire repair

    ReplyDelete
  72. Excellent website you have here: so much cool information!.. cell phone deals

    ReplyDelete
  73. I think that thanks for the valuabe information and insights you have so provided here. TweakBox

    ReplyDelete
  74. I truly adored perusing your online journal. It was exceptionally all around wrote and simple to undertand. Not at all like extra online journals I have perused which are truly not tht great. I likewise discovered your posts extremely fascinating. Truth be told subsequent to understanding, I needed to go demonstrat to it to my companion and he ejoyed it also! AirShou Screen Recorder

    ReplyDelete
  75. i am surprisingly here. I discovered this board and I in discovering It really accommodating and it helped me out a great deal. I would like to present something back and help other people, for example, you helped me. FtiOS iOS 11

    ReplyDelete
  76. This was a really great contest and hopefully I can attend the next one. It was alot of fun and I really enjoyed myself..
    IT managed Services Chicago

    ReplyDelete
  77. Great site, Distinguished criticism that I can handle. Im pushing ahead and may apply to my present place of employment as a pet sitter, which is exceptionally charming, yet I have to extra grow. Respects. Tinyumbrella Download

    ReplyDelete
  78. Fabulous web journal! Do you have any tips and indications for trying authors? I'm wanting to begin my own particular site soon however I'm somewhat lost on everything. Would you propose beginning with a free stage like WordPress or go for a paid alternative? There are such a large number of alternatives out there that I'm totally overpowered .. Any proposals? Much obliged! Tutu Helper

    ReplyDelete
  79. Great post yet I was thinking about whether you could compose a litte more on this subject? I'd be extremely grateful on the off chance that you could expound a smidgen further. Welcome it! Panda Helper

    ReplyDelete
  80. It is really a great work and the way in which you are sharing the knowledge is excellent.Thanks for your informative article

    software testing course in chennai

    ReplyDelete
  81. Thanks for the precious information. I m really grateful to you.
    imessage on pc

    ReplyDelete
  82. Really thanks to you because valuable information. Track Easily throught blue dart courier tracking

    ReplyDelete
  83. Numerous mixture versatile apps today use Apache Cordova, a basic stage that comprises of an arrangement of JavaScript APIs for getting to cell phone capacities through modules that are worked with local code. clash royals

    ReplyDelete
  84. Really cool post, highly informative and professionally written and I am glad to be a visitor of this perfect blog, thank you for this rare info!
    industrial safety course in chennai

    ReplyDelete
  85. It's interesting that many of the bloggers to helped clarify a few things for me as well as giving. Most of ideas can be nice content. The people to give them a good shake to get your point and across the command.
    offshore safety course in chennai

    ReplyDelete
  86. Thanks for sharing the details! thanks for sharing information,nice article.
    i would like to more information from your side!
    please added more then tips!Am working in
    Excel Training In Hyderabad

    ReplyDelete
  87. I am so glad today fortune turned to face me and not the booty as usual there were financial difficulties and I decided to play online casino stumbled upon this site reliable online casino list how good it was that I found it

    ReplyDelete
  88. Looking for File Sharing Application? Here is the Best File Sharing Application of 2019 to Transfer Files from PC to Android/iPhone Mobile. Written by Meet Patel

    ReplyDelete
  89. Awesome post, I found this article very interesting check out my blog android app developers.

    ReplyDelete
  90. There is no doubt in the app you can easily download the app the most exciting features are waiting for you can easily go with Tutu App. There are nothing extra features required in this app because the most important items are already available in his update versions. Download Latest Version. Also love Snaptube For PC

    ReplyDelete
  91. who don't want to download a paid app for free? Well, in this post we are going to introduce such app that will allow you to download any app at free of cost. We are talking about Panda Helper APK for Android.
    Panda Helper VIP

    ReplyDelete
  92. Emoji icons are cute little images that represents something in our daily lives. You could, for example substitute "I love you" with a simple emoji icon that represents a heart. It is commonly used and supported by most cell phones in Japan.
    emoji

    ReplyDelete
  93. When I initially left a comment I appear to have clicked the -Notify me when new comments are added- checkbox and now each time a comment is added I receive four emails with the exact same comment. There has to be an easy method you can remove me from that service? Kudos! get it now

    ReplyDelete
  94. Thank you for sharing some cool information regarding wordpress website design. Here I found one company in Brisbane area and it is very popular in Australia. They provide many service like seo birsbane, website design, digital marketing. Thank you!

    ReplyDelete
  95. This is an awesome post. Really very informative and creative contents.
    ios app Devlopment company in chennai

    ReplyDelete
  96. If you are intrested in the thai lottery result tips and updates and thai lottery result get it from the Link

    ReplyDelete
  97. Great blog thanks for sharing Looking for the best creative agency to fuel new brand ideas? Adhuntt Media is not just a digital marketing company in chennai. We specialize in revamping your brand identity to drive in best traffic that converts.

    ReplyDelete
  98. Nice blog thanks for sharing Is this a special day for you? Beautiful and fragrant flowers are sure to make it even more amazing of a day no doubt. This is why Karuna Nursery Gardens offers you the best rental plants in Chennai that too at drop dead prices.

    ReplyDelete
  99. Excellent blog thanks for sharing Run your salon business successfully by tying up with the best beauty shop in Chennai - The Pixies Beauty Shop. With tons of prestigious brands to choose from, and amazing offers we’ll have you amazed.

    ReplyDelete
  100. Soma pill is very effective as a painkiller that helps us to get effective relief from pain. This cannot cure pain. Yet when it is taken with proper rest, it can offer you effective relief from pain.
    This painkiller can offer you relief from any kind of pain. But Soma 350 mg is best in treating acute pain. Acute pain is a type of short-term pain which is sharp in nature. Buy Soma 350 mg online to get relief from your acute pain.

    https://globalonlinepills.com/product/soma-350-mg/


    Buy Soma 350 mg
    Soma Pill
    Buy Soma 350 mg online



    Buy Soma 350 mg online
    Soma Pill
    Buy Soma 350 mg

    ReplyDelete
  101. If you're an employer and you want to keep an eye on your new employees and their activities on the Internet, this page will be perfect for you - spy-apps-software.com Top lists of the best spy apps!

    ReplyDelete
  102. TutuApp is a trending app nowadays because it contains many new and good features of games, music, etc. Tutuapp APK
    TutuApp APK
    Tutu App
    Tutu App APK
    TutuApp Free
    TutuApp ios

    ReplyDelete
  103. A IEEE project is an interrelated arrangement of exercises, having a positive beginning and end point and bringing about an interesting result in Engineering Colleges for a particular asset assignment working under a triple limitation - time, cost and execution. Final Year Project Domains for CSE In Engineering Colleges, final year IEEE Project Management requires the utilization of abilities and information to arrange, plan, plan, direct, control, screen, and assess a final year project for cse. The utilization of Project Management to accomplish authoritative objectives has expanded quickly and many engineering colleges have reacted with final year IEEE projects Project Centers in Chennai for CSE to help students in learning these remarkable abilities.



    Spring Framework has already made serious inroads as an integrated technology stack for building user-facing applications. Spring Framework Corporate TRaining the authors explore the idea of using Java in Big Data platforms.
    Specifically, Spring Framework provides various tasks are geared around preparing data for further analysis and visualization. Spring Training in Chennai


    ReplyDelete

  104. Tutu Helper is the one of the best ios,android App store to get the tons of free app and game. Here the latest version of TutuApp of free.
    Tutu Helper
    Tutu App

    ReplyDelete
  105. TutuApp is a trending app nowadays because it contains many new and good features of games, music, etc. TutuApp APK
    Tutu App

    ReplyDelete





  106. Showbox Apk is a meta-search engine scraper that scrapes data from the world wide web and provides you with the best for your movies and TV shows.
    It works directly as well as p2p i.e peer to peer.
    if you are looking for free movies and tv shows you can find them on showbox app its easy to download and install
    you can stream movies and tv shows on tvtap for free its great app for all types of live stream
    if you wanna watch free movies and tv shows you can tune in to beetv app for free

    whatsapp status

    RedBox TV App has introduced a lot more features than any other TV Channels Streaming App. It does have categories, depending on Counties as well.
    To find out your native language you can swipe to Country based categories, where you’ll find the channels which are highly native to your region and language. That’s an extra of RedBox TV App.
    Apart from this, RedBox is available for almost every possible platform. Unlike the other apps, Redbox tv don’t claims any fake hoax, all the stuff where are features in this post will be served.















    ReplyDelete
  107. Awesome blog thankks for sharing 100% virgin Remy Hair Extension in USA, importing from India. Premium and original human hair without joints and bondings. Available in Wigs, Frontal, Wavy, Closure, Bundle, Curly, straight and customized color hairstyles Extensions.

    ReplyDelete
  108. Very useful blog thanks for sharing IndPac India the German technology Packaging and sealing machines in India is the leading manufacturer and exporter of Packing Machines in India.

    ReplyDelete
  109. Good job! Fruitful article. I like this very much. It is very useful for my research. It shows your interest in this topic very well. I hope you will post some more information about the software. Please keep sharing!!
    SEO Training in Chennai
    SEO Training in Bangalore
    SEO Training in Coimbatore
    SEO Training in Madurai
    SEO Course in Chennai
    SEO Course in Chennai
    SEO Course in Bangalore
    SEO Course in Coimbatore

    ReplyDelete
  110. I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.
    business analytics course
    data science interview questions

    ReplyDelete
  111. I finally found great post here.I will get back here. I just added your blog to my bookmark sites. thanks.Quality posts is the crucial to invite the visitors to visit the web page, that's what this web page is providing.
    data science course Mumbai
    data science interview questions
    data analytics course in mumbai

    ReplyDelete
  112. I wanted to thank you for this excellent read!! I absolutely loved every little bit of it. I have got you book marked to look at new stuff you post…
    Tech geek

    ReplyDelete
  113. Poker online situs terbaik yang kini dapat dimainkan seperti Bandar Poker yang menyediakan beberapa situs lainnya seperti http://62.171.128.49/hondaqq/ , kemudian http://62.171.128.49/gesitqq/, http://62.171.128.49/gelangqq/, dan http://62.171.128.49/seniqq. yang paling akhir yaitu http://62.171.128.49/pokerwalet/. Jangan lupa mendaftar di panenqq

    ReplyDelete
  114. This is a wonderful article, Given so much info in it, Thanks for sharing. CodeGnan offers courses in new technologies and makes sure students understand the flow of work from each and every perspective in a Real-Time environmen python training in vijayawada. , data scince training in vijayawada . , java training in vijayawada. ,

    ReplyDelete
  115. Allinfodesk: Jobs in Pakistran 2020 Latest Vacancies
    https://allinfodesk.com/category/jobs-in-pakistan/
    Jobs in Pakistan
    https://allinfodesk.com/category/jobs-in-pakistan/

    ReplyDelete
  116. Impressive! I finally found great post here. Nice article on data science . It's really a nice experience to read your post. Thanks for sharing your innovative ideas to our vision.
    Data Science Course Training in Bangalore

    ReplyDelete

  117. Thank you for taking the time to provide us with your valuable information. We strive to provide our candidates with excellent care
    http://chennaitraining.in/creo-training-in-chennai/
    http://chennaitraining.in/building-estimation-and-costing-training-in-chennai/
    http://chennaitraining.in/machine-learning-training-in-chennai/
    http://chennaitraining.in/data-science-training-in-chennai/
    http://chennaitraining.in/rpa-training-in-chennai/
    http://chennaitraining.in/blueprism-training-in-chennai/

    ReplyDelete
  118. Great blog !It is best institute.Top Training institute In chennai
    http://chennaitraining.in/openspan-training-in-chennai/
    http://chennaitraining.in/uipath-training-in-chennai/
    http://chennaitraining.in/automation-anywhere-training-in-chennai/
    http://chennaitraining.in/microsoft-azure-training-in-chennai/
    http://chennaitraining.in/workday-training-in-chennai/
    http://chennaitraining.in/vmware-training-in-chennai/

    ReplyDelete
  119. You made some decent points there. I looked on the internet to learn more about the issue and found most individuals will go along with your reviews views on this website.

    ReplyDelete
  120. Excellent web site you have got here.. It’s hard to find excellent writing like yours nowadays.lap I truly appreciate people like you! Take care!!

    ReplyDelete

  121. TeaTv Apk Is Another One Of The Best Online Streaming Application For Smartphone Users. This Application Is Available For Android & IOS Users. Also, You Can Connect This Application Easily With Amazon Firestick, Android Smart TVs, Start Settop Box, Etc. There are more apk to online streaming is Mediabox apk for smartphone users ( Android and ios ) and
    Best Wishes Quotes 
    Watch Free online Movies 
    onhax 
    onhax android 
    Hulu Apk 

    ReplyDelete
  122. I have to search sites with relevant information on given topic and provide them to teacher our opinion and the article.

    business analytics courses

    ReplyDelete
  123. keep up the good work. this is an Ossam post. This is to helpful, i have read here all post. i am impressed. thank you. this is our courses in data analytics
    courses in data analytics | https://www.excelr.com/data-analytics-certification-training-course-in-mumbai

    ReplyDelete
  124. Appriciable article, We at Property Hunters shifted this service to a level much higher than the broker concept. you can see more details like this article Good location in pearl qatar

    ReplyDelete
  125. Thank you so much for sharing this nice informations.
    android training institutes in coimbatore

    data science course in coimbatore

    data science training in coimbatore

    python course in coimbatore

    python training institute in coimbatore

    Software Testing Course in Coimbatore

    CCNA Course in Coimbatore

    ReplyDelete
  126. wonderful article. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. This article resolved my all queries. keep it up.
    data analytics course in Bangalore

    ReplyDelete
  127. The thought on iOS app architecture and Test Driven Development has been a great concept to discuss. The ideas has been articulated with professional skills and efforts. I wish you further success in the next jot down.
    Web Designing Course Training in Chennai | Web Designing Course Training in annanagar | Web Designing Course Training in omr | Web Designing Course Training in porur | Web Designing Course Training in tambaram | Web Designing Course Training in velachery

    ReplyDelete
  128. I have honestly never read such overwhelmingly good content like this. I agree with your points and your ideas. This info is really great. Thanks.
    SAP training in Kolkata
    Best SAP training in Kolkata
    SAP training institute in Kolkata

    ReplyDelete
  129. I'm not one of those readers that comments on articles often, but yours really compelled me. There's a lot of interesting content in this article that is interesting and bold.
    SAP training in Mumbai
    Best SAP training in Mumbai
    SAP training institute Mumbai

    ReplyDelete
  130. I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.

    Correlation vs Covariance

    ReplyDelete
  131. It's so great to know you are a writer that cares about the information you provide. This is smartly done and well-written in my opinion.
    SAP training in Kolkata
    SAP training Kolkata
    Best SAP training in Kolkata
    SAP course in Kolkata
    SAP training institute Kolkata

    ReplyDelete
  132. I have express a few of the articles on your website now, and I really like your style of blogging. I added it to my favorite’s blog site list and will be checking back soon…keep it up
    Ai & Artificial Intelligence Course in Chennai
    PHP Training in Chennai
    Ethical Hacking Course in Chennai Blue Prism Training in Chennai
    UiPath Training in Chennai

    ReplyDelete
  133. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. This article inspired me to read more. keep it up.
    Correlation vs Covariance
    Simple linear regression

    ReplyDelete
  134. Awesome blog. I enjoyed reading your articles. You can install all paid games for free with tutuapp
    tutuapp download

    ReplyDelete
  135. After reading your article I was amazed. I know that you explain it very well. And I hope that other readers will also experience how I feel after reading your article.
    Data Analyst Course

    ReplyDelete
  136. Great Article… I love to read your articles because your writing style is too good, its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.



    Robotic Process Automation (RPA) Training in Chennai | Robotic Process Automation (RPA) Training in anna nagar | Robotic Process Automation (RPA) Training in omr | Robotic Process Automation (RPA) Training in porur | Robotic Process Automation (RPA) Training in tambaram | Robotic Process Automation (RPA) Training in velachery



    ReplyDelete
  137. Neither a transistor nor an artificial neuron could manage itself; but an actual neuron can. artificial intelligence training in hyderabad

    ReplyDelete
  138. This is my first visit to your blog! We are a team of volunteers and new
    initiatives in the same niche. Blog gave us useful information to work. You
    have done an amazing job!

    artificial intelligence training in Bangalore

    ReplyDelete
  139. To establish a network by putting towers in a region we can use the clustering technique to find those tower locations which will ensure that all the users receive optimum signal strength.
    data science training bangalore

    ReplyDelete
  140. wonderful one. I would like to thank you for the efforts you had made for writing this awesome article. This article inspired me to read more. keep it up.
    Correlation vs Covariance
    Simple linear regression
    data science interview questions

    ReplyDelete
  141. I like viewing web sites which comprehend the price of delivering the excellent useful resource free of charge. I truly adored reading your posting. Thank you!

    data science interview questions

    ReplyDelete
  142. Awesome blog. I enjoyed reading your articles. You can install all paid games for free with tutuapp
    tutuapp download

    ReplyDelete
  143. Download and install panda helper on Android and iOS devices

    ReplyDelete
  144. This Was An Amazing ! I Haven't Seen This Type of Blog Ever ! Thankyou For Sharing, data sciecne course in hyderabad

    ReplyDelete
  145. Attend online training from one of the best training institute Data Science Course in Hyderabad

    ReplyDelete
  146. You have shared a nice article here. After reading your article I got very much information and It resolved many of my doubts. Thanks for sharing this article here.
    IELTS Coaching in chennai

    German Classes in Chennai

    GRE Coaching Classes in Chennai

    TOEFL Coaching in Chennai

    spoken english classes in chennai | Communication training


    ReplyDelete