Storyboards/Nibs vs. Doing Everything With Code in iOS

When you start working on a new app, one major decision that needs to be made is whether you want to do all of the interface work graphically (through xibs/storyboards) or programatically. Each method has pros and cons. Hopefully this post will help you make the best choice for your project!

Why Storyboards are Actually Really Cool…

With my experience, I have found that the best time to use interface tools to build your app is when you are quickly prototyping something by yourself. If you are at a hackathon, or playing around with a new app idea, doing all the interface work in storyboards is actually quite ideal. You can very quickly iterate and pump out a half decent functioning app in very little time. Additionally, if you are just beginning iOS, it is incredibly helpful to have the ability to visualize how code is linked up to a functioning interface. Even though I usually don’t use interface builder in projects now, it greatly helped my understanding of iOS development by allowing me to visually link snippets of code to the graphics I would see on the screen. This is really cool, and makes concepts that can be hard to comprehend very intuitive. 

Why Storyboards Aren’t Always that Great…

So, even though Interface Builder and Storyboards are a really cool technology and are fantastic when learning and prototyping things on iOS, as you begin dealing with source control and collaborating with other developers, you will rapidly run into issues. For example, lets say I am on a team of developers, and by accident two developers fix the same bug or add or overwrite something on the same view using storyboards. This will cause a merge conflict (assuming you are doing source control with git). Now, since the actual interface “code” generated by Interface Builder is non-readable xml, it can be nearly impossibly to fix even the smallest merge conflict in a massive storyboard file. This could lead to massive amounts of work being lost or corrupted. This is a very bad thing. 

Next, since storyboards are essentially a level of abstraction on Cocoa, it can be much harder to do customization of UI. For example, if I wanted to recreate the now popular interface flow of sliding the main view controller to the right to access the side menu view controller, I would have to get a reference to the two view controllers and navigation controller stack.  If I built the app doing everything programmatically, this would be fairly straightforward and also be pretty clean code. With Storyboards however, just to get a reference to a single view controller I would have to do something like this:

MyViewController *controller = (MyViewController*)[mainStoryboard
instantiateViewControllerWithIdentifier: @”<Controller ID>”];

This is kind of gross and unnecessarily long code. Also, with this additional abstraction, it could be much harder to track down small bugs and other flaws. 

Finally, if you are working on a very large project over the course of a few months, if you have everything involving the interface in one place organized by the name of the view it is way easier to track down what happens where, and know where to add or remove code. If you have parts of interface “code” in a storyboard and part of what is driving the interface living in a .m file, it can become very confusing as to what actually controls what. 

 

 

 

Advertisements

JSButton: A simple way to pass blocks to buttons and trigger them with a UIControlEvent

My friend Josh Sklar (a senior at Michigan, and soon to be full time iOS dev at Detroit Labs) wrote a very cool subclass of UIButton that allows you to pass a block to a button and have it executed with a given UIControlEvent.

For example, instead of creating a ton of selectors, you can now simply do:

JSButton *buttonOne = [[JSButton alloc]initWithFrame:CGRectMake((self.view.frame.size.width - kButtonWidth)/2, kLabelHeight, kButtonWidth, kButtonHeight)];
    [buttonOne setTitle:@"Buton One" forState:UIControlStateNormal];

    [buttonOne performBlock:^(id sender) {
        JSButton *btn = (JSButton*)sender;
        NSLog(@"Some trivial code for touching up inside on %@", btn.titleLabel.text);
    } forEvents:UIControlEventTouchUpInside];

    [self.view addSubview:buttonOne];

Here is a link to the repo: https://bitbucket.org/jrmsklar/jsbutton

Large Social Networks are Doing Mobile Wrong.

Yesterday I had the chance to talk to a Twitter iOS engineer. Although this was part of my interview process, there was some time at the end of the call to discuss whatever I wished to. So, I went ahead and asked why they trashed a pretty fantastic iPad optimized version of Twitter to resort to a strange, stretched out iPhone version of Twitter for all iOS devices.

