All Projects β†’ RuiAAPeres β†’ Receiver

RuiAAPeres / Receiver

Licence: mit
Swift Β΅framework implementing the Observer pattern πŸ“‘

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Receiver

mobx-router5
Router5 integration with mobx
Stars: ✭ 22 (-90.76%)
Mutual labels:  observer, observable
react-mobx-router5
React components for routing solution using router5 and mobx
Stars: ✭ 58 (-75.63%)
Mutual labels:  observer, observable
EventEmitter
Simple EventEmitter with multiple listeners
Stars: ✭ 19 (-92.02%)
Mutual labels:  observer, observable
reactive-box
1 kB effective reactive core
Stars: ✭ 19 (-92.02%)
Mutual labels:  observer, observable
Dob
Light and fast πŸš€ state management tool using proxy.
Stars: ✭ 713 (+199.58%)
Mutual labels:  observable, observer
ng-observe
Angular reactivity streamlined...
Stars: ✭ 65 (-72.69%)
Mutual labels:  observer, observable
mutation-observer
A library for idiomatic use of MutationObserver with Angular
Stars: ✭ 32 (-86.55%)
Mutual labels:  observer, observable
state inspector
State change & method call logger. A debugging tool for instance variables and method calls.
Stars: ✭ 24 (-89.92%)
Mutual labels:  observer, observable
Observable
The easiest way to observe values in Swift.
Stars: ✭ 346 (+45.38%)
Mutual labels:  observable, observer
React Reactive Form
Angular like reactive forms in React.
Stars: ✭ 259 (+8.82%)
Mutual labels:  observable, observer
Oba
Observe any object's any change
Stars: ✭ 101 (-57.56%)
Mutual labels:  observable, observer
Ease
It's magic.
Stars: ✭ 1,213 (+409.66%)
Mutual labels:  observable, observer
Lightweightobservable
πŸ“¬ A lightweight implementation of an observable sequence that you can subscribe to.
Stars: ✭ 114 (-52.1%)
Mutual labels:  observable, observer
Harmony
A simple and flexible PHP middleware dispatcher based on PSR-7, PSR-11, and PSR-15
Stars: ✭ 144 (-39.5%)
Mutual labels:  microframework
Mag.js
MagJS - Modular Application Glue
Stars: ✭ 157 (-34.03%)
Mutual labels:  observable
Kefir
A Reactive Programming library for JavaScript
Stars: ✭ 1,769 (+643.28%)
Mutual labels:  observable
Awesome Rxjs
Awesome list of RxJS 5
Stars: ✭ 141 (-40.76%)
Mutual labels:  observable
Observable Slim
Observable Slim is a singleton that utilizes ES6 Proxies to observe changes made to an object and any nested children of that object. It is intended to assist with state management and one-way data binding.
Stars: ✭ 178 (-25.21%)
Mutual labels:  observable
Lit
World's smallest responsive πŸ”₯ css framework (395 bytes)
Stars: ✭ 1,866 (+684.03%)
Mutual labels:  microframework
Marble
Marble.js - functional reactive Node.js framework for building server-side applications, based on TypeScript and RxJS.
Stars: ✭ 1,947 (+718.07%)
Mutual labels:  observable

Receiver

codecov Build Status Swift 4.1 License MIT

  1. Intro
  2. 🌈 Enter Receiver! 🌈
  3. Adding as a Dependency πŸš€
  4. Basic usage 😎
  5. Operators πŸ€–
  6. Strategies
  7. Opinionated, in what way? πŸ€“
  8. Ok, so why would I use this? πŸ€·β€β™€οΈ

Intro

As a ReactiveSwift user myself, most of time, it's difficult to convince someone to just simply start using it. The reality, for better or worse, is that most projects/teams are not ready to adopt it:

  1. The intrinsic problems of adding a big dependency.
  2. The learning curve.
  3. Adapting the current codebase to a FRP mindset/approach.

Nevertheless, a precious pattern can still be used, even without such an awesome lib like ReactiveSwift. πŸ˜–

🌈 Enter Receiver! 🌈

Receiver is nothing more than an opinionated micro framework implementation of the Observer pattern (~120 LOC). Or, if you prefer, FRP without the F and a really small R (rP πŸ€”).

Adding as a Dependency πŸš€

Carthage

If you use Carthage to manage your dependencies, simply add Receiver to your Cartfile:

github "RuiAAPeres/Receiver" ~> 0.0.1

If you use Carthage to build your dependencies, make sure you have added Receiver.framework to the "Linked Frameworks and Libraries" section of your target, and have included them in your Carthage framework copying build phase.

CocoaPods

If you use CocoaPods to manage your dependencies, simply add Receiver to your Podfile:

pod 'Receiver', '~> 0.0.1'

Basic usage 😎

Let's begin with the basics. There are three methods in total. Yup, that's right.

