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 full-size slide

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

    View full-size 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 full-size slide

  4. Choices
    • RxSwift
    • ReactiveCocoa
    • PromiseKit
    • ReSwift

    View full-size slide

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

    View full-size 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 full-size slide

  7. FRP is not just a wrapper around
    KVO

    View full-size slide

  8. FRP “flattens” asynchronous code
    — Me.

    View full-size slide

  9. An Observable is a sequence of
    events.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  12. RxMarbles.com

    View full-size slide

  13. RxMarbles.com

    View full-size slide

  14. RxMarbles.com

    View full-size slide

  15. RxMarbles.com

    View full-size slide

  16. RxMarbles.com

    View full-size slide

  17. “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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  22. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  26. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide