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.
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.
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.
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.
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.
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…
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.
YES
are passed without changes, otherwise they are ignored (discarded).@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.You can use any of those provided components or create your own and chain them together to build the logic of your application. Examples:
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.Anytime the name of user changes, display it in a label:
[OCAProperty(self, user.name, NSString)
connectTo:OCAProperty(self, label.text, NSString)];
OCAProperty
– Macro 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
.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.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.
To be described later:
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