Top Banner
@NatashaTheRobot
69

Swift Delhi: Practical POP

Apr 13, 2017

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Swift Delhi: Practical POP

@NatashaTheRobot

Page 2: Swift Delhi: Practical POP

• @NatashaTheRobot 🤖

• NatashaTheRobot.com

• This Week in Swift Newsletter

• Swift Jobs

• tryswift.co 🐥#🎉

• @NatashaTheNomad %

Page 3: Swift Delhi: Practical POP

UITableViewDelegate UITableViewDataSource UITextFieldDelegate NSURLSessionDelegate CLLocationManagerDelegate MCSessionDelegate

Page 4: Swift Delhi: Practical POP

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

Page 5: Swift Delhi: Practical POP

–- Professor of Blowing-Your-Mind

"Swift Is a Protocol-Oriented Programming Language"

Page 6: Swift Delhi: Practical POP
Page 7: Swift Delhi: Practical POP

Practical POP

• View

• (UITable)ViewController

• Networking

Page 8: Swift Delhi: Practical POP

POP Views

Page 9: Swift Delhi: Practical POP
Page 10: Swift Delhi: Practical POP
Page 11: Swift Delhi: Practical POP

// 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: CGPoint(x: self.center.x - 4.0, y: self.center.y)) animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4.0, y: self.center.y)) layer.add(animation, forKey: "position") }

Page 12: Swift Delhi: Practical POP

// ViewController.swift

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var foodImageView: FoodImageView! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() } }

Page 13: Swift Delhi: Practical POP

💃💃💃

Page 14: Swift Delhi: Practical POP
Page 15: Swift Delhi: Practical POP

// 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: CGPoint(x: self.center.x - 4.0, y: self.center.y)) animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4.0, y: self.center.y)) layer.add(animation, forKey: "position") } }

Page 16: Swift Delhi: Practical POP

// ViewController.swift

class ViewController: UIViewController {

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

Page 17: Swift Delhi: Practical POP

🤔

Page 18: Swift Delhi: Practical POP

// 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: CGPoint(x: self.center.x - 4.0, y: self.center.y)) animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4.0, y: self.center.y)) layer.add(animation, forKey: "position") } }

Page 19: Swift Delhi: Practical POP

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() } }

Page 20: Swift Delhi: Practical POP
Page 21: Swift Delhi: Practical POP

// Shakeable.swift

import UIKit

protocol Shakeable { }

