Fetching articles...

Removing storyboards and xib-files to save time and money

Not only does it cut build times by an insane amount (in one of my apps I saved minutes), it also makes my work a lot easier. Let me walk you through how this is possible and why I think its a great strategy.

First, I must mention that this is not for everybody. If you are working with people that can't code, don't want to learn or are afraid of reading code, this is just not an option. Such teams simply must have their nib/xib files ready for a graphics designer to try out new things, replace images, etc. To those I would like to send a quick advice: start learning. Layout code is usually the easiest to read and understand, and if you understand code - you will become a better designer. It is also fun and rewarding.

Open Source

It feels great to finally be able to open source something I've done. If you are interested in these ideas or if you rather see than read all this text, just download and run the example app, and look at the viewController's code. I promise: things will get immediately apparent. You find the project here: 

https://github.com/OlofT/AutoStandards

The problems

Doing layout with storyboards or xibs is tedious (will call them both nib-files from here on). There are a lot of manual work involved and sometimes there are things just not possible with nib-files. Especially storyboards since they are lacking features that xibs can do, but sometimes the other way around (note that this also changes for every release).

  1. Every element you add has the same standard settings and colors that might not be suitable for your app. To change these standards you need to click through every single element manually and perform the change.
  2. We have the manual tasks of hooking elements to code, assigning an IBOutlet and setting buttons didPress actions, etc. This must also be done for every single element. If I have these items in my nib files, why aren't those created automatically in my code?
  3. Changing the look of your app per user demand, e.g. having a night mode (where everything is dark and text is light) will still require a lot of code. This effectively doubles the amount of needed work. First you design everything in nibs, then you do the same design in code (usually twice since you want to switch back to day-mode). If something is wrong you have two places to debug (or three).
  4. Version control becomes a special type of hell. Think on this for a moment - when it is most useful to have nibs (large teams), then it is also the most problematic.
  5. Compile times starts to become a burdon for large apps. Note that nib-files are compiled code just like any other code you could write yourself, but turning the XML into code just takes so much more time.
  6. Creating apps takes much more of your time.
  7. Code reuse for nibs are more or less impossible (at least impractical). Even copying stuff from a nib usually goes wrong or isn't worth the trouble.
  8. Nibs are not automatic.

The AutoStandards solution

