Like many of the iOS (and macOS) developers today, I  began developing for Apple devices by following Apple's own tutorials. I  still remember how magical it was, back in 2011 and coming from a  background of Python and C++, to open a .xib file and the corresponding class on the assistant editor and just drag from one file to the other to create IBOutlets and IBActions.  'This is great', I thought. It was really refreshing to be relieved of  boilerplate code and focus only on what my app should do.

It didn't take long though, and I began seeing the downsides of  relying on such contraption. Specially when I moved from working on my  small educational projects and began working on larger projects with  other developers.

Merging hell

Both .xib and Storyboards are not meant to be human  readable. You are supposed to work with the nice graphical  representation of those files that Xcode generates for you. This means  that if you and your coworker happen to be doing work on the same file,  one of you will have to deal with solving merge conflicts later on. A  lot of the times, merging will involve choosing changes randomly and  then opening the file on Xcode and figuring out what needs to be done to  ensure that the file is in  the state it needs to be.

Last but not least: Xcode will alter the Interface Builder files just  by opening them. Sometimes the slightest modification results in  massive changes to the file (this is because the XML nodes of the file are represented as a set in memory and sets do not guarantee order). Other times Xcode will just change the syntax of the .xib for the fun of it.

All I did was open the file

Tight coupling with (almost) no compile time safety

Have you ever ran your app, with confidence that it would work as  expected, just to see it crash once you open a certain view controller?  And upon checking what exactly the crash was, figured that it was an  IBOutlet that was not connected? Yeah, we've all been there.

Moreover, Interface Builder files are harder to reuse and even harder  to refactor. It's almost impossible to get a single look at a file like  that and have a good idea of everything that that file is defining.

I've seen just about everything when it comes to Interface Builder  woes. From files that would suddenly make Xcode crash just from opening a  certain file to a view controller that could be instantiated with  different xibs and when instantiated with one of these xibs could lead to a crash because, wait for it, an IBOutlet didn't have the correspondent element on that xib and since Xcode generates the IBOutlets as implicitly unwrapped optionals, any attempt to access that variable would cause the app to crash.

Dragging and clicking things is not my strong suit

There are small things that make the task of adding a constraint  through the Interface Builder an infuriating task. Like having to move  to the next layout constraint field so that Xcode will not ignore my  constraint when I click Add Constraint, the time it takes for  Xcode to open a single Storyboard, or being in the middle of editing  something and having Xcode crash on me (and then having to reopen the  project and figure out what was the last change that got saved).

When UI work is done with code, it's easier to figure out exactly  what is happening or what do I need to change in that code that was  written one year ago to implement that new feature. Specially if the  code was written with consideration for good practises, everything will  be explicit and I will know exactly where to look. This is something  that reminds me of the joys of the years I spent working with Python: Explicit is better than implicit.

But writing auto layout code is so verbose and tedious...

There are a few frameworks that take away the pain of writing auto layout code. My framework of choice is Cartography.  Cartography makes writing auto layout code extremely simple and  intuitive. Just check out this example from the project's README:

constrain(view) { view in  
    view.width  == 100
    view.height == 100
}

let group = ConstraintGroup()

// Attach `view` to the top left corner of its superview
constrain(view, replace: group) { view in  
    view.top  == view.superview!.top
    view.left == view.superview!.left
}

/* Later */

// Move the view to the bottom right corner of its superview
constrain(view, replace: group) { view in  
    view.bottom == view.superview!.bottom
    view.right  == view.superview!.right
}

UIView.animateWithDuration(0.5, animations: view.layoutIfNeeded)  

Did you see that? Constants! Sweet constants!

There will always be those cases that you feel at a loss on how to  implement something with auto layout. For those cases I can tell you  this: there's nothing wrong with overriding layoutSubviews and calculating your frames there. Don't ever let someone tell you otherwise. Always favor the simplest solution to a problem.