Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Road To Damascus, or FRP and Me

Abizer Nasir
September 14, 2017

The Road To Damascus, or FRP and Me

Presentation I gave at NSSpain 2017 about my thoughts on Functional Reactive Program and whether I should use it.

Abizer Nasir

September 14, 2017
Tweet

More Decks by Abizer Nasir

Other Decks in Technology

Transcript

  1. The Road to Damascus
    or, FRP and me
    Abizer Nasir | @abizern | abizern.org

    View Slide

  2. I like to keep things simple
    • MVC
    • Dependency Injection, protocols, value types
    • Storyboards
    • Core Data

    View Slide

  3. Functional Reactive Programming
    • Reactive: Treats data as a flow over time rather than individual
    events. A sequence.
    • Functional: map and filter and reduce can be applied to these
    sequences - the things we like about Swift

    View Slide

  4. Choices
    • RxSwift
    • ReactiveCocoa
    • PromiseKit
    • ReSwift

    View Slide

  5. Functional Reactive Programming
    (FRP) is not a solution for Massive
    View Controllers.

    View Slide

  6. Not a good starting point
    “The RepositoryListViewController is also a delegate and a data
    source for the table view. It handles the navigation, formats model
    data to display and performs network requests. Wow, a lot of
    responsibilities for just one View Controller!”

    View Slide

  7. FRP is not just a wrapper around
    KVO

    View Slide

  8. FRP “flattens” asynchronous code
    — Me.

    View Slide

  9. An Observable is a sequence of
    events.

    View Slide

  10. Events only have three states
    public enum Event {
    case next(Element)
    case error(Swift.Error)
    case completed
    }

    View Slide

  11. Events are emitted when an Observable is
    subscribed to
    Think of subscribing as repeatedly calling next() an an Iterator.

    View Slide

  12. View Slide

  13. RxMarbles.com

    View Slide

  14. RxMarbles.com

    View Slide

  15. RxMarbles.com

    View Slide

  16. RxMarbles.com

    View Slide

  17. RxMarbles.com

    View Slide

  18. “Flattens Asynchronous Code”
    By that I mean they are treated as the same thing.
    • A single value injected into a View Controller
    • The text in a Text View
    • The response from a network request.

    View Slide

  19. Validate UserName & Password, Classic
    final class ViewController: UIViewController {
    @IBOutlet private var name: UITextField! {
    didSet { name.addTarget(self, action: #selector(validate(_:)), for: .editingChanged) }
    }
    @IBOutlet private var password: UITextField!{
    didSet { password.addTarget(self, action: #selector(validate(_:)), for: .editingChanged)}
    }
    @IBOutlet private var confirm: UIButton!
    @objc private func validate(_ sender: UITextField) {
    let name = self.name.text ?? ""
    let password = self.password.text ?? ""
    confirm.isEnabled = !name.isEmpty && !password.isEmpty
    }
    }

    View Slide

  20. Validate UserName & Password, RxSwift
    class LoginViewController: UIViewController {
    @IBOutlet private var name: UITextField!
    @IBOutlet private var password: UITextField!
    @IBOutlet private var confirm: UIButton!
    let disposeBag = DisposeBag()
    override func viewDidLoad() {
    super.viewDidLoad()
    let username = name.rx.text.orEmpty.asObservable()
    let password = password.rx.text.orEmpty.asObservable()
    confirmButtonValid(username: username, password: password)
    .bind(to: confirmButton.rx.isEnabled)
    .disposed(by: disposeBag)
    }
    func confirmButtonValid(username: Observable, password: Observable) -> Observable {
    return Observable.combineLatest(username, password { (username, password) in
    return !username.isEmpty && !password.isEmpty
    }
    }
    }

    View Slide

  21. func confirmButtonValid(username: Observable, password: Observable) -> Observable {
    return Observable.combineLatest(username, password { (username, password) in
    return !username.isEmpty && !password.isEmpty
    }
    }

    View Slide

  22. Observable can be
    - .next(Bool)
    - .error(Error)
    - .completed
    Looks a lot like Result
    - .success(Bool)
    - .failure(Error)

    View Slide

  23. Composition
    searchBar.rx.text
    .orEmpty
    .filter { $0.characters.count > 3 }
    .debounce { 0.5, scheduler: MainScheduler.instance }
    .map { query in
    let url = URLBuilder.urlFor(query)
    return URLRequest(url: url)
    .flatMapLatest{ request in
    return URLSession.shared.rx.json(request: request)
    .catchErrorJustReturn([])
    }
    .map { json -> [Model] in
    guard let items = json as? [[String: Any]] else { return [] }
    return items.flatMap { Model.init }
    }
    .bindTo(tableView.rx.items) { tableView, row, model in
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
    cell.configureWith(model)
    return cell
    }
    .disposed(by: disposeBag)
    h/t Marin Todorov @icanzilb

    View Slide

  24. The code is in one place. It is
    readable, the order is determined
    and easy to reason about.

    View Slide

  25. Magic?
    • Not really. The code is still there.
    • Boilerplate is hidden away

    View Slide

  26. It’s a Big DSL
    • RxSwift for FRP
    • RxBindings for working with UI elements
    • RxTest

    View Slide

  27. It’s a Large Dependency
    • Active project with many contributors and users.
    • Easily installed with CocoaPods or Carthage, but these are the
    days of Xcode transitions.
    • You don’t have to use it everywhere, but if you’ve added it...

    View Slide

  28. Where Am I on the road?
    Well, I’m still on it.

    View Slide

  29. RxSwift
    https://store.raywenderlich.com/
    products/rxswift

    View Slide

  30. Thank You
    !
    Abizer Nasir | @abizern | abizern.org

    View Slide