Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Functional Reactive Programming without Black Magic (UIKonf 2015)

3,401 views

Published on

While there are a lot of talks how ReactiveCocoa is kind of a silver bullet that solves almost every problem you throw at it, most people still think of it as a magic black box. This talk is about the basic concepts how write your own ReactiveCocoa in Swift. It also features an in-depth look into Results, Promises and Signals. And I promise not to use the scary M-word.

See by example how to implement a small app featuring networking, json parsing and table views using signals. Without black magic and by using great new language features like pattern matching, generics and the nice new closure syntax.

All content of this talk is from the perspective of an object oriented developer, therefore no prerequisites are necessary. In the end you will be able to judge yourself if you want to go reactive by using ReactiveCocoa, your own framework or no reactivity at all.

Published in: Software

Functional Reactive Programming without Black Magic (UIKonf 2015)

  1. 1. How to use Functional Reactive Programming without Black Magic
  2. 2. let me = Person(name: "Jens Ravens") @JensRavens GitHub: JensRavens nerdgeschoss.de swift.berlin
  3. 3. A short introduction to functional programming, the universe and everything.
  4. 4. In the beginning McIlroy created the unix pipe. And he saw it was good. ls | grep *.jpg | sort
  5. 5. Application Architecture
  6. 6. starting from a blank slate
  7. 7. implement to understand
  8. 8. And I promise not to use the scary M-word. me, 12 weeks ago.
  9. 9. How to train your monad.
  10. 10. –Saunders Mac Lane All told, a monad is just a monoid in the category of endofunctors. “
  11. 11. Monads are just Applicative Functors “
  12. 12. ls | grep *.jpg | sort
  13. 13. Buy it, use it, break it, fix it, Trash it, change it, mail - upgrade it. – Daft Punk, Technologic
  14. 14. Buy it; if error { //TODO: Handle me! } else { use it; if error { //TODO: Handle me! } else { break it; if error { //TODO: Handle me!
  15. 15. ls | grep *.jpg | sort
  16. 16. Monad something that defines map and bind
  17. 17. The optional Monad let string: String? = "World" let greeting = string.map{"Hello ($0)”} //Hello World extension Optional { func bind<U> (f: T->U?) -> U? { if let result = self.map({f($0)}) { return result } else { return nil } } }
  18. 18. The optional Monad let string: String? = "World" let greeting = string.map{"Hello ($0)”} //Hello World func greetOrNil(name: String)->String? { if name == "World" { return "Hello World" } else { return nil } } let greeting2 = string.bind(greetOrNil) //Hello World
  19. 19. The optional Monad extension Optional { func bind<U> (f: T->U?) -> U? { if let result = self.map({f($0)}) { return result } else { return nil } } } extension Optional { func bind<U> (f: T->Optional<U>) -> Optional<U> { switch self { case let .Some(value): return f(value) case .None: return nil } } }
  20. 20. The result Monad public enum Result<T> { case Success(Box<T>) case Error(NSError) }
  21. 21. The result Monad public enum Result<T> { … public func map<U>(f: T -> U) -> Result<U> { switch self { case let .Success(v): return .Success(Box(f(v.value))) case let .Error(error): return .Error(error) } } … }
  22. 22. The result Monad public enum Result<T> { … public func bind<U>(f: T -> Result<U>) -> Result<U> { switch self { case let .Success(v): return f(v.value) case let .Error(error): return .Error(error) } } … }
  23. 23. ls | grep *.jpg | sort Monad Transform
  24. 24. func parseString(data: NSData) -> Result<String> func parseJson(data: NSData) -> Result<[String: AnyObject]> func asyncGreeting(name: String, completion: Result<String>->Void)
  25. 25. public func bind<U>(f:(T, (Result<U>->Void))->Void) -> (Result<U>->Void)->Void { return { g in switch self { case let .Success(v): f(v.value, g) case let .Error(error): g(.Error(error)) } } }
  26. 26. Interstellar
  27. 27. ls | grep *.jpg | sort
  28. 28. public final class Signal<T> { private var value: Result<T>? private var callbacks: [Result<T> -> Void] = [] public func subscribe(f: Result<T> -> Void) { if let value = value { f(value) } callbacks.append(f) } public func update(value: Result<T>) { self.value = value self.callbacks.map{$0(value)} } }
  29. 29. public func map<U>(f: T -> U) -> Signal<U> { let signal = Signal<U>() subscribe { result in signal.update(result.map(f)) } return signal } public func bind<U>(f: T -> Result<U>) -> Signal<U> { let signal = Signal<U>() subscribe { result in signal.update(result.bind(f)) } return signal }
  30. 30. public func bind<U>(f: (T, (Result<U>->Void))->Void) -> Signal<U> { let signal = Signal<U>() subscribe { value in value.bind(f)(signal.update) } return signal }
  31. 31. pushing instead of pulling
  32. 32. the rise of custom operators
  33. 33. infix operator >>> { associativity left precedence 160 } public func >>> <A,B> (left: Signal<A>, right: A->Result<B>) -> Signal<B> { return left.bind(right) } public func >>> <A,B>(left: Signal<A>, right: (A, (Result<B>->Void))->Void) -> Signal<B>{ return left.bind(right) } public func >>> <A,B> (left: Signal<A>, right: A->B) -> Signal<B> { return left.map(right) }
  34. 34. ls | grep *.jpg | sort
  35. 35. ls | grep *.jpg | sort ls >>> grep("*.jpg") >>> sort
  36. 36. But what about Threads?
  37. 37. public final class Thread { public static func main<T>(a: T, completion: T->Void) { dispatch_async(dispatch_get_main_queue()) { completion(a) } } public static func background<T>(queue: dispatch_queue_t)(_ a: T, _ completion: T->Void) { dispatch_async(queue){ completion(a) } } } ls >>> Thread.background(queue) >>> grep("*.jpg") >>> sort >>> Thread.main
  38. 38. Extending UIKit to support Signals.
  39. 39. var SignalHandle: UInt8 = 0 extension UITextField { public var textSignal: Signal<String> { let signal: Signal<String> if let handle = objc_getAssociatedObject(self, &SignalHandle) as? Signal<String> { signal = handle } else { signal = Signal("") NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("textChanged:"), name: UITextFieldTextDidChangeNotification, object: self) objc_setAssociatedObject(self, &SignalHandle, signal, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) } return signal } public func textChanged(notification: NSNotification) { textSignal.update(.Success(Box(self.text))) } }
  40. 40. If it’s variable, it qualifies as a Signal.
  41. 41. ReactiveKitten It’s about gifs. And cats. And gifs of cats.
  42. 42. userTyping >>> createURL >>> loadRequest >>> parseData >>> mainThread >>> displayCats The transform, the cat and you.
  43. 43. import Interstellar private func request(path: String, completion: Result<NSData>->Void) { let url = NSURL(string: baseURL.stringByAppendingString(path))! let request = NSURLRequest(URL: url) session.dataTaskWithRequest(request){ data, response, error in if error != nil { completion(.Error(error)) } else if let response = response as? NSHTTPURLResponse { if response.statusCode >= 200 && response.statusCode<300 { completion(.Success(Box(data))) } else { completion(.Error(NSError(domain: "Networking", code: response.statusCode, userInfo: nil))) } } else { completion(.Error(NSError(domain: "Networking", code: 500, userInfo: nil))) } }.resume() }
  44. 44. private func parseJSON(data: NSData) ->Result<[String: AnyObject]> { var error: NSError? if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? [String: AnyObject] { return .Success(Box(json)) } else { return .Error(error!) } }
  45. 45. let imageSignal = gifSignal >>> getURL >>> Thread.background >>> loadFromCache >>> retryFromNetwork >>> Thread.main
  46. 46. class ViewController: UIViewController { var signal: Signal<[Gif]>! let searchBar = UISearchBar() override func viewDidLoad() { super.viewDidLoad() navigationItem.titleView = searchBar signal = searchBar.textSignal >>> Network().search() >>> Thread.main
  47. 47. github.com/jensravens/reactivekitten
  48. 48. What’s next()?
  49. 49. Functors, Applicatives and Monads in Pictures. http://adit.io/posts/2013-04-17- functors,_applicatives,_and_monads_in_pictures.html
  50. 50. The Introduction to RP you’ve been missing. https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
  51. 51. ReactiveCocoa 3
  52. 52. RxSwift
  53. 53. Interstellar available on Carthage jensravens/interstellar
  54. 54. Thank you. @JensRavens github.com/jensravens/interstellar

×