Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Natasha Murashev: Practical Protocol-Oriented Programming

Natasha Murashev: Practical Protocol-Oriented Programming

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

May 10, 2016
Tweet

Transcript

  1. @NatashaTheRobot

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

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

  4. None
  5. Practical POP • View • (UITable)ViewController • Networking

  6. POP Views

  7. None
  8. None
  9. // 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") } }
  10. // ViewController.swift import UIKit class ViewController: UIViewController { @IBOutlet weak

    var foodImageView: FoodImageView! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() } }
  11. None
  12. None
  13. // 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") } }
  14. // ViewController.swift class ViewController: UIViewController { @IBOutlet weak var foodImageView:

    FoodImageView! @IBOutlet weak var actionButton: ActionButton! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() actionButton.shake() } }
  15. None
  16. // 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") } }
  17. 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() } }
  18. None
  19. // Shakeable.swift import UIKit protocol Shakeable { } extension Shakeable

    where Self: UIView { func shake() { // implementation code } }
  20. class FoodImageView: UIImageView, Shakeable { } class ActionButton: UIButton, Shakeable

    { }
  21. class FoodImageView: UIImageView, Shakeable, Dimmable { }

  22. class FoodImageView: UIImageView, Dimmable { }

  23. Transparent View Controllers and Dim Backgrounds totem.training

  24. None
  25. POP (UITable)ViewControllers

  26. None
  27. // FoodLaLaViewController override func viewDidLoad() { super.viewDidLoad() let foodCellNib =

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

  29. protocol ReusableView: class {} extension ReusableView where Self: UIView {

    static var reuseIdentifier: String { return String(self) } }
  30. extension UITableViewCell: ReusableView { } FoodTableViewCell.reuseIdentifier // FoodTableViewCell

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

  32. protocol NibLoadableView: class { } extension NibLoadableView where Self: UIView

    { static var nibName: String { return String(self) } }
  33. extension FoodTableViewCell: NibLoadableView { } FoodTableViewCell.nibName // "FoodTableViewCell"

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

  35. extension UITableView { func register<T: UITableViewCell where T: ReusableView, T:

    NibLoadableView>(_: T.Type) { let nib = UINib(nibName: T.nibName, bundle: nil) registerNib(nib, forCellReuseIdentifier: T.reuseIdentifier) } }
  36. let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil) tableView.registerNib(foodCellNib, forCellReuseIdentifier: "FoodTableViewCell")

  37. tableView.register(FoodTableViewCell)

  38. extension UITableView { func dequeueReusableCell<T: UITableViewCell where T: ReusableView>(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 } }
  39. guard let cell = tableView.dequeueReusableCellWithIdentifier(“FoodTableViewCell", forIndexPath: indexPath) as? FoodTableViewCell else

    { fatalError("Could not dequeue cell with identifier: FoodTableViewCell") }
  40. let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell

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

    } return tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell
  42. iOS Cell Registration & Reusing with Swift Protocol Extensions and

    Generics medium.com/@gonzalezreal
  43. Protocol-Oriented Segue Identifiers in Swift natashatherobot.com

  44. None
  45. POP Networking

  46. struct FoodService { func get(completionHandler: Result<[Food]> -> Void) { //

    make asynchronous API call // and return appropriate result } }
  47. enum Result<T> { case Success(T) case Failure(ErrorType) }

  48. struct FoodService { func get(completionHandler: Result<[Food]> -> Void) { //

    make asynchronous API call // and return appropriate result } }
  49. // 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) } } }
  50. View Controller Tests?!!!

  51. // 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) } } }
  52. // FoodLaLaViewController func getFood(fromService service: FoodService) { service.getFood() { [weak

    self] result in // handle result } }
  53. // FoodLaLaViewControllerTests func testFetchFood() { viewController.getFood(fromService: FoodService()) // now what?

    }
  54. struct FoodService { func get(completionHandler: Result<[Food]> -> Void) { //

    make asynchronous API call // and return appropriate result } }
  55. protocol Gettable { associatedtype T func get(completionHandler: Result<T> -> Void)

    }
  56. struct FoodService: Gettable { func get(completionHandler: Result<[Food]> -> Void) {

    // make asynchronous API call // and return appropriate result } }
  57. // FoodLaLaViewController override func viewDidLoad() { super.viewDidLoad() getFood(fromService: FoodService()) }

    func getFood<S: Gettable where S.T == [Food]>(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) } } }
  58. // FoodLaLaViewControllerTests class Fake_FoodService: Gettable { var getWasCalled = false

    func get(completionHandler: Result<[Food]> -> Void) { getWasCalled = true completionHandler(Result.Success(food)) } }
  59. // FoodLaLaViewControllerTests func testFetchFood() { let fakeFoodService = Fake_FoodService() viewController.getFood(fromService:

    fakeFoodService) XCTAssertTrue(fakeFoodService.getWasCalled) XCTAssertEqual(viewController.dataSource.count, food.count) XCTAssertEqual(viewController.dataSource, food) }
  60. Update: View Controller Data Injection with Storyboards and Segues in

    Swift natashatherobot.com
  61. Protocols with Associated Types Alexis Gallagher 2015.funswiftconf.com

  62. None
  63. Practical POP • View • (UITable)ViewController • Networking

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

  65. Blending Cultures: The Best of Functional, Protocol-Oriented, and Object-Oriented Programming

    Daniel Steinberg realm.io
  66. @NatashaTheRobot