The simple solution to all of these is just to do layout in code. Remember that nib-files are a graphical representation of code, there is nothing you can do with nibs that you can't do in code. However, the reverse is not true. There are a lot you can do in code, that can't be done in nibs.

  1. Have a standard and create all elements with that standard. My version is a class I call "AutoStandards", for automatically applying the app's standard look to all views. Every ViewController calls the same creation methods, so when you need to change the setup you have one place to go to, and the changes are applied to the whole app.
  2. Since the standard is in code, just let it look into your viewController and see what properties exists. Then it will create all those elements for you, setup the way you like them to be (as defined by your standard).
  3. If the viewController has a button and responds to the "buttonDidPress:" method - AutoStandards just assigns it automatically. Even better: if your button variable is called "createAccountButton" and the controller responds to "createAccountButtonPressed" - that method gets called instead. All your setup work will be reduced to one line of code. It feels purely magical.
  4. AutoStandards creates all elements, so it can also set all colors and fonts. If you name those colors e.g. background, textColor, etc, when changing them - AutoStandards can just as easily replace all colors with each corresponding name. No more hassle with AppearanceProxy (which isn't good enough for its job anyway), just set new colors and let the work be done automatically for you. In addition, you can download your colors on demand, so designers can experiment with the look of your app without recompiling.
  5. Not only is it easy to keep track of what color has been set where (since everything is in code), doing so gives you "dark mode" for free. If you want special colors for Christmas or per user, just add another dictionary with colors.
  6. Version control conflicts is never fun, but when there is only code, it is as easy as it gets.

A quick note on why I disapprove of AppearanceProxy: It isn't granular enough. All views cannot have the same backgrounds just based on their classes (or what classes they are within), a view displaying a warning might have a yellow background, but the rest might be white. A button saying "ok" might be green but a "no" button might be red. You just can't have such rules applying to all members of all objects based on classes. In addition, AppearanceProxy cannot change views already created. You still need your own code that modify the appearance of each individual object that is visible on screen.

"Auto" layout

There are a couple of really great things about the AutoLayout framework, especially one comes to mind: that it can automatically reverse elements when going from a left-to-right language (setting a "Done"-button to the left of the text input instead of to the right). There is no need to avoid AutoLayout just because you are doing layout in code, in fact I would argue that it is easier in code. Especially if you have help, take a look at Stevia or Cartography for example.

I don't use AutoLayout myself since it adds complexity, introduce bugs and sometimes slows down the app (especially if the layout is complex - which is when you need it the most). You must understand that AutoLayout is a solver of linear programming problems, which always has an optimal solution (if your constraints are correctly set), but that doesn't make it a trivial problem. I have seen it get stuck for several seconds in certain cases - which just isn't acceptable when loading a view. Layout in code is on the other hand more or less instant, just a couple of additions and divisions - even an old phone could do a million of these without breaking a sweat. None of my apps are translated to arabic (yet), and if they are, adding code for right-to-left languages is an afternoons work. Layout code is really not that hard. If you know the four simple rules of arithmetic, you are 90% done. The difficulty is to keep the code clean and tidy, since it quickly becomes quite verbose.

Downsides

You could argue that the actual design is what takes time, and doing that is easier with nibs, and I agree. But how much easier is it than using a piece of paper or photoshop? Also - have you ever tried to modify a nib with AutoLayout? It is faster to just scrap it all and start over from scratch. As I said, this is not for everybody, but for me at least (and I truly believe that I'm not alone), paper + code is faster and easier. It takes more or less the same amount of time when doing the initial design, but when changing something it is a million times faster.

Layout code does get noisy fairly quickly and there are a lot of boilerplate. Get the view's frame, change some values, set it back again. Take the next view's frame and repeat for all view controller's views.

To mitigate this and make layout code easier, AutoStandards can set all frames for all views itself, by keeping references for certain frames and their corresponding views. This reduces a lot of boiler plate code and focuses the layout code to just be simple rectangle-operations. The references are of-course automatic, there is no setup needed.

Here is a simple example from one of my apps, a chat-application. This is an early version when there just was a textview to type in and a tableView for all messages:

@interface ADMessageController ()
    {
        CGRect messageViewRect, inputRect, sendButtonRect, keyboardFrame;
    }

    @property (nonatomic) UITableView *messageView;
    @property (nonatomic) UITextView *input;
    @property (nonatomic) UIButton *sendButton;

    @end

    @implementation ADMessageController

    - (void)loadView
    {
        [super loadView];

        //make self.view have the main background color - which is set independently
        [AutoStandards color:AutoColorMainViewBackground forView:self.view method:AutoSetValueBackgroundColor];

        [AutoStandards createViews:self superView:self.view];   //auto-create all other views and add them to self.view
    }

    //You really shouldn't do this in viewWillLayoutSubviews (will make things harder when animating etc) - this works but is just an example.
    - (void) viewWillLayoutSubviews
    {
        //Now we just calculate our views with ordinary simple math and variables.
        CGFloat margin = 5;
        CGFloat touchSize = 40;

        CGRect remainder = self.view.frame;

        //subtract keyboard from the visible view
        CGRect visibleKeyboard = CGRectIntersection(keyboardFrame, remainder);
        remainder.size.height -= visibleKeyboard.size.height;

        //let the view with all messages be on top
        CGRectDivide(remainder, &inputRect, &messageViewRect, touchSize, CGRectMaxYEdge);
        AutoSliceRect(&inputRect, margin, CGRectMinXEdge);

        //then we have the text-input and a send button
        CGRectDivide(inputRect, &sendButtonRect, &inputRect, touchSize + 5 * margin, CGRectMaxXEdge);

        //shrink the send button and the input - people like nice margins
        sendButtonRect = CGRectInset(sendButtonRect, margin, margin);
        inputRect = CGRectInset(inputRect, 0, margin);

        //Then we set all rects at the same time with this simple function
        [AutoStandards setFrames:self];
    }

    - (void) sendButtonPressed:(id)button
    {
        //send stuff!
    }

    @end

This is all of the actual code needed. As you can see, nothing needs to be hooked up or set in different ways.

Swift and manual-labour-layout

The automatic part of the AutoStandards is made possible by the divine Objective-C runtime, which also Swift lives by (on iOS/mac). That means all of this could be written as well with Swift as with Objective-C (just so much easier in Objective-C). There might be a dark future where the current runtime is replaced by something not as dynamic, or that Swift will not be as dynamic (and you can't use Objective-C anymore), and then you will need to revert back to manual-labour-layout. That is at least 15 years in the future and for me at least, I will never go back to manual-labour-layout. Once you've seen how great things can be, it is just no putting the genie back in his bottle. However, I don't think it will come to that. There is talk about making Swift more dynamic, and since it supports a fair amount of dynamic features now, it would be strange to take them away. Note that AutoStandards only introspects your classes - there is no swissling, adding iVars or similar "dangerous" dynamism. 

The reason Apple might close down the runtime is to prevent usage of private APIs and other circumventions of frameworks that programmers usually resort to in order to fix bugs Apple themselves are not able to fix (or care enough to fix). Usually you don't go there unless you really have to, but there are forces at work that still feel the need to prevent dynamism. Simply put: it doesn't matter if we have an all-swift future or not, its the runtime that makes this possible and currently there is a battle in heaven between good and evil - I can just hope the good side win (but it looks quite dark at the moment).

This lands me in the strange situation of having Swift as the future for the platform, but holding off until we get dynamism - which might not happen. 

Automatic Coding

I believe in an automatic future, where every labour-like task is automated. Things like regular work, cooking, cleaning, commute, etc. People have been looking into how to get baristas and taxis to be automated, but for me, the more interesting tasks are how to automate politicians, lawyers, judges, designers, architects and programmers. So I naturally wanted to start with automating programming, since it would seem most natural and best fit to automate (and it's the line of work I know the most of). When I was working as a contractor and doing the same view controller for the seventh hundred time (with nibs), made my soul hurt. Then just imagine that there are a million other people like me, doing the same thing all over the world. All these mindless repetitive tasks needs to go away, and the solution is to automate them. Just imagine what we all could achieve with all that time!