Pragmatic Swift

Post on 13-Apr-2017

162 views 0 download

transcript

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