Date post: | 16-Apr-2017 |
Category: |
Technology |
Upload: | natasha-murashev |
View: | 59,740 times |
Download: | 0 times |
@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
Genericsmedium.com/@gonzalezreal
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 Swiftnatashatherobot.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 ProgrammingDaniel Steinberg
realm.io
@NatashaTheRobot