Skip to content
This repository has been archived by the owner on Jul 1, 2020. It is now read-only.

Latest commit

 

History

History
132 lines (88 loc) · 8.95 KB

README.md

File metadata and controls

132 lines (88 loc) · 8.95 KB

Objective-Chain Flattr this

Object-oriented reactive framework written in Objective-C, that abstracts production, transformation and consumption of values in a declarative way.

Project is inspired by ReactiveCocoa, but takes more object-oriented approach.

Aim is to build reusable and scalable solution for MVVM bindings. Article about MVVM implementation using Objective-Chain.

Project can be reliably used in production. Follow the Roadmap for progress.

Concept

Everything happens for a reason and this is especially true in software. Basic principle of software is to receive input and provide output. Reacting to events with actions, but our actions can trigger new events. A reactive framework should allow you to write the rules declaratively. This means you write it once and it works forever (or at least until cancelled).

In iOS and OS X applications, we know multiple way to react on events: Target + Action, Notifications, Key-Value Observing, Delegation and Blocks. All of them have different characteristics and therefore they are used in different cases and using different APIs. Objective-Chain attempts to unify these callback mechanisms and allows you to easily receive events, filter or transform their values and subsequently execute actions or chain them to other events.

Event vs. Value

To avoid confusion, we should clarify the difference between Event and Value. The difference, for purpose of Objective-Chain, is none. Producing a Value is an Event and Events usually have some Value associated with them. And if not, No Value is still a Value.

Main Components

Core concept is really simple: Producers send values and Consumers receive them. Producer and Consumer are abstract terms, so the true functionality is provied by their concrete implementations.

Producers

  • Timer – Periodically sends time intervals to Consumers until stopped.

  • Property – Observes KVO notifications of given object and key-path and sends latest values to Consumers. It's one of the Core features.

  • Notificator – Observes NSNotifications with given name and sends them to Consumers.

  • Target – Receiver of target-action callbacks that sends the sender to Consumers.

  • Command – Generic Producer to be used manually by invoking its methods.

  • Hub – Special Producer, that takes multiple other Producers and forwards their values. There are currently three kinds fof Hub: merging, combining and depending. More on those later.

  • In addition, you can easily subclass Producer with custom implementation. If there are other sources of events/values that should be implemented, feel free to suggest it.

Consumers

  • Property – Yes, the same Property as the Producer above, but this time it set received values using KVC. Setting usually triggers KVO event, that is immediately produced. It's one of the Core features.

  • Invoker – Invokes regular invocations optionally replacing the arguments with received values. Don't worry, it has never been easier to create and use NSInvocations! It's one of the Core features.

  • Subscriber – Most versatile Consumer, that can be customized using blocks. Allows you to easily create ad-hod implementations of consumers, if there is no better alternative (and trust me, there usually is).

  • Switch – Similar to switch or if-else control statements, Switch takes multiple Consumers with one Predicate for each. Once it receives value, it invokes all sub-consumers whose predicates evaluate to YES.

  • There are some more provided Consumers, but they usually only uses Subscriber to perform their task. If there are other special cases, that need custom subclass, suggest them.

These were only the endpoints of Chain, now the fun begins…

Mediators

Mediator is simply a Producer and Consumer that can stand in between and make changes to the values. It never produces new values and never uses them in a meaningful way.

  • Bridge – Basic Mediator that passes all values further. It is best when you want to expose a Producer in object's interface. Optionally, and this is important, Bridge can use a Value Transformer to convert values before passing them to Consumers. This is one of the Core features. More on Transformers later.
  • FilterMediator that evaluates a Predicate on the values. Those that evaluates to YES are passed without changes, otherwise they are ignored (discarded).
  • Context – Interesting and flexible Mediator, that simply forwards the values. The point is, it sends them in a known context. For example, inside of animation block, or inside of @synchronized statement, or even send them on another Queue. Context object is really simple, but allows you to do powerful things. More on Queues later.
  • Throttle – Time-aware Mediator. It forwards values with reduced frequency, for example when user types fast on keyboard, Throttle can be configured to send latest entered text after 0.3 s pause.

Creating Chains

You can use any of those provided components or create your own and chain them together to build the logic of your application. Examples:

  1. Listen for notification and invoke a selector:

    [[OCANotificator notify:NSUserDefaultsDidChangeNotification]
     connectTo:OCAInvocation(self, reloadPreferences)];
    • +notify: – Creates a Producer that listens for given notification.
    • OCAInvocation – Macro that creates NSInvocation for [self reloadPreferences] call.
    • -invoke: – Internally creates a Consumer OCAInvoker with given invocation and attaches it to the Notificator. Target of the invocation is stored weakly.
  2. Anytime the name of user changes, display it in a label:

    [OCAProperty(self, user.name, NSString)
     connectTo:OCAProperty(self, label.text, NSString)];
    • OCAPropertyMacro that creates an OCAProperty object. It takes a target, key-path and a class of values (NSString in both cases, more about class-validation later). Property object can act as both Producer and Consumer. This macro uses Xcode autocompletion and is validated during build time.
    • -connectTo: – Adds the argument to the receiver's list of Consumers. Any changes in user.name will be reflected by label.text.
  3. When text of text field doesn't change for 0.3 seconds, initiate search:

    [[[self.textField producerForText]
      throttle:0.3]
     connectTo:OCAInvocation(self, startSearchWithText: OCAPH(NSString) )];
    • -producerForText – Creates OCATarget Producer configured for the text field (receiver). Sends the entered text every time it changes.
    • -throttle: – Internally creates OCAThrottle Mediator with given delay, attaches it to the receiver and returns it (so we can continue chaining). Throttle will send the entered text only after it didn't change (nothing is received) for 0.3 seconds.
    • (-invoke: and OCAInvocation are described in the first example above)
    • OCAPH – Macro that is a short alias for OCAPlaceholder. When used as an argument of invocation passed to OCAInvoker, it will be replaced by real value. In this case, we used only one Placeholer, so the received text from text field (and throttled) will be passed to the invocation. Argument of the macro is class used for validation.

Example Project

For more code examples see included Chain Examples project. You can use it as a sandbox for experimenting with Objective-Chain and even submit a Pull Request, so your experiments will be merged into the master repo.

Additional Features

To be described later:

  • Class validation.
  • Transformers.
  • Predicates.
  • Queues.
  • Value boxing/unboxing.
  • Invocation catcher.
  • Decomposer.

Follow the Roadmap for progress.


Check it, load it, link it, use it,
View it, code it, quick - combine it.

Chain it, branch it, merge it, fork it,
Switch it, send it, bridge - transform it.

Catch it, change it, call it, tune it,
Drag and drop it, box - unbox it.


Licensed under The MIT License (MIT)
Copyright © 2014-2015 Martin Kiss