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

Building Fabric.app with ReactiveCocoa

Building Fabric.app with ReactiveCocoa

Overview of how the Fabric App was built by taking advantage of some of ReactiveCocoa's features.

Javier Soto

June 15, 2016
Tweet

More Decks by Javier Soto

Other Decks in Programming

Transcript

  1. Building Fabric.app with Reac5veCocoa
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 1

    View Slide

  2. Intro
    @Javi
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 2

    View Slide

  3. Outline
    • Intro
    • History
    • Contribu.ng to Reac.veCocoa
    • Fabric App Architecture
    • RAC examples from Fabric App
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 3

    View Slide

  4. Intro: What is Fabric.app?
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 4

    View Slide

  5. What is Fabric.app?
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 5

    View Slide

  6. History
    h"ps:/
    /github.com/blog/1107-reac8vecocoa-for-a-be"er-world
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 6

    View Slide

  7. History
    • Reac&veCocoa 1: May 2012 (Objec&ve-C)
    • Reac&veCocoa 2: September 2013 (Objec&ve-C)
    • Reac&veCocoa 3: September 2015 ([email protected] 1)
    • Reac&veCocoa 4: January 2016 ([email protected] 2)
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 7

    View Slide

  8. "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 8

    View Slide

  9. Contribu)ng to Reac)veCocoa
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 9

    View Slide

  10. Contribu)ng to Reac)veCocoa: Coding
    • Refactoring
    • New tests
    • Proposing new operators
    • Compa5bility with new Swi; versions
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 10

    View Slide

  11. Contribu)ng to Reac)veCocoa: Other ways!
    • Replying to issues
    • Wri1ng / improving docs
    • Helping other users
    • Evangelizing
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 11

    View Slide

  12. Fabric App Architecture
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 12

    View Slide

  13. Fabric App Architecture
    • FabricAPI.framework:
    • Networking
    • Models
    • Fabric App:
    • View Controllers
    • View Models
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 13

    View Slide

  14. Examples of Usage of Reac0veCocoa in the
    Fabric App
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 14

    View Slide

  15. Examples - Networking
    final class AuthenticatedFabricAPI {
    func applications() -> SignalProducer<[Application], FabricAPIError> {
    return apiNetworking.requestJSONProducer(
    URL: APIURL(path: "api/v3/projects"),
    method: .GET
    )
    .attemptMap(Application.decodeObjectsInJSON)
    .observeOn(UIScheduler())
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 15

    View Slide

  16. DataLoadState
    final class MyViewModel {
    var data: MyEntity?
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 16

    View Slide

  17. DataLoadState
    enum DataLoadState {
    case Loading
    case Failed
    case Loaded(DataType)
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 17

    View Slide

  18. DataLoadState
    enum DataLoadState {
    case Loading
    case Failed
    case Loaded(DataType)
    }
    extension SignalProducerType {
    func materializeToLoadState() -> SignalProducer, NoError> {
    let producer = self
    .map(DataLoadState.Loaded)
    .startWithValue(DataLoadState.Loading)
    return producer.ignoreErrors(replacementValue: DataLoadState.Failed)
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 18

    View Slide

  19. extension SignalProducerType where Value: DataLoadState {
    func ignoreLoadingAndErrorsAfterSuccess() -> SignalProducer, Error> {
    var hasSuccededOnce = false
    return self.filter { value in
    defer {
    if value.success {
    hasSuccededOnce = true
    }
    }
    return !hasSuccededOnce || value.success
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 19

    View Slide

  20. Examples - View Models
    typealias ApplicationLoadState = DataLoadState<[Application]>
    final class ApplicationListViewModel {
    let applications: AnyProperty
    private let applicationsMutableProperty = MutableProperty(ApplicationLoadState.loading())
    init(fabricAPI: AuthenticatedFabricAPI) {
    self.applications = AnyProperty(self.applicationsMutableProperty)
    self.applicationsMutableProperty <~ fabricAPI.applications().materializeToLoadState()
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 20

    View Slide

  21. Examples - Consuming Data From a View Model
    self.viewModel.applications.producer.startWithNext { applicationsLoadState in
    switch applicationsLoadState {
    case .Loading:
    label.text = "Loading..."
    case .Failed:
    label.text = "Error loading applications!"
    case .Loaded(let applications):
    reloadTableView(applications: applications)
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 21

    View Slide

  22. Reac%veCocoa Extensions
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 22

    View Slide

  23. extension SignalProducerType {
    func startWithValue(value: Value) -> SignalProducer {
    return SignalProducer(value: value).concat(self.producer)
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 23

    View Slide

  24. Reac%veCocoa Extensions
    extension SignalProducerType {
    func startWithNil() -> SignalProducer {
    return self
    .map(Optional.init)
    .startWithValue(nil)
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 24

    View Slide

  25. Reac%veCocoa Extensions
    extension SignalProducerType {
    func ignoreErrors(
    replacementValue replacementValue: Self.Value? = nil
    ) -> SignalProducer {
    return self.flatMapError { _ in
    return replacementValue.map(SignalProducer.init) ?? .empty
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 25

    View Slide

  26. Reac%veCocoa Extensions
    extension SignalProducerType {
    func failRandomly(withError error: Self.Error) -> SignalProducer {
    return self.attemptMap { value in
    let shouldFail = arc4random() % 3 == 0
    return shouldFail ? Result(error: error) : Result(value: value)
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 26

    View Slide

  27. Reac%veCocoa Extensions
    extension NSProcessInfo {
    var lowPowerModelEnabledProducer: SignalProducer {
    return NSNotificationCenter.defaultCenter()
    .rac_notifications(NSProcessInfoPowerStateDidChangeNotification, object: nil)
    .map { _ in return () }
    .startWithValue(())
    .map { [unowned self] _ in
    return self.lowPowerModeEnabled
    }
    .skipRepeats(==)
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 27

    View Slide

  28. Reac%veCocoa Extensions
    let shouldReload = combineLatest(
    viewIsOnScreen,
    NSProcessInfo.processInfo().lowPowerModelEnabledProducer.map { !$0 }
    ).map { $0 && $1 }
    let reloadPeriodically = shouldReload
    .flatMap(.Latest) { [unowned self] shouldReload in
    return shouldReload ?
    timer(30, onScheduler: scheduler).map { _ in () }
    : .empty
    }
    let request = reloadPeriodically.flatMap(.Latest) { someAPIRequest }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 28

    View Slide

  29. Reac%veCocoa Extensions
    extension SignalProducerType {
    func continueWhenApplicationIsBackgrounded(
    taskName taskName: String,
    timeoutError: Self.Error
    ) -> SignalProducer {
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 29

    View Slide

  30. Reac%veCocoa Extensions
    extension SignalProducerType {
    func repeatWith(
    producer: SignalProducer<(), NoError>,
    throttleWithInterval: NSTimeInterval,
    onScheduler scheduler: DateSchedulerType
    ) -> SignalProducer {
    return SignalProducer(value: ()).concat(producer)
    .throttle(throttleWithInterval, onScheduler: scheduler)
    .promoteErrors(Error)
    .flatMap(.Concat) { _ in
    return self.producer
    }
    }
    }
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 30

    View Slide

  31. Reac%veCocoa Extensions
    self.api.request(parameterFoo: bar)
    .repeatWith(
    viewWillAppearProducer,
    throttleWithInterval: 60,
    onScheduler: QueueScheduler.mainQueueScheduler
    )
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 31

    View Slide

  32. Conclusion
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 32

    View Slide

  33. Ques%ons?
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 33

    View Slide

  34. Thank you! <3 !
    See you next year!
    "Building Fabric.app with Reac6veCocoa" - Javier Soto. RACDC2016 - April 2016 34

    View Slide