extension Shakeable where Self: UIView { func shake() { // implementation code } }

Page 22: Swift Delhi: Practical POP

class FoodImageView: UIImageView, Shakeable {

}

class ActionButton: UIButton, Shakeable {

}

Page 23: Swift Delhi: Practical POP

class FoodImageView: UIImageView, Shakeable, Dimmable {

}

Page 24: Swift Delhi: Practical POP

class FoodImageView: UIImageView, Dimmable {

}

Page 25: Swift Delhi: Practical POP

Transparent View Controllers and Dim Backgrounds

totem.training

Page 26: Swift Delhi: Practical POP

👯👯👯👯👯

Page 27: Swift Delhi: Practical POP

POP (UITable)ViewControllers

Page 28: Swift Delhi: Practical POP
Page 29: Swift Delhi: Practical POP

// FoodLaLaViewController

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

Page 30: Swift Delhi: Practical POP

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

Page 31: Swift Delhi: Practical POP

protocol ReusableView: class {}

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

Page 32: Swift Delhi: Practical POP

extension UITableViewCell: ReusableView { }

FoodTableViewCell.reuseIdentifier // FoodTableViewCell

Page 33: Swift Delhi: Practical POP

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

Page 34: Swift Delhi: Practical POP

protocol NibLoadableView: class { }

extension NibLoadableView where Self: UIView {

static var nibName: String { return String(describing: self) }

}

Page 35: Swift Delhi: Practical POP

extension FoodTableViewCell: NibLoadableView { }

FoodTableViewCell.nibName // "FoodTableViewCell"

Page 36: Swift Delhi: Practical POP

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

Page 37: Swift Delhi: Practical POP

extension UITableView { func register<T: UITableViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView { let nib = UINib(nibName: T.nibName, bundle: nil) register(nib, forCellReuseIdentifier: T.reuseIdentifier) } }

Page 38: Swift Delhi: Practical POP

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

Page 39: Swift Delhi: Practical POP

tableView.register(FoodTableViewCell.self)

Page 40: Swift Delhi: Practical POP

extension UITableView { func dequeueReusableCell<T: UITableViewCell>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView { guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as? T else { fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)") } return cell } }

Page 41: Swift Delhi: Practical POP

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

Page 42: Swift Delhi: Practical POP

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

Page 43: Swift Delhi: Practical POP

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

Page 44: Swift Delhi: Practical POP

iOS Cell Registration & Reusing with Swift Protocol Extensions and

Genericsmedium.com/@gonzalezreal

Page 45: Swift Delhi: Practical POP

Protocol-Oriented Segue Identifiers in Swift

natashatherobot.com

Page 46: Swift Delhi: Practical POP
Page 47: Swift Delhi: Practical POP

POP Networking

Page 48: Swift Delhi: Practical POP

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

Page 49: Swift Delhi: Practical POP

enum Result<T> { case Success(T) case Failure(Error) }

Page 50: Swift Delhi: Practical POP

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

Page 51: Swift Delhi: Practical POP

// FoodLaLaViewController

var dataSource = [Food]() { didSet { tableView.reloadData() } }

override func viewDidLoad() { super.viewDidLoad() getFood() }

private func getFood() { FoodService().get() { [weak self] result in switch result { case .Success(let food): self?.dataSource = food case .Failure(let error): self?.showError(error: error) } } }

Page 52: Swift Delhi: Practical POP

View Controller Tests?!!! 😱

Page 53: Swift Delhi: Practical POP

// FoodLaLaViewController

var dataSource = [Food]() { didSet { tableView.reloadData() } }

override func viewDidLoad() { super.viewDidLoad() getFood() }

private func getFood() { FoodService().get() { [weak self] result in switch result { case .Success(let food): self?.dataSource = food case .Failure(let error): self?.showError(error) } } }

Page 54: Swift Delhi: Practical POP

// FoodLaLaViewController

func getFood(fromService service: FoodService) {

service.getFood() { [weak self] result in // handle result } }

Page 55: Swift Delhi: Practical POP

// FoodLaLaViewControllerTests

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

Page 56: Swift Delhi: Practical POP

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

Page 57: Swift Delhi: Practical POP

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

Page 58: Swift Delhi: Practical POP

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

Page 59: Swift Delhi: Practical POP

// FoodLaLaViewController

override func viewDidLoad() { super.viewDidLoad() getFood(fromService: FoodService()) }

func getFood<S: Gettable>(fromService service: S) where S.T == [Food] { service.get() { [weak self] result in switch result { case .Success(let food): self?.dataSource = food case .Failure(let error): self?.showError(error) } } }

Page 60: Swift Delhi: Practical POP

// FoodLaLaViewControllerTests

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

Page 61: Swift Delhi: Practical POP

// FoodLaLaViewControllerTests

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

Page 62: Swift Delhi: Practical POP

Update: View Controller Data Injection with Storyboards and

Segues in Swiftnatashatherobot.com

Page 63: Swift Delhi: Practical POP

Protocols with Associated TypesAlexis Gallagher

2015.funswiftconf.com

Page 64: Swift Delhi: Practical POP

😎😎😎

Page 65: Swift Delhi: Practical POP

Practical POP

• View

• (UITable)ViewController

• Networking

Page 66: Swift Delhi: Practical POP

Beyond Crusty: Real-World Protocols

Rob Napier thedotpost.com

Page 67: Swift Delhi: Practical POP

Blending Cultures: The Best of Functional, Protocol-Oriented, and

Object-Oriented ProgrammingDaniel Steinberg

realm.io

Page 68: Swift Delhi: Practical POP

T H A N K Y O U !

• @NatashaTheRobot 🤖

• NatashaTheRobot.com

• This Week in Swift Newsletter

• Swift Jobs

• tryswift.co 🐥#🎉

• @NatashaTheNomad %

Page 69: Swift Delhi: Practical POP

@NatashaTheRobot