IB Free: Living Without Interface Builder and Loving It

“Your eyes can deceive you. Don’t trust them.”
—Obi-Wan Kenobi

Most of the iOS leads here at Raizlabs are not using xibs or Storyboards: in other words, we are not using Interface Builder (IB) to construct our views. In this blog post, I’m going to describe the benefits of being IB-free, and will show you how to build a project and control a view’s layout using Anchorage. (If you’re targeting iOS 9 or before, you can also use Cartography, which isn’t based on layout anchors) The finished example project is available on GitHub here.

Why Bother?

When I started working with iOS, Interface Builder (IB) was a wonderful thing: I could create views and implement navigation without writing code or even understanding UIKit. However, once my projects increased in complexity, I found myself becoming more and more frustrated: when designs evolved, I would often just have to start the layout over by clearing all constraints. I often couldn’t get complex layouts to work in IB and I did not understand UIKit well enough to implement anything that IB didn’t allow out of the box. This frustration was compounded immensely when I began to work on projects with multiple team members and source control: I couldn’t understand what other team members had done by looking at a diff of xibs or Storyboards, let alone untangle a merge conflict.

But, with all that frustration, it was not easy to imagine a better way. IB is Apple’s way, and far more often than not in the world of iOS, we follow the guidelines that Apple sets, usually for good reasons. However, this is one case where being a rebel pays off: Swift and tools like Anchorage make coding layout a joyful experience. Once I stopped using IB, I also developed a deeper understanding of how UIKit works. Things like navigation, presentation, and complex routing ceased to be a mystery. Additionally, since all of the layout was now in code, refactoring it or interpreting another developer’s layout work became just like working on any other code.

The rest of this post is going to demonstrate the joy of life without IB. But first, if you’d like a little more motivation to learn these techniques, here’s what some other iOS developers at Raizlabs have to say about leaving IB in the dust:

“Given that Interface Builder is heavily touted by Apple as the ideal solution for building UI, I was very reluctant to ditch it, especially given how wordy programmatic layout used to be. Once I saw how concise and declarative doing auto-layout in Swift could be, I decided to give it a shot. After defaulting to no xibs for about a week, I was totally sold. I feel the maintainability of my view code has gone up considerably, a few blocks of Swift code is much more approachable to a new developer than a tangled mess of Interface Builder constraints, and tracking changes over time in version control is also possible where it really wasn’t before.”
—Nick Bonatsakis, iOS Team Lead at Raizlabs

“I used to be tempted by IB all the time, but every time I got burned, and now I’m not even tempted any more.”
—Zev Eisenberg, iOS Developer at Raizlabs, author of BonMot

“I was skeptical about going xibless at first, but I think it’s decreased dev time of complex screens, even across size classes.”
—Rob Visentin, iOS Developer at Raizlabs, author of Anchorage and Raze

“By going xibless, all of the layout code remains human-readable, which saves time when reviewing pull requests, and makes it easier for new developers to make changes to an existing layout.”
—Mike Skiba, iOS Developer at Raizlabs, author of BentoMap

Setting Up a Project Without a Storyboard

The first stumbling block in leaving IB behind is setting up a project without relying on a storyboard. However, this can be done in three steps:

  1. Create a new project and delete the ‘Main’ storyboard
  2. In the app target’s ‘General’ tab, under ‘Deployment Info’ delete the text ‘Main’ from the ‘Main Interface’ combo box.
    Removing 'Main' as an interface in Xcode
  3. Add the following code to launch your main view controller under the app delegate’s didFinishLaunchingWithOptions()

  4. (Optional if you want to use Anchorage) Install Anchorage via Cocoapods or Carthage. Or, if you do not want to use a dependency manager and you are using Swift 3.0, you can just add this file to your project.

That’s it. You are now IB free. You rebel scum 🙂

Organizing Views in a View Controller

Let’s start by placing two views in a view controller. One of these views is going to be built with a xib and the other will be built without IB and use Anchorage for its layout.

 

A view controller with two views: one with a xib and one without.

I will show how each of these views is built in the next section, but first I want to show you how a simple layout of these two views in a view controller can be accomplished without IB.

Instantiate the views

First, just initialize the views and call a configure function in your view controller’s viewDidLoad() method:

Note how the two views are declared as private constants: we don’t need to expose these views as public and we do not expect them to change.

