KEMBAR78
Pragmatic Swift | PDF
Pragmatic Swift
Blake 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
Documentation
Save 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
Organization
A 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 Control
What 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 APIs
var 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 Initialization
class 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 Initialization
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: - 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
Sources
https://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

Pragmatic Swift

  • 1.
  • 2.
    A collection of"use everyday" tips & tricks
 to help keep your code base 
 maintainable, understandable, & extendable.
  • 3.
    Covered tonight… Documentation Organization Striving forSmallness Access Control Early Exits Collection-Type APIs Property Initialization Lazy Loading Lazy Sequences Slow Build Times
  • 4.
    Documentation Save valuable timewhen ⌥ + 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:  ⌘ + ⌥ + /
  • 5.
    Documentation Useful comments…
 // TODO:*** Shows in Jump Bar *** // NOTE: Something important! Integrate these into build systems with scripts 
 or special tools (e.g. swiftlint)
  • 6.
    Documentation // Forces Xcodeto 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
  • 7.
    Organization A well organizedproject 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 { /***/ }
  • 8.
    Strive for Smallness Beingsmall 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)
  • 9.
    Strive for Smallness Howelse can we keep things small?
 
 Extensions, Data Sources, Network Stacks, 
 Inheritance, Composition, Rich Enums, 
 Generics, Access Control
  • 10.
    Access Control What isneed 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
  • 11.
  • 12.
    Early Exits Help preventnesting Important validation logic is up front Clearly indicates success/failure Prefer “guard” over “if”
  • 13.
    Early Exits // NotPreferred 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 ❌ ✅
  • 14.
    Collection-Type APIs Take advantageof 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
  • 15.
    Collection-Type APIs var 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
  • 16.
    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
  • 17.
    Property Initialization class 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 ... } }
 
 

  • 18.
    Property Initialization 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: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.addSubview(collectionView) // Auto Layout Code ... } }
  • 19.
    Lazy Loading Initialize ataccess rather than init
 class MyViewController: UIViewController { lazy var collectionView = UICollectionView.configuredCollectionView() // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.addSubview(collectionView) // Auto Layout Code ... } }
  • 20.
    Lazy Loading ProTip: Limitthe 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 ... } }
  • 21.
    Lazy Loading Constants declaredat global scope & statically are lazy by default. Does not apply to constants declared at instance scope.
  • 22.
    Lazy Sequences lazy propertyon SequenceType & CollectionType Lazily access elements in sequences/collections Huge performance benefit! Easy to implement…
  • 23.
    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!
  • 24.
    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!
  • 25.
    Slow Build Times SometimesSwift 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)
  • 26.
    Slow Build Times ProTip: 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
  • 27.
  • 28.