1. Creating the Receiver

let (transmitter, receiver) = Receiver<Int>.make()

A receiver can never be created without an associated transmitter (what good would that be?)

2. Listening to an event πŸ“‘

This is how you observe events:

receiver.listen { cheezburgers in print("Can I haz \(cheezburgers) cheezburger. 🐈") }

As expected, you can do so as many times as you want:

receiver.listen { cheezburgers in print("Can I haz \(cheezburgers) cheezburger. 🐈") }


receiver.listen { cheezburgers in print("I have \(cheezburgers) cheezburgers and you have none!")}

And both handlers will be called, when an event is broadcasted. ⚑️

3. Broadcasting an event πŸ“»

This is how you send events:

transmitter.broadcast(1)

Operators πŸ€–

Receiver provides a set of operators akin to ReactiveSwift:

Strategies

If you are familiar with FRP, you must have heard about cold and hot semantics (if not don't worry! ☺️). Receiver provides all three flavours explicitly, when you initialize it, via make(strategy:). By default, the Receiver is .hot.

.cold ❄️:

let (transmitter, receiver) = Receiver<Int>.make(with: .cold)
transmitter.broadcast(1)
transmitter.broadcast(2)
transmitter.broadcast(3)

receiver.listen { wave in
    // This will be called with `wave == 1`
    // This will be called with `wave == 2`
    // This will be called with `wave == 3`
    // This will be called with `wave == 4`
}

transmitter.broadcast(4)

Internally, the Receiver will keep a buffer of the previous sent values. Once there is a new listener, all the previous values are sent. When the 4 is sent, it will be "listened to" as expected.

.warm(upTo: Int) 🌈:

This strategy allows you to specify how big the buffer should be:

let (transmitter, receiver) = Receiver<Int>.make(with: .warm(upTo: 1))
transmitter.broadcast(1)
transmitter.broadcast(2)

receiver.listen { wave in
    // This will be called with `wave == 2`
    // This will be called with `wave == 3`
}

transmitter.broadcast(3)

In this case 1 will never be called, because the limit specified (upTo: 1) is too low, so only 2 is kept in the buffer.

.hot πŸ”₯:

let (transmitter, receiver) = Receiver<Int>.make(with: .hot) // this is the default strategy
transmitter.broadcast(1)
transmitter.broadcast(2)

receiver.listen { wave in
    // This will be called with `wave == 3`
}

transmitter.broadcast(3)

Anything broadcasted before listening is discarded.

Opinionated, in what way? πŸ€“

Initializer. 🌳

The make method, follows the same approach used in ReactiveSwift, with pipe. Since a receiver only makes sense with a transmitter, it's only logical for them to be created together.

Separation between the reader and the writer. ⬆️ ⬇️

A lot of libs have the reader and the writer bundled within the same entity. For the purposes and use cases of this lib, it makes sense to have these concerns separated. It's a bit like a UITableView and a UITableViewDataSource: one fuels the other, so it might be better for them to be split into two different entities.

Ok, so why would I use this? πŸ€·β€β™€οΈ

Well, to make your codebase awesome of course. There are a lot of places where the observer pattern can be useful. In the most simplistic scenario, when delegation is not good enough and you have an 1-to-N relationship.

A good use case for this would in tracking an UIApplication's lifecycle:

enum ApplicationLifecycle {
    case didFinishLaunching
    case didBecomeActive
    case didEnterBackground
}

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    private var transmitter: Receiver<ApplicationLifecycle>.Transmitter!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        let (transmitter, receiver) = Receiver<ApplicationLifecycle>.make()
        self.transmitter = transmitter
        // Pass down the `receiver` to where it's needed (e.g. ViewModel, Controllers)

        transmitter.broadcast(.didFinishLaunching)
        return true
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        transmitter.broadcast(.didEnterBackground)
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        transmitter.broadcast(.didBecomeActive)
    }
}

Similar to the ApplicationLifecycle, the same approach could be used for MVVM:

class MyViewController: UIViewController {
    private let viewModel: MyViewModel
    private let transmitter: Receiver<UIViewControllerLifecycle>.Transmitter

    init(viewModel: MyViewModel, transmitter: Receiver<UIViewControllerLifecycle>.Transmitter) {
        self.viewModel = viewModel
        self.transmitter = transmitter
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewdDidLoad()
        transmitter.broadcast(.viewDidLoad)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        transmitter.broadcast(.viewDidAppear)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        transmitter.broadcast(.viewDidDisappear)
    }
}

The nice part is that the UIViewController is never aware of the receiver, as it should be. ✨

At initialization time:

let (transmitter, receiver) = Receiver<UIViewControllerLifecycle>.make()
let viewModel = MyViewModel(with: receiver)
let viewController = MyViewController(viewModel: viewModel, transmitter: transmitter)
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].