Your first xibless layout!

The next thing you will do is define configureViews() in a private extension. In general, it is a good idea to make liberal use of extensions in Swift to 1) help organize your code: all of your view configuration code can go in this extension, and 2) keep large blocks of code private by marking the extension private.

Here’s what your configureViews() function will look like:

Note that there are clearly defined sections in this function: View Hierarchy, Style, and Layout. Each of these could be its own function as well, depending upon the complexity of your configuration. We’ll go over each of these sections separately.

View Hierarchy

Since IB isn’t managing your views anymore, you’ll need to add them manually. This is fairly straightforward, though keep in mind that order matters (e.g. the first subview added will be behind the next subview added and so on). Also keep in mind that the view hierarchy should be in place before any layout is coded: you can not activate constraints on views that are not in the same (or any) view hierarchy.

Style

Here’s where you style the view controller’s view and fill it with any static content. Note how, unlike with IB, you’ll have easy access to any predefined styles (colors, fonts, etc), as well as any static content like localized strings.

Layout

Now for the fun stuff. First, note how the layout section is further grouped into sections of code for each of the views, and that each section has a comment. This is a great way to lay the groundwork for your layout in code: first, write comments describing each section of the layout. This will be useful to other developers reading your code, but even more helpful is that you will first describe what the entire layout will look like before you code your constraints. Layouts often consist of interdependent elements, and you’ll be able to code more efficiently if you make sure you understand the entire layout before coding it.

Now for the code that lays out the first view, xibView, in the top half of the view controller’s view. xibView’s top is constrained to the top of the view controller’s view:

This is Anchorage in action. The use of the == operator creates a NSLayoutConstraint between xibView and the view controller’s view. If you wanted to save that constraint for later animation, you can make an assignment while you create the constraint:

Now that xibView is constrained to the top, we also want its width to equal the width of its parent view. This can be done by constraining xibView’s width anchor to its parent’s, or, as in the code, you can constrain it to its parent view’s horizontal anchors:

Finally, xibView should be half the height of its parent view:

Note how anchorage lets you use the / operator here to specify an adjustment to the constraint.

Now that we have the layout of the top view finished, laying out the position of the lower view is fairly straightforward. xiblessView’s top should be constrained to xibView’s bottom, and it should have the same height and width of xibView:

A Tale of Two Views

In this section I’m going to show you how to code the XiblessView that was used above. I’ll also compare the code of XibView and XiblessView.

Creating XiblessView

XiblessView’s initialization code is as follows:

The button is initialized as a constant, and configureView() is called as part of initializing the view. Even though we are never going to be using the initializer with NSCoder (because that initializer is only called as part of loading a xib for a view), we still have to place this initializer in this subclass because it is marked as required by its superclass.

The view configuration is placed in a private extension:

Note how the same pattern is followed here as with the view controller above: the button is first added as a subview, followed by code to style it, and finally code describing its layout. The only layout code here is the two constraints centering the button by setting the button’s center anchors to the view’s center anchors (the height and width of the button are implicitly set by its title).

Comparing the Two Views

The same layout and style is accomplished in the view with the xib with the following XML code:

Imagine seeing this code as part of a pull request. In comparison to the XiblessView code above, it is much more challenging to interpret even this simple layout (and imagine how this looks with more complex views, not to mention multi-view storyboards).

The xib code above also illustrates one of the more frustrating things about its structure: interpreting constraints becomes a game of look-up because of the identifiers that are used. You have to look up where each is declared to know that Zke-ge-Tpo is the button and EBI-05-sQD is the view (this is also aided by filling in the userLabel for each: if you insist on using IB, you should always fill in these values in the identity inspector in the left sidebar of the editor). This lookup game is nearly impossible once the view becomes more complex, or when it is part of a storyboard.

Trying a More Complex Layout

The layout of your views rarely remains static throughout the lifetime of an app. Views are often reorganized for better usability or to implement features added after they have been initially created. In previous projects, when I used IB and was presented with a new design for a complex screen, I would struggle with resetting constraints because interdependency or a change in view hierarchy would cause other constraints to break. Even doing simple things like dropping in and configuring a new control or view into an existing layout could be tricky because if there were overlapping views it is difficult to select and drag the correct one. However, when the layout is in code, it is much easier to refactor and reuse existing code. Additionally, you can easily keep existing code and change a view’s layout based upon its style.

