real world generics in swift

28
REAL WORLD GENERICS IN SWIFT @VADYMMARKOV

Upload: vadym-markov

Post on 15-Apr-2017

216 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Real World Generics In Swift

REAL WORLD GENERICS IN SWIFT

@VADYMMARKOV

Page 2: Real World Generics In Swift

ABOUT ME

let me = Developer( name: "Vadym Markov", specialisation: "iOS", company: "Hyper Interaktiv AS", interests: [ "Technology", "Open Source", "Music" ] )

Page 3: Real World Generics In Swift

GENERICS, HUH?

Page 4: Real World Generics In Swift

DEFINITION

▸Generic programming is a way to write functions and data types without being specific about the types they use

▸Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define

Page 5: Real World Generics In Swift

GENERICS AND SWIFT

▸Generics are one of the most powerful features of Swift

▸Much of the Swift standard library is built with generic code

▸ You’ve already been using generics, even if you didn’t realise it

Page 6: Real World Generics In Swift

SWIFT STANDARD LIBRARY

// Array var array = [String]()

// Dictionary var dictionary = [String: String]()

// Enum var string1 = Optional<String>.Some("Swift") var string2 = Optional<String>.None == nil

Page 7: Real World Generics In Swift

THE PROBLEMstruct IntQueue {

private var items = [Int]()

mutating func enqueue(item: Int) { items.append(item) }

mutating func dequeue() -> Int? { return items.removeFirst() }

func peek() -> Int? { return items.first }

func isEmpty() -> Bool { return items.isEmpty } }

Page 8: Real World Generics In Swift

SOLUTION?struct AnyQueue {

private var items = [Any]()

mutating func enqueue(item: Any) { items.append(item) }

mutating func dequeue() -> Any? { return items.removeFirst() }

func peek() -> Any? { return items.first }

func isEmpty() -> Bool { return items.isEmpty } }

Page 9: Real World Generics In Swift

SO NOW WE ARE ABLE TO PUSH STRINGS TO THE STACK, RIGHT?

YES BUT…

▸We are losing type safety

▸We also need to do a lot of casting

Page 10: Real World Generics In Swift

SOLUTION!struct Queue<T> {

private var items = [T]()

mutating func enqueue(item: T) { items.append(item) }

mutating func dequeue() -> T? { return items.removeFirst() }

func peek() -> T? { return items.first }

func isEmpty() -> Bool { return items.isEmpty } }

Page 11: Real World Generics In Swift

SOLUTION!

var stringQueue = Queue<String>() stringQueue.enqueue("String") stringQueue.dequeue() // "String"

var intQueue = Queue<Int>() intQueue.enqueue(1) intQueue.dequeue() // 1

Page 12: Real World Generics In Swift

WELCOME TO REALITY

Page 13: Real World Generics In Swift

GENERIC TYPES

class DataSource<Model, Cell: UITableViewCell> : NSObject, UITableViewDataSource, UITableViewDelegate {

let cellIdentifier: String let configureCell: (Model, Cell) -> Void var cellHeight: CGFloat = 64 var items = [Model]() var action: (Model -> Void)?

init(cellIdentifier: String, configureCell: (Model, Cell) -> Void) { self.cellIdentifier = cellIdentifier self.configureCell = configureCell }

// ...

Page 14: Real World Generics In Swift

// MARK: - UITableViewDataSource

func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 }

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count }

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCellWithIdentifier( cellIdentifier, forIndexPath: indexPath)

let item = items[indexPath.item]

if let cell = cell as? Cell { configureCell(item, cell) } return cell }

Page 15: Real World Generics In Swift

// MARK: - UITableViewDelegate

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return cellHeight }

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

tableView.deselectRowAtIndexPath(indexPath, animated: true)

guard let action = action else { return }

let item = items[indexPath.row] action(item) }

Page 16: Real World Generics In Swift

class TeamController: UITableViewController {

static let reusableIdentifier = "TeamCellIdentifier"

var developers = [Developer]()

lazy var dataSource: DataSource<Developer, TableViewCell> = { [unowned self] in let dataSource = DataSource( cellIdentifier: TeamController.reusableIdentifier, configureCell: self.configureCell)

dataSource.action = self.selectCell

return dataSource }()

// ...

func configureCell(developer: Developer, cell: TableViewCell) { cell.textLabel?.text = developer.name cell.imageView?.image = UIImage(named: "placeholder")) }

