Pragmatic Swift
-
Upload
kenneth-blake-merryman -
Category
Software
-
view
161 -
download
0
Transcript of Pragmatic Swift
Pragmatic SwiftBlake Merryman
A collection of "use everyday" tips & tricks to help keep your code base
maintainable, understandable, & extendable.
Covered tonight…Documentation
Organization
Striving for Smallness
Access Control
Early Exits
Collection-Type APIs
Property Initialization
Lazy Loading
Lazy Sequences
Slow Build Times
DocumentationSave valuable time when ⌥ + clicking around
Easily write rich Quick Help docs in Markdown
Add quick notes to function, variable, etc.: /// Important (but brief) QH note.
Auto-document functions: ⌘ + ⌥ + /
Documentation
Useful comments…
// TODO: *** Shows in Jump Bar *** // NOTE: Something important!
Integrate these into build systems with scripts or special tools (e.g. swiftlint)
Documentation
// Forces Xcode to flag a warning at compile time // Source: http://stackoverflow.com/a/26869489/1418335 // Disclaimer: Not tested; I use swiftlint :) if [ "${CONFIGURATION}" = "Debug" ]; then TAGS=“TODO:|FIXME:" echo "searching ${SRCROOT} for ${TAGS}" find "${SRCROOT}" \( -name "*.swift" \) -print0 | xargs -0 egrep \ --with-filename \ --line-number \ --only-matching "($TAGS).*\$" \ | perl -p -e "s/($TAGS)/ warning: \$1/" fi
OrganizationA well organized project can save hours of time
Files are Free!
Group according to large ideas
Extensions are great for grouping functionality:
// MARK: - Private Helpers extension MyViewController { /***/ }
// MARK: - + UICollectionViewDataSource extension MyViewController: UICollectionViewDataSource { /***/ }
Strive for Smallness
Being small at scope improves understanding
Take advantage of typed parameters:
func configure(_ cell: MyCollectionViewCell, at indexPath: IndexPath) func configure(_ cell: YourCollectionViewCell, at indexPath: IndexPath) func configure(_ cell: TheirCollectionViewCell, at indexPath: IndexPath)
Strive for Smallness
How else can we keep things small? Extensions, Data Sources, Network Stacks, Inheritance, Composition, Rich Enums, Generics, Access Control
Access ControlWhat is need to know and Who needs to know it?
Access Control dictates how an API is viewed
Levels: private, fileprivate, internal, public, open
Tips: - Use private to hide extension helpers - Use fileprivate to hide extensions
Access Control
Early Exits
Help prevent nesting
Important validation logic is up front
Clearly indicates success/failure
Prefer “guard” over “if”
Early Exits // Not Preferred if let a = optionalA, a.someBoolean == true { // Do GOOD thing :) } else { // Do BAD thing :( }
// Preferred guard let a = optionalA, a.someBoolean == true else { // Do Bad thing :( return } // Do GOOD thing! :D
❌
✅
Collection-Type APIs
Take advantage of new closure-based APIs to keep logic tight & readable
Chainable to allow for building complex logic fast
Great reference implementation for using Protocols (with default implementations) + Generics
Collection-Type APIsvar numbers = [Int]() numbers += 1...1_000
var oddNumbers = [Int]() for x in numbers { if x % 2 != 0 { oddNumbers.append(x) } }
var multNumbers = [Int]() for x in oddNumbers { let x10 = x * 10 multNumbers.append(x10) }
for x in multNumbers { print(x) }
// Prints: 10, 30, 50, ..., 9950, 9970, 9990
Collection-Type APIs
var numbers = [Int]() numbers += 1...1_000
numbers.filter { $0 % 2 != 0 } // Grab all of the odd numbers .map { $0 * 10 } // Multiply by 10 .forEach { print($0) } // Print out new values
// Prints: 10, 30, 50, ..., 9950, 9970, 9990
Property Initializationclass MyViewController: UIViewController {
var collectionView: UICollectionView?
override func viewDidLoad() { super.viewDidLoad()
let flowLayout = UICollectionViewFlowLayout() flowLayout.scrollDirection = .horizontal collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) collectionView?.translatesAutoresizingMaskIntoConstraints = false collectionView?.isPagingEnabled = true collectionView?.backgroundColor = .clear collectionView?.showsHorizontalScrollIndicator = false
view.addSubview(collectionView!) // Auto Layout Code ... } }
Property Initializationclass 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: - Lifecycle
override func viewDidLoad() { super.viewDidLoad()
view.addSubview(collectionView) // Auto Layout Code ... } }
Lazy Loading
Initialize at access rather than init
class MyViewController: UIViewController {
lazy var collectionView = UICollectionView.configuredCollectionView()
// MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.addSubview(collectionView) // Auto Layout Code ... } }
Lazy Loading
ProTip: Limit the scope of setter with Access Control
class MyViewController: UIViewController {
private(set) lazy var collectionView = UICollectionView.configuredCollectionView()
// MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.addSubview(collectionView) // Auto Layout Code ... } }
Lazy Loading
Constants declared at global scope & statically are lazy by default. Does not apply to constants declared at instance scope.
Lazy Sequences
lazy property on SequenceType & CollectionType
Lazily access elements in sequences/collections
Huge performance benefit!
Easy to implement…
Lazy Sequences
var numbers = [Int]() numbers += 1...1_000
let newNumbers = numbers .filter { $0 % 2 != 0 } // Executed 1,000 times .map { $0 * 10 } // Executed 500 times
newNumbers.last
All calculations completed before access!
Lazy Sequences
var numbers = [Int]() numbers += 1...1_000
let newNumbers = numbers.lazy .filter { $0 % 2 != 0 } // Executed 1 time .map { $0 * 10 } // Executed 1 time
newNumbers.last
Calculations performed as needed on access!
Slow Build Times
Sometimes Swift build times are slow (**cough** type inference **cough**)
Setting to add build times to logs: - Project Settings > Build Settings > Other Swift Flags - Add -Xfrontend -debug-time-function-bodies - “Expand All Transcripts” (look for new XX ms)
Slow Build Times
Pro Tip: Prioritize build time optimizations:
“Copy transcripts for shown results” for logs then in Terminal, run: pbpaste | egrep '\.[0-9]ms' | sort -t "." -k 1 -n | tail -10
Highlights the top 10 build time hogs in project
Q & A
Sourceshttps://developer.apple.com/reference/swift
http://stackoverflow.com/a/26869489/1418335
https://thatthinginswift.com/kill-your-viewdidload/
http://alisoftware.github.io/swift/2016/02/28/being-lazy/
https://thatthinginswift.com/debug-long-compile-times-swift/
https://twitter.com/erikaderstedt/status/725217977314992128