For example, let’s say that a version of the view requires four additional buttons in each corner of the screen:

The two views again, but this time the xibless view has four corner buttons

Let’s say that you also want to keep the original version of the view as well, so there are two styles: one without corner buttons and one with them.

Initializing a UIView with Style

Here’s the public code of xiblessView that can have four corner buttons in addition to a center button depending upon its style:

Walking through the code above, you’ll first notice a new enum: ViewStyle. This enum is used in the initializer to set the type of view: either with corner buttons or without.

Next, there’s an array of corner buttons. Note that these are declared as a constant optional array of UIButtons. We want our UI elements to be constant, and in this case the corner buttons are also optional because they may or may not be present, depending upon the view’s style.

If you examine the new initializer, you’ll see that the view is now initialized with a ViewStyle. Depending upon the style, the corner buttons will or will not be created during initialization. The number of buttons depends upon a privately declared constant, which we’ll go over in the next section.

Finally, because we now have a custom initializer that should always be used, we’re going to mark init(frame:) as unavailable. This makes the method unavailable to autocomplete, as well as showing a compile error if it is used. For good measure, we can go ahead and do the same thing with the init(coder:) method.

Coding the corner button layout

The private extension that handles the view’s configuration now looks like this:

Note the Constants struct: it is used to specify the number of corner buttons as well as specify any layout constants that are used as part of the layout.

Next, a ButtonPosition enum is created to associate indices 0 through 4 with different corner positions.

In configureView, configureCenterButton() is called. Then, if the corner buttons exist, the optional array of corner buttons is unwrapped and passed to a new method, configureCornerButtons().

Finally, in the configureCornerButtons() method, we can then layout the style and content of the buttons while looping over the array of buttons.

Contrasting Xibless Approach with IB approach

If you were to restrict yourself to using interface builder for this layout, there are many limitations:

  1. you couldn’t as easily preserve two different styles of the view
  2. you wouldn’t be able to use loops, constants and enums to lay out the view
  3. you wouldn’t be able to adjust the content, size and layouts of the buttons all at once.

You could also implement this view by having the the corner buttons be an IBCollection and then laying out the buttons in code, but at that point the xib starts to become misleading: it no longer represents your actual layout, so why bother with it at all? Additionally, how do you represent the different styles in the xib? You could put all the controls in the xib and then show or hide them based upon the view’s style, but you would have to do that programmatically; again resulting in the runtime layout determined by a confusing mix of xib code and swift code.

All of these approaches lead to common frustrations of working with a complex layout in xibs: some aspects of the UI are in the xib while others are in code, making it difficult to know what the view will look like at runtime and difficult to know where something needs to be changed. Additionally, think about how much cleaner the xibLess solution is: based upon the view’s style, the buttons that are not needed will never be initialized or added to the view.

Try It!

If you’ve been a developer long enough, you’ve likely lost count of the number of times that a new technique appeared initially odd or uncomfortable but then became something so useful and intuitive that you later wondered how you ever survived without it. If you’re still skeptical, I encourage you to give the non-IB approach a try on your next project, or even just with a screen in your current project. It might seem a little strange at first, but once you learn that you can trust, refactor, reason about, and reuse your layout code in a way that you never could in IB, you will not want to go back.

The example code for this blog post is available as a project here. You can get Anchorage here, and if you’d like to learn more about Anchorage, here’s a blog post describing the library.

If you’re interested in learning more about swift development in general without using interface builder, check out the first in my series of ‘in practice’ posts: IB Free in Practice 1: Managing a Modal.

