@NatashaTheRobot
Protocol-Oriented Programming in Swift
Dave Abrahams Professor of Blowing-Your-Mind
–- Professor of Blowing-Your-Mind
"Swift Is a Protocol-Oriented Programming Language"
Practical POP
• View
• (UITable)ViewController
• Networking
POP Views
// 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")
}
}
// ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var foodImageView: FoodImageView!
@IBAction func onShakeButtonTap(sender: AnyObject) {
foodImageView.shake()
}
}
💃💃💃
// 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")
}
}
// ViewController.swift
class ViewController: UIViewController {
@IBOutlet weak var foodImageView: FoodImageView!
@IBOutlet weak var actionButton: ActionButton!
@IBAction func onShakeButtonTap(sender: AnyObject) {
foodImageView.shake()
actionButton.shake()
}
}
🤔
// 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")
}
}
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()
}
}
// Shakeable.swift
import UIKit
protocol Shakeable { }
extension Shakeable where Self: UIView {
func shake() {
// implementation code
}
}
class FoodImageView: UIImageView, Shakeable {
}
class ActionButton: UIButton, Shakeable {
}
class FoodImageView: UIImageView, Shakeable, Dimmable {
}
class FoodImageView: UIImageView, Dimmable {
}
Transparent View Controllers
and Dim Backgrounds
totem.training
👯👯👯👯👯
POP (UITable)ViewControllers
// FoodLaLaViewController
override func viewDidLoad() {
super.viewDidLoad()
let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil)
tableView.registerNib(foodCellNib,
forCellReuseIdentifier: "FoodTableViewCell")
}
let foodCellNib = UINib(nibName: String(FoodTableViewCell), bundle: nil)
tableView.registerNib(foodCellNib,
forCellReuseIdentifier: String(FoodTableViewCell))
protocol ReusableView: class {}
extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(self)
}
}
extension UITableViewCell: ReusableView { }
FoodTableViewCell.reuseIdentifier
// FoodTableViewCell
let foodCellNib = UINib(nibName: "FoodTableViewCell",
bundle: nil)
tableView.registerNib(foodCellNib,
forCellReuseIdentifier: FoodTableViewCell.reuseIdentifier)
protocol NibLoadableView: class { }
extension NibLoadableView where Self: UIView {
static var nibName: String {
return String(self)
}
}
extension FoodTableViewCell: NibLoadableView { }
FoodTableViewCell.nibName
// "FoodTableViewCell"
let foodCellNib = UINib(nibName: FoodTableViewCell.nibName, bundle: nil)
tableView.registerNib(foodCellNib,
forCellReuseIdentifier: FoodTableViewCell.reuseIdentifier)
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)
}
}
let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil)
tableView.registerNib(foodCellNib,
forCellReuseIdentifier: "FoodTableViewCell")
tableView.register(FoodTableViewCell)
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
}
}
guard let cell = tableView.dequeueReusableCellWithIdentifier(“FoodTableViewCell", forIndexPath: indexPath)
as? FoodTableViewCell
else {
fatalError("Could not dequeue cell with identifier: FoodTableViewCell")
}
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell
if indexPath.row == 0 {
return tableView.dequeueReusableCell(forIndexPath: indexPath) as DesertTableViewCell
}
return tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell
iOS Cell Registration & Reusing with
Swift Protocol Extensions and
Generics
medium.com/@gonzalezreal
Protocol-Oriented Segue
Identifiers in Swift
natashatherobot.com
POP Networking
struct FoodService {
func get(completionHandler: Result<[Food]> -> Void) {
// make asynchronous API call
// and return appropriate result
}
}
enum Result<T> {
case Success(T)
case Failure(ErrorType)
}
struct FoodService {
func get(completionHandler: Result<[Food]> -> Void) {
// make asynchronous API call
// and return appropriate result
}
}
// 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)
}
}
}
View Controller Tests?!!! 😱
// 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)
}
}
}
// FoodLaLaViewController
func getFood(fromService service: FoodService) {
service.getFood() { [weak self] result in
// handle result
}
}
// FoodLaLaViewControllerTests
func testFetchFood() {
viewController.getFood(fromService: FoodService())
// 🤔 now what?
}
struct FoodService {
func get(completionHandler: Result<[Food]> -> Void) {
// make asynchronous API call
// and return appropriate result
}
}
protocol Gettable {
associatedtype T
func get(completionHandler: Result<T> -> Void)
}
struct FoodService: Gettable {
func get(completionHandler: Result<[Food]> -> Void) {
// make asynchronous API call
// and return appropriate result
}
}
// 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)
}
}
}
// FoodLaLaViewControllerTests
class Fake_FoodService: Gettable {
var getWasCalled = false
func get(completionHandler: Result<[Food]> -> Void) {
getWasCalled = true
completionHandler(Result.Success(food))
}
}
// FoodLaLaViewControllerTests
func testFetchFood() {
let fakeFoodService = Fake_FoodService()
viewController.getFood(fromService: fakeFoodService)
XCTAssertTrue(fakeFoodService.getWasCalled)
XCTAssertEqual(viewController.dataSource.count, food.count)
XCTAssertEqual(viewController.dataSource, food)
}
Update: View Controller Data
Injection with Storyboards and
Segues in Swift
natashatherobot.com
Protocols with Associated Types
Alexis Gallagher
2015.funswiftconf.com
😎😎😎
Practical POP
• View
• (UITable)ViewController
• Networking
Beyond Crusty: Real-World
Protocols
Rob Napier
thedotpost.com
Blending Cultures: The Best of
Functional, Protocol-Oriented, and
Object-Oriented Programming
Daniel Steinberg
realm.io
@NatashaTheRobot

