Slide 1

Slide 1 text

@NatashaTheRobot

Slide 2

Slide 2 text

Protocol-Oriented Programming in Swift Dave Abrahams Professor of Blowing-Your-Mind

Slide 3

Slide 3 text

–- Professor of Blowing-Your-Mind "Swift Is a Protocol-Oriented Programming Language"

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Practical POP • View • (UITable)ViewController • Networking

Slide 6

Slide 6 text

POP Views

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

// FoodImageView.swift import UIKit class FoodImageView: UIImageView { func shake() { let animation = CABasicAnimation(keyPath: "position") animation.duration = 0.05 animation.repeatCount = 5 animation.autoreverses = true animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 4.0, self.center.y)) animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 4.0, self.center.y)) layer.addAnimation(animation, forKey: "position") } }

Slide 10

Slide 10 text

// ViewController.swift import UIKit class ViewController: UIViewController { @IBOutlet weak var foodImageView: FoodImageView! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() } }

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

// ShakeableButton.swift import UIKit class ActionButton: UIButton { func shake() { let animation = CABasicAnimation(keyPath: "position") animation.duration = 0.05 animation.repeatCount = 5 animation.autoreverses = true animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 4.0, self.center.y)) animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 4.0, self.center.y)) layer.addAnimation(animation, forKey: "position") } }

Slide 14

Slide 14 text

// ViewController.swift class ViewController: UIViewController { @IBOutlet weak var foodImageView: FoodImageView! @IBOutlet weak var actionButton: ActionButton! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() actionButton.shake() } }

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

// UIViewExtension.swift import UIKit extension UIView { func shake() { let animation = CABasicAnimation(keyPath: "position") animation.duration = 0.05 animation.repeatCount = 5 animation.autoreverses = true animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 4.0, self.center.y)) animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 4.0, self.center.y)) layer.addAnimation(animation, forKey: "position") } }

Slide 17

Slide 17 text

class FoodImageView: UIImageView { // other customization here } class ActionButton: UIButton { // other customization here } class ViewController: UIViewController { @IBOutlet weak var foodImageView: FoodImageView! @IBOutlet weak var actionButton: ActionButton! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() actionButton.shake() } }

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

// Shakeable.swift import UIKit protocol Shakeable { } extension Shakeable where Self: UIView { func shake() { // implementation code } }

Slide 20

Slide 20 text

class FoodImageView: UIImageView, Shakeable { } class ActionButton: UIButton, Shakeable { }

Slide 21

Slide 21 text

class FoodImageView: UIImageView, Shakeable, Dimmable { }

Slide 22

Slide 22 text

class FoodImageView: UIImageView, Dimmable { }

Slide 23

Slide 23 text

Transparent View Controllers and Dim Backgrounds totem.training

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

POP (UITable)ViewControllers

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

// FoodLaLaViewController override func viewDidLoad() { super.viewDidLoad() let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil) tableView.registerNib(foodCellNib, forCellReuseIdentifier: "FoodTableViewCell") }

Slide 28

Slide 28 text

let foodCellNib = UINib(nibName: String(FoodTableViewCell), bundle: nil) tableView.registerNib(foodCellNib, forCellReuseIdentifier: String(FoodTableViewCell))

Slide 29

Slide 29 text

protocol ReusableView: class {} extension ReusableView where Self: UIView { static var reuseIdentifier: String { return String(self) } }

Slide 30

Slide 30 text

extension UITableViewCell: ReusableView { } FoodTableViewCell.reuseIdentifier // FoodTableViewCell

Slide 31

Slide 31 text

let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil) tableView.registerNib(foodCellNib, forCellReuseIdentifier: FoodTableViewCell.reuseIdentifier)

Slide 32

Slide 32 text

protocol NibLoadableView: class { } extension NibLoadableView where Self: UIView { static var nibName: String { return String(self) } }

Slide 33

Slide 33 text

extension FoodTableViewCell: NibLoadableView { } FoodTableViewCell.nibName // "FoodTableViewCell"

Slide 34

Slide 34 text

let foodCellNib = UINib(nibName: FoodTableViewCell.nibName, bundle: nil) tableView.registerNib(foodCellNib, forCellReuseIdentifier: FoodTableViewCell.reuseIdentifier)

Slide 35

Slide 35 text

extension UITableView { func register(_: T.Type) { let nib = UINib(nibName: T.nibName, bundle: nil) registerNib(nib, forCellReuseIdentifier: T.reuseIdentifier) } }

Slide 36

Slide 36 text

let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil) tableView.registerNib(foodCellNib, forCellReuseIdentifier: "FoodTableViewCell")

