How to write storyboard-free views in Swift with SnapKit

Kenza Iraki
4 min readOct 18, 2016

--

In my last blog post, I listed some of the reasons why I stopped using storyboards. If I’ve managed to convince you that there is a better way, you might be wondering: what now? After that post, I received some questions asking me how to go about laying out views without the Interface Builder. Let’s be honest, Apple certainly doesn’t make it straightforward to get rid of it, and I’ve found very few tutorials on layout constraints, let alone any using SnapKit. I figured, I might as well write a little guide myself! So here we go:

Step 1: Create a new project (I assume you know how to do that, if not you might want to check this tutorial)

Step 2: Go to your storyboard file on the project navigator, and delete it: I know this might seem a little scary, but trust me, there’s no feeling like pressing on that delete button

Step 3: Set up your app delegate:

  • Update the window property inside the App Delegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
}
  • Initialize the window and give it a root view controller in your didFinishLaunchingWithOptions method in your AppDelegate.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window?.makeKeyAndVisible()
window?.rootViewController = MainViewController()
}

Your root view controller should be your app’s first view controller. If you want to embed your app in a navigation controller, you should write the following instead:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController(rootViewController: MainViewController()
}

That’s it for the basic setup! Now on to SnapKit

Step 4: add SnapKit to your dependencies—I will be using Cocoapods here (if you don’t know what that is, check this out)

  • Add it to your Podfile
pod 'SnapKit'

(make sure you get the appropriate version of the library for your version of Swift on SnapKit’s Github)

  • Run pod install in your terminal

Step 5: Create a view and a view controller

There’s unfortunately a little bit of boilerplate code associated with writing views in code, but there are very simple ways to get rid of that.

  • Create a UIView with some properties and an initializer
import SnapKitclass MainView: UIView {
private(set) var label = UILabel()
private(set) var textField = UITextField()
private(set) var button = UIButton()
init() {
self.frame = CGRect.zero
}
}

I personally like to keep things compartmentalized and organized, this is why I usually have 2 different methods taking care of the UI in the view.

First initializeUI() : this is where all my view layout is set, background colors, text fonts (although I like to keep those separate in factories and a Stylesheet), subviews etc.). When using the storyboard to drag and drop UI objects, IB automatically adds these as subviews of either your whole view or the view you were hovering above. When writing code layout, you need to add all of your UI objects as subviews, otherwise your constraints will break and you will get an exception.

private func initializeUI() {
addSubview(label)
addSubview(button)
addSubview(textField)
// This can be shortened to simply addSubviews([label, button, textField]) with the help of an extension available in this library)
label.text = "Type something"
textField.placeholder = "Anything"
button.setTitle("Submit", for: .normal)
}

And of course, the next part is where all the SnapKit magic happens, the createConstraints() method:

private func createConstraints() {
label.snp.makeConstraints { make in
// This code centers the label and sets it 10 points from the top of the screen (remember, the superview is the one to which we added the label as a subview, so here, the MainView)
make.centerX.equalToSuperview()
make.top.equalToSuperview().inset(10)
}
textField.snp.makeConstraints { make in
// This sets the text field 10 points from the bottom of the label, and at the center of the screen
make.top.equalTo(label.snp.bottom).offset(10)
make.centerX.equalToSuperview()
}
button.snp.makeConstraints { make in
// Similarly, this sets the button 30 points from the text field and at the center of the screen
make.top.equalTo(textField.snp.bottom).offset(30)
make.centerX.equalToSuperview()
}
}

And last but not least, you need to call these methods from the initializer of your UIView. Make sure you call initializeUI before createConstraints, as your objects need to already have a superview before laying out any constraints.

override init() {
self.frame = CGRect.zero
initializeUI()
createConstraints()
}

And that’s it! All you have to do write a few (very readable) statements about where you want your subviews to be located on the screen and to each other, and you’ve got your storyboard-free view!

  • Setup your view controller: you need to set your custom MainView as the view controller’s view
class MainViewController: UIViewController {// 1: create a computed property that returns the view controller's view as your custom view (give it a name such as contentView, customView...).    var contentView: MainView {
return view as! MainView
}
// 2: initialize your view by overriding loadView() override loadView() {
view = MainView()
}
// 3: add whatever logic you need to exert on the view, but don't forget to reference contentView from now on instead of view override func viewDidLoad() {
super.viewDidLoad()
contentView.button.addTarget(buttonWasTapped())// If you're not a fan of setting button actions like this, check out EZSwiftExtension's BlockButton or some reactive/binding frameworks if you're feeling up to the challenge
}
func buttonWasTapped() {
contentView.label.text = textField.text
contentView.textfield.text = ""
}
}

And you’re all good to go! If that seems a little too cumbersome for you, don’t forget to check out the link I referenced above on Generic Views and Generic View Controllers, that will cut down all that boilerplate code for you.

The first time might feel a little overwhelming, but SnapKit’s syntax and the easy ways to keep all this code clean, short and sweet make it worth it! In no time, you’ll forget why you ever even used storyboards! :)

--

--

Kenza Iraki
Kenza Iraki

Written by Kenza Iraki

Engineering Team Lead @ Breathe Life. Creator of Let’s React meetups https://twitter.com/kenza_iraki

Responses (6)