MINIMIZING DECISION FATIGUE
TO IMPROVE TEAM PRODUCTIVITY
TRY! SWIFT MARCH, 2017
DEREK LEE @DEREKLEEROCK
☀ 🌛?
Class Struct
🤔
Tabs SpacesCocoapods CarthageStoryboards CodeA BUIKit ReactNative
?
A B
🤔
???
?? ?
??? ??? ?
???
? ?????
? ???
?
?????? ?
??
???? ?
??
?
??????
???? ?
??? ?? 😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A DAY IN THE LIFE @ PIVOTAL LABS
BREAKFAST!
🕣8:45am
MORNING OFFICE STANDUP
🕘9:06am
🕘9:15am
PROJECT STANDUP
PROJECT ORGANIZATION
Open Quickly ⌘+⇧+O
Filter in Navigator ⌘+⌥+J
Reveal in Navigator ⌘+⇧+J
Find in Files ⌘+⇧+F
HOW CAN WE FIND FILES IN XCODE?
“Hunt and Peck”
HOW DO WE REALLY FIND FILES IN XCODE?
“Helpers” FolderNo organization
WHAT WE’D LIKE TO AVOID
MVC?
APPLICATION ・ COMPONENTS ・ UI
APPLICATION
COMPONENTS
UI
PAIR PROGRAMMING
🕙10:00am
PAIR PROGRAMMING - SETUP
▸ Ping-Pong
PAIR PROGRAMMING - STYLES
+
▸ Driver + Navigator
PAIR PROGRAMMING - IN ACTION▸ We pair 99% of the time
▸ All disciplines pair: Engineering, Design, PMs
▸ Change pairs daily
▸ Regularly switch tracks of work
LUNCHTIME TECH TALK
🕧12:30pm
BACK TO PAIRING
🕜1:30pm
IMPROMPTU TEAM DISCUSSION
🕝2:30pm
SWIFT FILE ORGANIZATION
VIEW CONTROLLER: SHOW DOCUMENT ITEMS: ^ + 6class CountingRepeaterViewController: UIViewController { fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int
let countingLabel: UILabel
init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... }
override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) { ... }
func addSubviews() { ... }
func addConstraints() { ... }
... }
With “// MARK:”Without Annotations With “// MARK: —“MARK ANNOTATION COMPARISON
VIEW CONTROLLER ORGANIZATIONclass CountingRepeaterViewController: UIViewController { // MARK: - Properties fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int
// MARK: - View Elements let headerLabel: UILabel let countingLabel: UILabel
// MARK: - Initialization init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... }
// MARK: - Lifecycle Methods override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) }
// MARK: - Private Methods fileprivate extension CountingRepeaterViewController { func addSubviews() { ... }
func addConstraints() { ... }
VIEW CONTROLLER ORGANIZATIONclass CountingRepeaterViewController: UIViewController { // MARK: - Properties fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int
// MARK: - View Elements let headerLabel: UILabel let countingLabel: UILabel
// MARK: - Initialization init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... }
// MARK: - Lifecycle Methods override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) { ... } }
// MARK: - Private Methods fileprivate extension CountingRepeaterViewController { func addSubviews() { ... }
func addConstraints() { ... }
// MARK: - Lifecycle Methods override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) { ... }
class CountingRepeaterViewController: UIViewController { // MARK: - Properties fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int
// MARK: - View Elements let headerLabel: UILabel let countingLabel: UILabel
// MARK: - Initialization init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... }
// MARK: - Lifecycle Methods override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) }
// MARK: - Private Methods fileprivate extension CountingRepeaterViewController { func addSubviews() { ... }
func addConstraints() { ... }
CREATE TEMPLATE FROM XCODE SNIPPETS
PROTOCOL CONFORMANCEstruct DefaultCustomer: Customer { let name: String private(set) var rentals: [Rental]
init(name: String) { ... }
mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... } }
PROTOCOL CONFORMANCEstruct DefaultCustomer: Customer { // MARK: - Properties let name: String private(set) var rentals: [Rental]
// MARK: - Initialization init(name: String) { ... }
mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... } }
PROTOCOL CONFORMANCEstruct DefaultCustomer { // MARK: - Properties let name: String private(set) var rentals: [Rental]
// MARK: - Initialization init(name: String) { ... } }
// MARK: - Customer extension DefaultCustomer: Customer { mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... } }
PROTOCOL CONFORMANCEstruct DefaultCustomer { // MARK: - Properties let name: String private(set) var rentals: [Rental]
// MARK: - Initialization init(name: String) { ... } }
// MARK: - Customer extension DefaultCustomer: Customer { mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... } }
// MARK: - Private Methods fileprivate extension DefaultCustomer { func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... } }
PING-PONG BREAK
🕞3:30pm
CROSS-FUNCTIONAL PAIRING
🕓4:00pm
Engineering x Design
STYLING UI OBJECTS
extension UIFont { class func abcMediumFont(size: CGFloat) -> UIFont { return UIFont(name: "AvenirNext-Medium", size: size)! }
class func abcBoldFont(size: CGFloat) -> UIFont { return UIFont(name: "AvenirNext-Bold", size: size)! } }
DEFINING FONTS
extension UIColor { class var abcDarkSkyBlue: UIColor { return UIColor( red: 52.0 / 255.0, green: 152.0 / 255.0, blue: 219.0 / 255.0, alpha: 1.0 ) }
class var abcBlueish: UIColor { return UIColor( red: 41.0 / 255.0, green: 128.0 / 255.0, blue: 185.0 / 255.0, alpha: 1.0 ) } }
DEFINING COLORS
enum UIButtonStyle { case primary, negative
func applyTo(button: UIButton) { switch (self) {
case .primary: button.titleLabel?.font = UIFont.abcMediumFont(
size: 15 )
button.setTitleColor(UIColor.white, for: .normal) button.backgroundColor = UIColor.abcDarkSkyBlue button.layer.borderColor = UIColor.abcBlueish.cgColor button.layer.borderWidth = 1.0 break
case .negative: // ... break } } }
DEFINING STYLES
APPLYING STYLESextension UIButton { func apply(style: UIButtonStyle) { style.applyTo(button: self) } }
class MyViewController: UIViewController {
let confirmButton: UIButton let cancelButton: UIButton
...
fileprivate func applyStyles() { confirmButton.apply(style: .primary) cancelButton.apply(style: .negative) } }
RETROSPECTIVE (RETRO)
🕔5:00pm
RETROS - INGREDIENTS
🙂🙂
🙂
🙂
🙂
🙂
Core Team Members Food & Snacks
🍓🧀
🍙🍪
Drinks
☕🍵
🍷🍺
RETROS @ PIVOTAL LABS
😃Discuss
😭Keep
😕Improve
RETROS @ PIVOTAL LABS
▸ Reflect → Continuous improvement
▸ Building Trust
▸ Honest communication
▸ Identify & solve problems early
▸ Team brainstorming
Kent Beck, Extreme Programming Explained
THE COURAGE TO SPEAK TRUTHS, PLEASANT OR UNPLEASANT,
FOSTERS COMMUNICATION AND TRUST.
SUMMARY
▸ Project Standup
▸ Pair Programming
▸ Lunchtime Tech Talk
▸ Impromptu Team Discussions
▸ Cross-Functional Pairing
▸ Retrospectives
▸ Project Organization
▸ Swift File Organization
▸ Styling UI Objects
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
😓😓 😓
😓😓 😓
😓😓 😓
😓😓 😓
😁😃 😛
🙃😎 😅
Thank you!
try! Swift March 2017
@DEREKLEEROCK Thank you!
try! Swift March 2017