A collection of extensions and methods to make Swift and iOS even better
Inspired by Soroush Khanlou's talk, You Deserve Nice Things, at #Pragma Conference 2017.
To load a view from a nib, don't use this:
let view = Bundle.main.loadNibNamed("YourView", owner: self, options: nil)?.first as? YourView
Instead, use this:
let view = YourView().loadNib()
Just grab UIView+loadNib.swift to make loading nibs super easy.
Thanks to Matt Lorentz for contributing this!
To dequeue a table view cell, don't use this:
dequeueReusableCell(withIdentifier identifier: String)
Instead, use this:
dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath)
Why? Look closely at the documentation for each, and check out the Return Value. On the second, it says:
A UITableViewCell object with the associated reuse identifier. This method always returns a valid cell.
This is nicer than checking for nil and instantiating a new cell.
Because sometimes you want to time your build without xcodebuild
. Just run this command in your Terminal:
defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
The Result monad is a better way to handle operations that return one type on success and another on failure. It replaces code like this:
let completion = { (model: Model?, error: Error?) in
switch (model, error) {
case (.some(_), .none):
print("Success! We got our object.")
case (.none, .some(_)):
print(error)
case (.none, .none):
print("This shouldn't happen.")
case (.some, .some):
print("This shouldn't happen either.")
}
}
Into this:
let completion = { (result: Result<Model, Error>) in
switch (result) {
case .success(let model):
print("Success! We got our object back")
case .failure(let error):
print("The function failed, but in style!")
}
}
XCTBlockExpectation
turns this test case:
func test_XCTNSPredicateExpectation_should_be_replaced_with_XCTBlockExpectation() {
var moveAlong = false
let predicate = NSPredicate(block: { _,_ in moveAlong == true })
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: nil)
DispatchQueue.global().asyncAfter(deadline: .now() + 0.05) {
moveAlong = true
}
wait(for: [expectation], timeout: 2)
}
Into this:
func test_XCTBlockExpectation() {
var moveAlong = false
let expectation = XCTBlockExpectation { moveAlong == true }
DispatchQueue.global().asyncAfter(deadline: .now() + 0.05) {
moveAlong = true
}
wait(for: [expectation], timeout: 2)
}
XCTBlockExpectation
is more concise; it's also a zillion times faster than XCTNSPredicateExpectation
. For some reason, Apple's XCTNSPredicateExpectation
only seems to evaluate the block every second. This is insanely slow, especially if you have even a modest suite of async tests (100 async tests == 100+ seconds to complete). XCTBlockExpectation
returns immediately when the block condition is met, making it way faster than XCTNSPredicateExpectation
.