func selectCell(developer: Developer) { UIApplication.sharedApplication().openURL(developer.githubURL) } }

Page 17: Real World Generics In Swift

protocol CacheAware { func object<T: Cachable>(key: String, completion: (object: T?) -> Void) }

class DiskStorage: CacheAware {

func object<T: Cachable>(key: String, completion: (object: T?) -> Void) { // ... } }

// String must conform Cachable

let storage = DiskStorage(name: "Unknown") storage.add("string", object: "My string”) storage.object("string") { (object: String?) in }

GENERIC FUNCTIONS

Page 18: Real World Generics In Swift

struct ViewModel: Mappable {

// ...

var meta = [String : AnyObject]()

func meta<T>(key: String, _ defaultValue: T) -> T { return meta[key] as? T ?? defaultValue } }

let modelFoo = ViewModel(title: "foo", meta: ["id" : 1])

modelFoo.meta("id", 0) // 1 modelFoo.meta("foo", "bar") // bar

GENERIC FUNCTIONS

Page 19: Real World Generics In Swift

WHAT ABOUT PROTOCOLS?

▸Protocols in Swift cannot be defined generically using type parameters.

▸ Instead, protocols can define what are known as associated types.

Page 20: Real World Generics In Swift

ASSOCIATED TYPES protocol Cachable { typealias CacheType

static func decode(data: NSData) -> CacheType? func encode() -> NSData? }

extension String: Cachable {

typealias CacheType = String

static func decode(data: NSData) -> CacheType? { // ... }

func encode() -> NSData? { // ... } }

Page 21: Real World Generics In Swift

ASSOCIATED TYPES

let list: [Cachable] = []

▸ Cachable represents a set of types rather than a single type

▸ In an array of Cachable, you are not be able to say anything about the return type of the decode() method

Page 22: Real World Generics In Swift

HOW COULD IT LOOK LIKE?

let list : [Cachable<String>] = []

let’s imagine Swift supporting generic parameterised protocols

Page 23: Real World Generics In Swift

MORE EXAMPLES: OPERATORS infix operator ?= { associativity right precedence 90 }

public func ?=<T>(inout left: T, right: T?) { guard let value = right else { return } left = value }

public func ?=<T>(inout left: T?, right: T?) { guard let value = right else { return } left = value }

// ...

func testIfLetAssignment() { let hyper = NSURL(string: "hyper.no")! let faultyURL = NSURL(string: "\\/http")

var testURL: NSURL? testURL ?= hyper // "hyper.no" testURL ?= faultyURL // "hyper.no" }

Page 24: Real World Generics In Swift

MORE EXAMPLES: PROTOCOL EXTENSIONS

protocol Queueable { func process() -> Bool }

extension Array where Element : Queueable {

mutating func processQueue(from: Int, to: Int, process: ((element: Element) -> Void)) { // ... } }

var testQueue = [Object(), Object()] testQueue.processQueue()

Page 25: Real World Generics In Swift

MORE EXAMPLES: ENUMS enum Result<T, ErrorType> { case Success(T) case Failure(ErrorType) }

struct UFO { enum Error: ErrorType { case NotFound } }

func recognizeUFO(completion: Result<UFO, UFO.Error> -> Void) { var ufo: UFO?

// Some async complex calculations

if let result = ufo { completion(Result.Success(result)) } else { completion(Result.Failure(UFO.Error.NotFound)) } }

Page 26: Real World Generics In Swift

WHY GENERICS?

▸ Type safety

▸ Less code duplication

▸No type casting hell

▸ Public API flexibility

▸Abstraction is fun

Page 27: Real World Generics In Swift

RESOURCES

▸ https://developer.apple.com/swift/

▸ https://github.com/hyperoslo/Orchestra

▸ https://github.com/hyperoslo/Cache

▸ https://github.com/hyperoslo/Spots

▸ https://github.com/hyperoslo/Sugar

Page 28: Real World Generics In Swift

QUESTIONS?

HTTPS://GITHUB.COM/VADYMMARKOV

@VADYMMARKOV