SnapKit for iOS: Constraints in a Snap

In this tutorial you’ll learn about SnapKit, a lightweight DSL (domain-specific language) to make Auto Layout and constraints a breeze to work with. By Shai Mishali.

4.7 (40) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Keeping a Reference

While you won’t experiment with this option in SnappyQuiz, it’s still one you should know of.

In standard NSLayoutConstraint fashion, you can store a reference to your constraint and modify it later on. That’s also possible with SnapKit, using the Constraint type:

var topConstraint: Constraint?

lblTimer.snp.makeConstraints { make in 
  // Store your constraint
  self.topConstraint = make.top.equalToSuperview().inset(16)
  make.leading.trailing.bottom.equalToSuperView()
}

// Which you can later modify
self.topConstraint?.update(inset: 32)

// Or entirely deactivate
self.topConstraint?.deactivate()

When Things Go Wrong

Sometimes in life, things go wrong. This is even more often the case when talking about Auto Layout constraints.

Back in QuizViewController+Constraints.swift, find the following line:

make.centerX.equalToSuperview()

Right below it, but still inside the makeConstraints closure, add:

make.centerY.equalToSuperview()

Build and run the app. As you can see, the UI is entirely broken:

App with broken constraints

Also, as expected, you’ll see a giant wall of broken constraints in your debug console, which should look similar to the following:

[LayoutConstraints] Unable to simultaneously satisfy constraints.

"<SnapKit.LayoutConstraint:0x600001b251a0@QuizViewController+Constraints.swift#62 UIView:0x7f9371e004a0.top == UILayoutGuide:0x60000062c0e0.top>",
    "<SnapKit.LayoutConstraint:0x600001b25260@QuizViewController+Constraints.swift#64 UIView:0x7f9371e004a0.height == 32.0>",
    "<SnapKit.LayoutConstraint:0x600001b2dc80@QuizViewController+Constraints.swift#38 UILabel:0x7f9371e088c0.height == 45.0>",
    "<SnapKit.LayoutConstraint:0x600001b2dce0@QuizViewController+Constraints.swift#39 UILabel:0x7f9371e088c0.top == UIView:0x7f9371e004a0.bottom + 32.0>",
    "<SnapKit.LayoutConstraint:0x600001b2dda0@QuizViewController+Constraints.swift#41 UILabel:0x7f9371e088c0.centerY == UIView:0x7f9371e09a50.centerY>",
    "<NSLayoutConstraint:0x600001c6c2d0 'UIView-Encapsulated-Layout-Height' UIView:0x7f9371e09a50.height == 551   (active)>",
    "<NSLayoutConstraint:0x600001c61450 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x60000062c0e0'UIViewSafeAreaLayoutGuide']   (active, names: '|':UIView:0x7f9371e09a50 )>"

Will attempt to recover by breaking constraint 
<SnapKit.LayoutConstraint:0x600001b2dc80@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>

Oh boy. Where do you even start? All you see is a bunch of memory addresses that don’t necessarily mean too much. It’s also quite difficult to understand which constraints were broken.

Luckily, SnapKit provides a great additional modifier to track down these sort of issues, called labeled(_:).

Replace the entire lblTimer constraint block with the following:

lblTimer.snp.makeConstraints { make in
  make.width.equalToSuperview().multipliedBy(0.45).labeled("timerWidth")
  make.height.equalTo(45).labeled("timerHeight")
  make.top.equalTo(viewProgress.snp.bottom).offset(32).labeled("timerTop")
  make.centerX.equalToSuperview().labeled("timerCenterX")
  make.centerY.equalToSuperview().labeled("timerCenterY")
}

Noticed the labeled(_:) addition on every constraint? This lets you attach a descriptive title for every constraint, so you don’t have to pick through memory addresses and lose your sanity.

Build and run your app one final time. Your broken constraints should provide much clearer information at this point:

[LayoutConstraints] Unable to simultaneously satisfy constraints.

"<SnapKit.LayoutConstraint:0x60000365c4e0@QuizViewController+Constraints.swift#62 UIView:0x7fc53e4181d0.top == UILayoutGuide:0x600002b0ae60.top>",
"<SnapKit.LayoutConstraint:0x60000365e8e0@QuizViewController+Constraints.swift#64 UIView:0x7fc53e4181d0.height == 32.0>",
"<SnapKit.LayoutConstraint:timerCenterY@QuizViewController+Constraints.swift#41 UILabel:0x7fc53e41d060.centerY == UIView:0x7fc4fe507170.centerY>",
"<SnapKit.LayoutConstraint:timerHeight@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>",
"<SnapKit.LayoutConstraint:timerTop@QuizViewController+Constraints.swift#39 UILabel:0x7fc53e41d060.top == UIView:0x7fc53e4181d0.bottom + 32.0>",
"<NSLayoutConstraint:0x6000031346e0 'UIView-Encapsulated-Layout-Height' UIView:0x7fc4fe507170.height == 551   (active)>",
"<NSLayoutConstraint:0x600003139c70 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x600002b0ae60'UIViewSafeAreaLayoutGuide']   (active, names: '|':UIView:0x7fc4fe507170 )>"

Will attempt to recover by breaking constraint 
<SnapKit.LayoutConstraint:timerHeight@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>

This looks similar, but look carefully. You can see gems like timerCenterY. This is much more informative, and you have some great labeled constraints to start debugging your way through.

More specifically, the only three labels you can recognize in this output are timerCenterY, timerHeight and timerTop. Since the height is static, you can be sure the conflict is between the two constraints left. That narrowed things down much faster than picking through the original mess of Auto Layout debugging output!

Once you’re done, feel free to remove the centerY constraint that started this mess.

Where to Go From Here?

Congratulations! You now know most of what SnapKit has to offer, but there are still a few features and modifiers you should look into, such as priority, divided and more. Check out SnapKit’s official GitHub repo for more information.

Remember, SnapKit is there to help you by creating an easy-to-consume, problem-specific syntax for creating constraints, but it doesn’t provide features that can’t be achieved with regular NSLayoutConstraints. Feel free to experiment with both and find a good middle ground that works for each scenario.

We hope you’ve enjoyed this tutorial. Got any more questions or … constraints ? Leave a comment in the forum thread below.