Slide 37

Slide 37 text

tableView.register(FoodTableViewCell)

Slide 38

Slide 38 text

extension UITableView { func dequeueReusableCell(forIndexPath indexPath: NSIndexPath) -> T { guard let cell = dequeueReusableCellWithIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as? T else { fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)") } return cell } }

Slide 39

Slide 39 text

guard let cell = tableView.dequeueReusableCellWithIdentifier(“FoodTableViewCell", forIndexPath: indexPath) as? FoodTableViewCell else { fatalError("Could not dequeue cell with identifier: FoodTableViewCell") }

Slide 40

Slide 40 text

let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell

Slide 41

Slide 41 text

if indexPath.row == 0 { return tableView.dequeueReusableCell(forIndexPath: indexPath) as DesertTableViewCell } return tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell

Slide 42

Slide 42 text

iOS Cell Registration & Reusing with Swift Protocol Extensions and Generics medium.com/@gonzalezreal

Slide 43

Slide 43 text

Protocol-Oriented Segue Identifiers in Swift natashatherobot.com

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

POP Networking

Slide 46

Slide 46 text

struct FoodService { func get(completionHandler: Result<[Food]> -> Void) { // make asynchronous API call // and return appropriate result } }

Slide 47

Slide 47 text

enum Result { case Success(T) case Failure(ErrorType) }

Slide 48

Slide 48 text

struct FoodService { func get(completionHandler: Result<[Food]> -> Void) { // make asynchronous API call // and return appropriate result } }

Slide 49

Slide 49 text

// FoodLaLaViewController var dataSource = [Food]() { didSet { tableView.reloadData() } } override func viewDidLoad() { super.viewDidLoad() getFood() } private func getFood() { FoodService().getFood() { [weak self] result in switch result { case .Success(let food): self?.dataSource = food case .Failure(let error): self?.showError(error) } } }

Slide 50

Slide 50 text

View Controller Tests?!!!

Slide 51

Slide 51 text

// FoodLaLaViewController var dataSource = [Food]() { didSet { tableView.reloadData() } } override func viewDidLoad() { super.viewDidLoad() getFood() } private func getFood() { FoodService().getFood() { [weak self] result in switch result { case .Success(let food): self?.dataSource = food case .Failure(let error): self?.showError(error) } } }

Slide 52

Slide 52 text

// FoodLaLaViewController func getFood(fromService service: FoodService) { service.getFood() { [weak self] result in // handle result } }

Slide 53

Slide 53 text

// FoodLaLaViewControllerTests func testFetchFood() { viewController.getFood(fromService: FoodService()) // now what? }

Slide 54

Slide 54 text

struct FoodService { func get(completionHandler: Result<[Food]> -> Void) { // make asynchronous API call // and return appropriate result } }

Slide 55

Slide 55 text

protocol Gettable { associatedtype T func get(completionHandler: Result -> Void) }

Slide 56

Slide 56 text

struct FoodService: Gettable { func get(completionHandler: Result<[Food]> -> Void) { // make asynchronous API call // and return appropriate result } }

Slide 57

Slide 57 text

// FoodLaLaViewController override func viewDidLoad() { super.viewDidLoad() getFood(fromService: FoodService()) } func getFood(fromService service: S) { service.get() { [weak self] result in switch result { case .Success(let food): self?.dataSource = food case .Failure(let error): self?.showError(error) } } }

Slide 58

Slide 58 text

// FoodLaLaViewControllerTests class Fake_FoodService: Gettable { var getWasCalled = false func get(completionHandler: Result<[Food]> -> Void) { getWasCalled = true completionHandler(Result.Success(food)) } }

Slide 59

Slide 59 text

// FoodLaLaViewControllerTests func testFetchFood() { let fakeFoodService = Fake_FoodService() viewController.getFood(fromService: fakeFoodService) XCTAssertTrue(fakeFoodService.getWasCalled) XCTAssertEqual(viewController.dataSource.count, food.count) XCTAssertEqual(viewController.dataSource, food) }

Slide 60

Slide 60 text

Update: View Controller Data Injection with Storyboards and Segues in Swift natashatherobot.com

Slide 61

Slide 61 text

Protocols with Associated Types Alexis Gallagher 2015.funswiftconf.com

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

Practical POP • View • (UITable)ViewController • Networking

Slide 64

Slide 64 text

Beyond Crusty: Real-World Protocols Rob Napier thedotpost.com

Slide 65

Slide 65 text

Blending Cultures: The Best of Functional, Protocol-Oriented, and Object-Oriented Programming Daniel Steinberg realm.io

Slide 66

Slide 66 text

@NatashaTheRobot