Git Product home page Git Product logo

reactor's Introduction

Build Status Carthage compatible CocoaPods Swift 2.2 Platforms iOS

Intro

Reactor provides a Model layer with minimum configuration. It makes use of the following elements to achieve that:

Reactor then uses flows (represented by the ReactorFlow<T>), that are typically seen in applications. For example:

  1. Does persisted data exist and is it valid?
  2. Yes: Use it ✅
  3. No: Fetch data from the network 1. Do we have an internet connection?
    1. Yes: make the Request.
    2. Parse the data and persist it (send any error that might occur) ✅
    3. Request failed: send an error ❌
    4. No: send an error ❌

This particular flow is provided out of the box by Reactor. In the future we will provide others.

Use Reactor if... ✅

  • You are are starting a new project. 🌳
  • You are in the process of defining your model layer. 🛠
  • You are creating a prototype or demo and you need something working quickly. 🚀
  • You don't feel comfortable enough with ReactiveCocoa and need some help with the setup. 🆒
  • You already have your Model layer in place, but you think Reactor could formalize your flows. ✨

Not suited if... ❌

  • You have an unusual flow, that doesn't really fit the ReactorFlow<T>. ⛔️
  • You already have a Model layer and you feel it wouldn't really benefit you in any way. 😞
  • You already have a parser and your own network library (Alamofire for example). 🔥
  • After checking the Advance usage, Reactor doesn't provide what you need. 😭😭

How to use

Carthage

github "MailOnline/Reactor"

Cocoapods

# Since there is already a podfile named `Reactor`, we are using `MOReactor`.
pod 'MOReactor', '~> 0.9'

Basic setup

For Reactor to work, you need to make sure your Model objects comply with the Mappable protocol. This protocol allows you to encode and decode an object. This is necessary for parsing the object (coming from the network) and storing it on disk.

Let's use the Author struct as an example (this can be found in the Unit tests). The first step is to make the Author struct compliant with the Mappable protocol:

struct Author {
  let name: String
}

extension Author: Mappable { 

  static func mapToModel(object: AnyObject) -> Result<Author, MappedError> {

  guard
    let dictionary = object as? [String: AnyObject],
    let name = dictionary["name"] as? String
    else { return Result(error: MappedError.Custom("Invalid dictionary @ \(Author.self)\n \(object)"))}

    let author = Author(name: name)

    return Result(value: author)
  }
 
  func mapToJSON() -> AnyObject {
    return ["name": self.name]
  }
}

Note: The above implementation, is just an example, you are free to use whatever means you prefer.

The first function mapToModel is what allows a model object to be created (JSON ➡️ Model). The second function mapToJSON is the inverse (Model ➡️ JSON).

The second step would be:

let baseURL = NSURL(string: "https://myApi.com")!
let reactor = Reactor<Author>(persistencePath: path, baseURL:baseURL)

Now that you have the reactor ready, it exposes two functions:

func fetch(resource: Resource) -> SignalProducer<T, Error>
func fetchFromNetwork(resource: Resource) -> SignalProducer<T, Error>

We find that these are the two most common scenarios:

  1. When you get to a new screen, you try to get some data. In this case, Reactor checks the persistence store first and if it fails it will fallback to the network.
  2. You want fresh data, so Reactor will use the network.

The final piece is the Resource, which is nothing more than a struct that encapsulates how the request will be made:

  • path
  • query
  • body
  • HTTP headers
  • HTTP method

Without Persistence

If it doesn't make sense to persist data, you pass the persistencePath as an empty string ("") or:

let baseURL = NSURL(string: "https://myApi.com")!
let reactor = Reactor<Author>(baseURL:baseURL)

In the future will make this explicit via a ReactorConfiguration. As for the mapToJSON function, you can simply return an NSNull:

func mapToJSON() -> AnyObject {
  return NSNull()
}

Advance Usage

In order to make most of Reactor, keep the following in mind (these are ReactorFlow<T>'s properties):

var networkFlow: Resource -> SignalProducer<T, Error>
var loadFromPersistenceFlow: Void -> SignalProducer<T, Error>
var saveToPersistenceFlow: T -> SignalProducer<T, Error>

All three properties are mutable (var) on purpose, so you can extend specific behaviours. For example, you might be interested in knowing why loadFromPersistenceFlow is failing and log it. With the default flow, this is not possible to do, because if loadFromPersistenceFlow fails, the network flow will kick in and the error is lost.

A way to accomplish this, is by creating a default flow and then extending it:

let baseURL = NSURL(string: "https://myApi.com")!
let reactor = Reactor<Author>(persistencePath: path, baseURL:baseURL)

let extendedPersistence = reactor.loadFromPersistenceFlow().on(failure: { error in print(error) })
reactor.loadFromPersistenceFlow =  { extendedPersistence }

You can further decompose the flow, since all the core pieces are exposed in the public API. More specifically:

The default flow provided by Reactor (Intro) is something you are welcome to use, but not tied to. Keep in mind the following when creating your own flows:

The Reactor<T>'s fetch function axiom:

  • the loadFromPersistenceFlow will always be called first. If it fails, fetchFromNetwork is called.

The Reactor<T>'s fetchFromNetwork function axiom:

  • the networkFlow will always be called first, if it succeeds it will be followed by saveToPersistenceFlow.

License

Reactor is licensed under the MIT License, Version 2.0. View the license file

Copyright (c) 2015 MailOnline

Header image by Henrique Macedo.

reactor's People

Contributors

ruiaaperes avatar dmcrodrigues avatar mluisbrown avatar

Watchers

James Cloos avatar Shantanu Desai avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.