introduction to functional reactive programming in swift · introduction to functional reactive...

55
Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1

Upload: others

Post on 03-Jun-2020

50 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Introduction to Functional Reactive

Programming in Swift

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1

Page 2: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

• Sebastian Grail

• iOS Developer at Canva

• @saibhai

• github: sebastiangrail

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 2

Page 3: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Why?

• Cocoa APIs are stateful

• A lot of existing imperative Objective-C code

• Target/action, delegation and KVO are hard to understand

• Easy to spread one concern over multiple places

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 3

Page 4: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

What is Reactive Programming?

• Reactive programming abstracts changes over time into signals

• Those signals send events until they either complete or error out

• Signals can be observed

• Observers receive new values from their signals

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 4

Page 5: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning single value

• Synchronous

T -> U

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 5

Page 6: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning single value

• Synchronous

T -> U

• Asynchronous

(T, U -> ()) -> ()

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 6

Page 7: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning single value

• Synchronous

T -> U

• Asynchronous

T -> Future<U>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 7

Page 8: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 8

Page 9: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

• Asynchronous

T -> Future<[U]>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 9

Page 10: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

• Asynchronous

T -> [Future<U>]

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 10

Page 11: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

• Asynchronous

(T, U -> ()) -> ()

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 11

Page 12: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

• Asynchronous

(T, U -> (), () -> ()) -> ()

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 12

Page 13: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

• Asynchronous

(T, U -> (), () -> (), E -> ()) -> ()

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 13

Page 14: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Function returning multiple values

• Synchronous

T -> [U]

• Asynchronous

T -> Signal<U, E>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 14

Page 15: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Where can Signals be used?

• Button: Replace target/action with Signal

• Gesture recognizer: Signal sends gesture state changes

• Network request: Signal that sends data when available

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 15

Page 16: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Hot Signal

• Always on

• Subscription does not trigger side effects

• All observers get the same events

• Usually never completes

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 16

Page 17: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Cold Signal

• Short life cycle

• Subscription triggers side effects

• Every subscriber gets its own events

• Usually completes after work is done

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 17

Page 18: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Reactive Cocoa• Original Objective-C API started in 2012

• Inspired by Reactive Extensions for .Net

• RAC 3 introduces new Swift API

• Still in beta!

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 18

Page 19: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Event

enum Event<T, E : ErrorType> { case Next(T) case Error(E) case Completed case Interrupted}

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 19

Page 20: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Signal

final class Signal<T, E : ErrorType>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 20

Page 21: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Creating a Signal

init(generator: SinkOf<Event<T, E>> -> Disposable?)

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 21

Page 22: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Creating a Signal

let signal = Signal<String, NoError> { sink in NSOperationQueue().addOperationWithBlock { while true { sleep(1) sendNext(sink, "Fire!") } } return nil}

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 22

Page 23: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Observing a signal

func observe<T, E>( next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) (signal: Signal<T, E>) -> Disposable?

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 23

Page 24: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Observing a signal

signal |> observe(next: println)signal |> observe(next: println, error: handleError)singal |> observe(completed: signalCompleted)

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 24

Page 25: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Signals are hot

let signal = Signal<String, NoError> { sink in NSOperationQueue().addOperationWithBlock { while true { sleep(1) println("sending") sendNext(sink, "Fire!") } } return nil}signal |> observe(next: println)signal |> observe(next: println)// sending - Fire! - Fire! - sending - Fire! - Fire!

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 25

Page 26: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

SignalProducer

struct SignalProducer<T, E : ErrorType>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 26

Page 27: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Creating a SignalProducer

init(startHandler: (SinkOf<Event<T, E>>, CompositeDisposable) -> ())

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 27

Page 28: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Creating a SignalProducer

let producer = SignalProducer<String, NoError> { sink, disposable in sendNext(sink, "Fire!") sendCompleted(sink)}

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 28

Page 29: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Starting a SignalProducer

func start<T, E>( next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) (producer: SignalProducer<T, E>) -> Disposable

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 29

Page 30: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Starting a SignalProducer

producer |> start(next: println)producer |> start(next: println, error: handleError)producer |> start(completed: signalCompleted)

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 30

Page 31: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

SignalProducers are cold

let producer = SignalProducer<String, NoError> { sink, disposable in println("sending") sendNext(sink, "Fire!") sendCompleted(sink) disposable.addDisposable { println("disposing") }}producer |> start(next: println)producer |> start(next: println)// "sending" - "Fire" - "disposing" - "sending" - "Fire" - "disposing"

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 31