Practical Protocol-Oriented-Programming

  • 1.
  • 2.
    Protocol-Oriented Programming inSwift Dave Abrahams Professor of Blowing-Your-Mind
  • 3.
    –- Professor ofBlowing-Your-Mind "Swift Is a Protocol-Oriented Programming Language"
  • 5.
    Practical POP • View •(UITable)ViewController • Networking
  • 6.
  • 9.
    // FoodImageView.swift import UIKit classFoodImageView: 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 classViewController: UIViewController { @IBOutlet weak var foodImageView: FoodImageView! @IBAction func onShakeButtonTap(sender: AnyObject) { foodImageView.shake() } }
  • 11.
  • 13.
    // ShakeableButton.swift import UIKit classActionButton: 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.
  • 16.
    // UIViewExtension.swift import UIKit extensionUIView { 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() } }
  • 19.
    // Shakeable.swift import UIKit protocolShakeable { } 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.
  • 23.
    Transparent View Controllers andDim Backgrounds totem.training
  • 24.
  • 25.
  • 27.
    // FoodLaLaViewController override funcviewDidLoad() { 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 { funcregister<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.
  • 38.
    extension UITableView { funcdequeueReusableCell<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 inSwift natashatherobot.com
  • 45.
  • 46.
    struct FoodService { funcget(completionHandler: Result<[Food]> -> Void) { // make asynchronous API call // and return appropriate result } }
  • 47.
    enum Result<T> { caseSuccess(T) case Failure(ErrorType) }
  • 48.
    struct FoodService { funcget(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.
  • 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(fromServiceservice: FoodService) { service.getFood() { [weak self] result in // handle result } }
  • 53.
    // FoodLaLaViewControllerTests func testFetchFood(){ viewController.getFood(fromService: FoodService()) // 🤔 now what? }
  • 54.
    struct FoodService { funcget(completionHandler: Result<[Food]> -> Void) { // make asynchronous API call // and return appropriate result } }
  • 55.
    protocol Gettable { associatedtypeT 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 funcviewDidLoad() { 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 ControllerData Injection with Storyboards and Segues in Swift natashatherobot.com
  • 61.
    Protocols with AssociatedTypes Alexis Gallagher 2015.funswiftconf.com
  • 62.
  • 63.
    Practical POP • View •(UITable)ViewController • Networking
  • 64.
  • 65.
    Blending Cultures: TheBest of Functional, Protocol-Oriented, and Object-Oriented Programming Daniel Steinberg realm.io
  • 66.