He responded with this:

  1. It was a struggle to maintain two separate implementations of the same app. Obviously Twitter wants to be able to offer the same great features in all of its apps, and it was very hard to consistently ship  the same features for the two different apps.
  2. New users who are less tech savvy were complaining about how confusing different apps were for Twitter.
  3. We want the ability to define what Twitter is more. Before we sort of let people use it for whatever they wanted, and now as new users join who are less familiar with tech, they want to know what they are signing up for .

This sounds very, very similar to the facebook mobile engineers who defended the use of html5 webviews in the iOS app until recently.

This way of thinking is just wrong for mobile.

The iPad and iPhone are different devices. The same applies for android tablets and phones. Tablets are not phones. With the extra screen space, there is an amazing amount of creative potential for developers+designers+product people.

Although it is easier for engineers to not worry about two separate code repos for the different apps, the end product takes a MASSIVE hit. The entire reason the iPad exists is to allow for a great way to interact with data with the large multitouch screen. If you really think that taking an app interface designed for a screen a 3rd or 4th of the size and just stretching it to fit is the best way for you to allow your consumers to interact with your service, then you are flat out wrong.

Yes, I realize this is not as efficient or logical as shipping a moderately nice app for all devices, but larger social networks need to realize that sometimes sacrificing business logic will produce a much better experience for end users and perhaps also result in happier engineers who are allowed to have more creative freedom in their products/products.

 

Basic Model-View-Controller flow for iOS

So, one important thing to learn in iOS as well as in any language are some core concepts of software design. This would be the model view controller pattern.

In terms of an iOS project, a good way to think of how this could be implemented would be in a calculator application. The model of this project would store the methods that process the number, i.e. a method to add, a method to substract, a method that divides, and a method that multiples, etc.

For the sake of this application, lets assume each of the methods has an input of two doubles and returns a double as well. Essentially, our model will contain all the methods that do any action to the numbers in the calculator.

In our view controller, we will then make an instance of our model class. This will allow us to use all the instance methods we created in our model class in view controller class. Essentially, we want to pass the values from our view into the methods stored in our model.
Here is the header file:

 

//
//  CalculatorModel.h
//  ModelCalculator
//
//  Created by Andrew Rauh on 5/12/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface CalculatorModel : NSObject

- (double) addNumbers: (double) numberToAdd1 :(double) numberToAdd2;

- (double) subNumbers: (double) numberToSubtract1 :(double) numberToSubtract2;

@end

And here is the implementation file. (.m)


//
//  CalculatorModel.m
//  ModelCalculator
//
//  Created by Andrew Rauh on 5/12/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "CalculatorModel.h"

@implementation CalculatorModel

- (double) addNumbers: (double) numberToAdd1 :(double) numberToAdd2
{
    return (numberToAdd1+numberToAdd2);
}

- (double) subNumbers: (double) numberToSubtract1 :(double) numberToSubtract2
{
    return numberToSubtract1-numberToSubtract2;
}

@end

Now, we need to make an instance of this model class in our main view controller. So, do this:




//
//  ViewController.h
//  ModelCalculator
//
//  Created by Andrew Rauh on 5/12/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "CalculatorModel.h"

@interface ViewController : UIViewController {
    
    UIButton *button1;
    CalculatorModel *calculatorModel;
    
}


@property (nonatomic, strong) UIButton *button1;
@property (nonatomic, retain) CalculatorModel *calculatorModel;


@end


And finally, we will actually use and access these methods in our implementation file.

//
//  ViewController.m
//  ModelCalculator
//
//  Created by Andrew Rauh on 5/12/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize button1, calculatorModel;



- (void)viewDidLoad
{
    [super viewDidLoad];
    calculatorModel = [[CalculatorModel alloc] init];
    
    
    
    //Then you could pass in values from your view into the model methods. 
    //in this case I am just passing two arbitrary values into the method to test it. 
    [calculatorModel addNumbers:3.4455 :5.3];
    NSLog(@"%f",[calculatorModel addNumbers:3.4455 :5.3] );
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

@end

I realize this is exceptionally simple, but hopefully it showed the basics of this development pattern for iOS!