Swift Tips: Property Configuration

Blake Merryman
Aug 22, 2017 · 3 min read

If you’ve been in iOS development for any amount of time, you’ve come across large init and viewDidLoad() methods that look something like this:

class MyViewController: UIViewController {  var collectionView: UICollectionView?  override func viewDidLoad() {
super.viewDidLoad()

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal

collectionView = UICollectionView(frame: .zero,
collectionViewLayout: layout)
collectionView?
.translatesAutoresizingMaskIntoConstraints = false
collectionView?.isPagingEnabled = true
collectionView?.backgroundColor = .clear
collectionView?.showsHorizontalScrollIndicator = false
view.addSubview(collectionView!)
// Auto Layout Code …
}
}

We can do better than that thanks to Swift’s ability to configure properties inline with closures.

class MyViewController: UIViewController {let collectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
let collectionView = UICollectionView(
frame: .zero,
collectionViewLayout: flowLayout
)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isPagingEnabled = true
collectionView.backgroundColor = .clear
collectionView.showsHorizontalScrollIndicator = false
return collectionView
}()
// MARK: - Lifecycleoverride func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// Auto Layout Code ...
}
}

Now viewDidLoad() only contains code directly relevant to loading the view, all property configuration is centralized, and its scope is greatly reduced. We also have guarantees about the existence of our collection view property.

We can do even better though. Perhaps we want to reuse this same collection view configuration elsewhere in our app. We can extend UICollectionView with a convenience initializer and refactor our configuration code completely out of our view controller.

extension UICollectionView {  convenience init(
flowLayoutScrollDirection: UICollectionViewScrollDirection) {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = flowLayoutScrollDirection
self.init(frame: .zero, collectionViewLayout: flowLayout)
translatesAutoresizingMaskIntoConstraints = false
isPagingEnabled = true
backgroundColor = .clear
showsHorizontalScrollIndicator = false
}
}class MyViewController: UIViewController { let collectionView = UICollectionView(flowLayoutScrollDirection: .horizontal) // MARK: - Lifecycle override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// Auto Layout Code ...
}
}

This is pretty clean now, but we still have room for improvement. All instance properties declared as constants (i.e., let) are initialized at object creation, so our collection view is initialized during the view controller’s initialization. If you have a slew of let properties that are expensive to configure (like lots of custom UI or data source objects), this can cause a major spike in your memory footprint. Luckily we have an easy solution for this: lazy loading.

Lazy loading is a strategy for loading things only when they are first accessed. In our example, we only need to load the collection view when we’re attempting to add it to the view hierarchy. It’s extremely simple to implement.

class MyViewController: UIViewController {  lazy var collectionView = UICollectionView(flowLayoutScrollDirection: .horizontal)// MARK: - Lifecycleoverride func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// Auto Layout Code ...
}
}

This is much better! You may notice that our collection view is no longer a constant. As of writing this article, there is no way to create a lazy loaded constant instance property. However, this simple work-around should help limit the scope of this property’s setter:

private(set) lazy var collectionView = UICollectionView(…)

Now our collection view property can only be set within the scope of the view controller’s core implementation (i.e., class MyViewController: UIViewController { … }). If you’re organizing your code using extensions, the portions of your view controller that have access to the setter will be greatly limited. This isn’t a perfect solution, but it’s workable. Constants declared statically or at global scope are automatically lazy loaded.

Configuring properties in this way will help keep your code flexible and easy to read, while optimizing performance.

In our next article, we’ll explore how to incorporate lazy sequences in Swift.


For more insights on design and development, subscribe to BPXL Craft and follow Black Pixel on Twitter.

Black Pixel is a creative digital products agency. Learn more at blackpixel.com.

BPXL Craft

Design and technology articles from the Black Pixel team. blackpixel.com

Blake Merryman

Written by

 Software Engineer

BPXL Craft

Design and technology articles from the Black Pixel team. blackpixel.com

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade