---
title: "Easier NSLayoutConstraint interactions"
slug: easier-nslayoutconstraint-interactions
section: tech
date: 2017-11-15T18:59:00.000Z
canonical: https://roland.leth.ro/blog/tech/easier-nslayoutconstraint-interactions
---

In Swift 4 `UILayoutPriority` has become a struct, with an initializer and a `rawValue`, instead of being a `rawValue` of `Float` itself. This means that simple assignments became _slightly_ harder:

```swift
let constraint = someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
// Swift < 4
constraint.priority = 999
// Swift 4
constraint.priority = UILayoutPriority(rawValue: 999)
```

Besides this, I've always had a pet peeve – activating constraints that require priority manipulation:

```swift
NSLayoutConstraint.activate([
  someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor),
  someView.trailingAnchor.constraint(equalTo: otherView.trailingAnchor)
])
```

Setting a priority for the `leadingAnchor` requires a couple of extra steps:

```swift
let leadingConstraint = someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
leadingConstraint.priority = UILayoutPriority(rawValue: 999)

NSLayoutConstraint.activate([
  leading,
  someView.trailingAnchor.constraint(equalTo: otherView.trailingAnchor)
])
```

What if we could solve these two problems almost in one go? At first I thought of creating an `enum` for this, but I decided to follow Apple's approach and created a `struct`, so let's do that:

```swift
struct LayoutPriority {
	
  var rawValue: Float
  var toUIKit: UILayoutPriority {
    return UILayoutPriority(rawValue: rawValue)
  }
	
  static let minNonZero = LayoutPriority(rawValue: 1) // 1
  static let belowDefaultLow = LayoutPriority(rawValue: UILayoutPriority.defaultLow.rawValue - 1) // 2
  static let defaultLow = LayoutPriority(rawValue: UILayoutPriority.defaultLow.rawValue)
  static let aboveDefaultLow = LayoutPriority(rawValue: UILayoutPriority.defaultLow.rawValue + 1) // 3
  static let belowDefaultHigh = LayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue - 1) // 4
  static let defaultHigh = LayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue)
  static let aboveDefaultHigh = LayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue + 1) // 5
  static let maxNonRequired = LayoutPriority(rawValue: UILayoutPriority.required.rawValue - 1) // 6
  static let required = LayoutPriority(rawValue: UILayoutPriority.required.rawValue) // 7
	
	
  // MARK: - Init
	
  init(rawValue: Float) {
    self.rawValue = rawValue
  }
	
}
```

It contains all the values from the `UIKit` version, and a few convenience ones (1-7), for the most used custom scenarios. For example, if you have two views with equal `compressionResistance` (`rawValue = 750`) and the Auto Layout engine can't solve the constraints, it might require you to change one of the views' `compressionResistance` to `749` or `751` to break the tie; now we have `belowDefaultHigh` and `belowDefaultLow` for that.

This brings us one step closer to our final goal – less typing, more autocomplete:

```swift
let constraint = someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
constraint.priority = LayoutPriority.maxNonRequired.toUIKit
```

Second step would be to create an `NSLayoutConstraint` extension:

```swift
extension NSLayoutConstraint {
  
  // This is probably not the best name, since it mutates `self`, but I thought it's better than add/set/change.
  @discardableResult
  func with(priority: LayoutPriority) -> NSLayoutConstraint {
    self.priority = UILayoutPriority(rawValue: priority.rawValue)
    return self
  }
  
}
```

And we reached our final goal, of setting priorities directly inline:

```swift
NSLayoutConstraint.activate([
  someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
    .with(priority: .maxNonRequired),
  someView.trailingAnchor.constraint(equalTo: otherView.trailingAnchor),
    .with(priority: LayoutPriority(rawValue: 123))
])
```

We can go an extra mile and add a property to the `NSLayoutConstraint` extension, that can bridge between `LayoutProperty` and `UILayoutProperty`:

```swift
extension NSLayoutConstraint {
  
  var layoutPriority: LayoutPriority {
    get { return LayoutPriority(rawValue: priority.rawValue) }
    set { priority = UILayoutPriority(rawValue: newValue.rawValue) }
  }
  
}
```

Although this won't bring _that_ much value, since we have the previous helper function, it does bring _some_, for example when changing between two layouts, based on a condition:

```swift
if layout1Condition {
  someConstraint.layoutPriority = .minNonZero
  someOtherConstraint.layoutPriority = .maxNonRequired
}
else {
  someConstraint.layoutPriority = .maxNonRequired
  someOtherConstraint.layoutPriority = .minNonZero
}
```

Lastly, we can take this one step even further, by adding operators on our `LayoutPriority`:

```swift
extension LayoutPriority {
	
  static func -(lhs: LayoutPriority, rhs: Float) -> LayoutPriority {
    return LayoutPriority(rawValue: lhs.rawValue - rhs)
  }
  
  static func -=(lhs: inout LayoutPriority, rhs: Float) {
    lhs = LayoutPriority(rawValue: lhs.rawValue - rhs)
  }
  
  // ...
  
  }
```

Now we can more easily handle constraints dependent on other:

```swift
let constraint = someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
constraint.layoutPriority = .belowDefaultHigh

let otherConstraint = otherView.leadingAnchor.constraint(equalTo: otherView.otherAnchor)
otherConstraint.layoutPriority = constraint.layoutPriority - 1
```

And why not have two more additions to our `NSLayoutConstraint` extension?

```swift
extension NSLayoutConstraint {
  
  @discardableResult
  func activate() -> NSLayoutConstraint {
    isActive = true
    return self
  }
  
  @discardableResult	
  func deactivate() -> NSLayoutConstraint {
    isActive = false
    return self
  }
  
}
```

These won't bring much value, either, unless you like to chain things, like me:

```swift
let constraint = someView.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
constraint.priority = UILayoutPriority(rawValue: UILayoutPriority.defaultHight.rawValue - 1)
constraint.isActive = true

// vs

someView.leadingAnchor
  .constraint(equalTo: otherView.leadingAnchor)
  .with(priority: .defaultHigh - 1)
  .activate()
```