8 thoughts on “IB Free: Living Without Interface Builder and Loving It”

  1. Great stuff here. Two thoughts:

    1) A lot of articles about layout purely in code use pretty trivial examples, as is the case here. It would be great to see something more “real” – a full view controller, for example – with strategies for managing complex view hierarchies with lots of objects. Would you break it into subclasses for each view, even if only used once in the app? Write a *lot* of inline layout code?

    2) One thing I’ve found to be a drawback to the code-only route is the debugging time increases, since you need to write code, run the app, and navigate to the view in question to ensure that your layout is correct. This is made much more complex when it’s a view that needs to adapt to different size classes or screen sizes. IB has the “preview” mode which can be helpful for at least initially ensuring correctness in complex adaptive layouts. Any thoughts about streamlining this process when going XIBless?

    Keep up the good content!

    • Good points Nick:

      1) I agree, since this is more of a foundation post, it could be useful to follow up with some real world examples. My own experience in not using IB in the last 3 or so real projects that I’ve been on, we would typically reuse a number of views with layout patterns throughout the app. There are worst case scenarios (often b/c of deadlines) where I would find myself just coding a long line of layout code. But even then, I was able to do it faster and with more confidence than I would have in IB (plus I would rather have that layout code out in the open rather than lying in the XIB weeds).

      2) Once I’ve gotten used to it, at the very least I think the development time is about the same if not decreased with all the added benefits of human readable code. You’re right in that there is more time spent running a screen (and if you’ve got a good routing system this can be made more efficient) and testing it out, but nowdays I have more confidence in my layout in code then what IB would show me as truth. The screens would often need to be tested anyway for functionality, and I found that I could code both the layout and functionality of a screen before worrying about viewing the screen for testing purposes.

      In terms of laying out our screens, we’re immensely helped by having our designers use tools like zepplin.io, where we can look at a design and know all the of the measurements involved in it (as opposed to running a screen and then nudging stuff around until it looks right). It is still an immediate drawback, particularly with screen sizes. As Brian noted, all of us are starting to think about how we can make a screen more easily viewable.

      Thanks for the feedback!

    • You can check out http://letsbuildthatapp.com for real world examples of apps built without using IB

  2. Hey Nick, I thought I’d follow up as a XIBless fan.

    In my past project, I used a view subclass for anything beyond edge constraints. Many of them were 1-off views with no intent to re-use. With swift, the overhead of defining a new view is pretty small and it reads nicely. Especially with more complex layouts that have some additional associated state.

    It also had an interesting feature for your second point. Since I could add @IBDesignable to the view, it let me confirm layout changes and tweak constants using IB, even though the view wasn’t built with IB.

    This didn’t work as well as I was hoping and while it’s something I’m still playing with it’s not much of a development aide at this point.

    – Not being able to print or set a breakpoint made this largely un-suitable for initial development.
    – It can help confirm multiple form factors and tweaking constants with @IBInspectable. The benefit wasn’t always worth the cost, but for screens with substantial layout differences I could see it helping.
    – Crashes in your code are not bubbled up in any helpful way. If nothing happens, check out Console for a crash.
    – Make sure your view code is in a framework so IB doesn’t build your whole project.

    I am hoping that playgrounds can take on this role a bit better in the future, but for now – launch, navigate, check is the development cycle.

  3. @Brian you can set breakpoints – then, go into Editor → Debug Selected Views. You can even assign it a keyboard shortcut.

  4. Hi John,

    Thanks for the great article.
    I am so happy people are speaking out about ditching Interface builder and Xibs.
    It surely deserve more press in the dev space.

    I couldn’t agree more with the fact that doing things in code brings
    a better understanding of how the whole layout system works.
    And with a greater understanding, better code, less bugs , more joy!

    As you noted the main issues are :

    – Apple tells us it’s the way to go.
    – The native api is very verbose and quite scary for a newcomer
    – We have to build and run to see what we’re doing

    1 – That’s were you come in
    2 – Nothing prevents us form building better apis
    3 – Code injection! -> What you see is waht you (actually) get \o/

    This is what we’ve tried to fix with Stevia, a library that we developed
    internally at Yummypets for quite a long time, and then open sourced a few months ago.

    What’s cool is that there are more and more libraries that try to bridge that gap, and hopefully this will become a more standard practise 🙂

    Cheers

  5. Hello

    Great article and I must say that i’m tempted to try this out.

    I can see the benefits that you can easily reuse views, add constants to your view like in the example. Easily create views with height constraints, easyer to work with in teams (less merge conflicts).

    As stated before with complex views, don’t you have a lot of code in 1 place? I can image that if you have over 10 ui components and they all need a different style that you will get a huge viewcontroller. I’m really curious on how you handle this.

    Thanks
    Mathias

    • Sorry about the delay in response Mathias. You can unclutter your view controllers by building the UI components separately, as in most cases each of those components will have their own customizations. You can check out an example of this in my recent post:
      /dev/2017/03/ibfree-practice-1-managing-modal/

Leave a Comment