Page 32: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Other ways to create Signals

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 32

Page 33: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

MutableProperty<T>

• Encapsulates a mutable property

• Exposes a SignalProducer

let property = MutableProperty<Int>(0)...property <~ someSignal

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 33

Page 34: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

DynamicProperty

• Mostly for legacy Objective-C code

• Wraps a KVO property

DynamicProperty(object: someObject, keyPath: "keyPath") <~ someSignal

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 34

Page 35: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

RACSignal

• ReactiveCocoa 2 signal

• Can be converted using

toSignalProducer() ->SignalProducer<AnyObject?, NSError>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 35

Page 36: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

RACSignal

UITextField .rac_textSignal()

UIControl .rac_signalForControlEvents(UIControlEvents)

NSNotificationCenter .rac_addObserverForName(String, object: AnyObject)

UIGestureRecognizer .rac_gestureSignal

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 36

Page 37: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Composing Signals

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 37

Page 38: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Composing Signals• Functions to compose signals

• Defined both as methods and free functions

• Free functions are curried to work with |>

• Most functions defined for Signal

• Can be lifted to work on SignalProducers

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 38

Page 39: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

filter

func filter<T, E>(predicate: T -> Bool) (signal: Signal<T, E>) -> Signal<T, E>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 39

Page 40: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

map

func map<T, U, E>(transform: T -> U) (signal: Signal<T, E>) -> Signal<U, E>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 40

Page 41: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

mapError

func mapError<T, E, F>(transform: E -> F) (signal: Signal<T, E>) -> Signal<T, F>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 41

Page 42: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

flatten

func flatten<T, E>(strategy: FlattenStrategy) (producer: SignalProducer<SignalProducer<T, E>, E>) -> SignalProducer<T, E>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 42

Page 43: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

flatMap

func flatMap<T, U, E>(strategy: FlattenStrategy, transform: T -> SignalProducer<U, E>) (producer: SignalProducer<T, E>) -> SignalProducer<U, E>

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 43

Page 44: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

sampleOn

func sampleOn<T, E>(sampler: SignalProducer<(), NoError>) (producer: SignalProducer<T, E>) -> SignalProducer<T, E>

• Forwards the latest value from producer whenever sampler sends a Next event.

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 44

Page 45: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

combineLatest

func combineLatest<A, B, Error>(a: SignalProducer<A, Error>, b: SignalProducer<B, Error>) -> SignalProducer<(A, B), Error>

• Sends the latest value when either signal sends a value

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 45

Page 46: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Putting it all together

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 46

Page 47: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 47

Page 48: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

let name = nameField .rac_textSignal() .toSignalProducer() |> map { $0 as? String } |> ignoreNil |> discardError

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 48

Page 49: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

extension UITextField { func textSignal () -> SignalProducer<String, NSError> { return self.rac_textSignal() .toSignalProducer() |> map { $0 as? String } |> ignoreNil |> discardError }}

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 49

Page 50: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

let name = nameField.textSignal()let password = passwordField.textSignal()

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 50

Page 51: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

let tap = loginButton .rac_signalForControlEvents(.TouchUpInside) .toSignalProducer() |> discardError |> map { _ in () }

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 51

Page 52: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

let enabled = combineLatest( name |> map(not(isEmpty)), password |> map(not(isEmpty))) |> map({ $0 && $1 }) |> map { NSNumber(bool: $0) as AnyObject? } |> discardError

DynamicProperty(object: loginButton, keyPath: "enabled") <~ enabled

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 52

Page 53: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

login: (String, String) -> SignalProducer<User, NSError>

combineLatest(name, password) |> sampleOn(tap) |> flatMap(FlattenStrategy.Concat, login) |> on(error: showError) |> retryForever |> start(next: showUser)

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 53

Page 54: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Resources• github.com/ReactiveCocoa/ReactiveCocoa

• www.quora.com/ReactiveCocoa

• blog.scottlogic.com/ceberhardt

• nomothetis.svbtle.com/an-introduction-to-reactivecocoa

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 54

Page 55: Introduction to Functional Reactive Programming in Swift · Introduction to Functional Reactive Programming in Swift Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 1File Size:

Conclusion• FRP encapsulates side effects in a composable way

• Signals can replace many different OO patterns

• Still a lot of boilerplate code for bridging

• Most of it can be removed by extending existing APIs

Sebastian Grail / YOW! Lambda Jam 2015 / @saibhai 55