ReactKit alternatives and similar libraries
Based on the "Events" category.
Alternatively, view ReactKit alternatives based on common mentions on social networks and blogs.
-
ReactiveCocoa
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift. -
ReactorKit
A library for reactive and unidirectional Swift applications -
OpenCombine
Open source implementation of Apple's Combine framework for processing values over time. -
Katana
Swift Apps in a Swoosh! A modern framework for creating iOS apps, inspired by Redux. -
BrightFutures
Write great asynchronous code in Swift using futures and promises -
PMKVObserver
Modern thread-safe and type-safe key-value observing for Swift and Objective-C -
Tempura
A holistic approach to iOS development, inspired by Redux and MVVM -
VueFlux
:recycle: Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux -
SignalKit
SignalKit is a reactive Swift framework with focus on clean and readable API. -
NoticeObserveKit
NoticeObserveKit is type-safe NotificationCenter wrapper. -
LightweightObservable
š¬ A lightweight implementation of an observable sequence that you can subscribe to. -
RxReduce
Reactive implementation of the state container pattern (like Redux). It is based on the simple concepts of state immutability and unidirectionnal data flow. -
Notificationz
š” Helping you own NotificationCenter in Swift! -
Aftermath
:crystal_ball: Stateless message-driven micro-framework in Swift. -
TopicEventBus
Publishāsubscribe design pattern implementation framework, with an ability to publish events by topic. -
OneWay
A Swift library for state management with unidirectional data flow. -
SSEventFlow
SSEventFlow is a type safe alternative to NotificationCenter, inspired by Flux -
Causality
A simple thread-safe, in-memory bus for Swift that supports fully-typed Events and States.
Appwrite - The Open Source Firebase alternative introduces iOS support
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of ReactKit or a related project?
README
ReactKit 
Swift Reactive Programming.
How to install
See Wiki page.
Example
For UI Demo, please see ReactKit/ReactKitCatalog.
Key-Value Observing
// create stream via KVO
self.obj1Stream = KVO.stream(obj1, "value")
// bind stream via KVC (`<~` as binding operator)
(obj2, "value") <~ self.obj1Stream
XCTAssertEqual(obj1.value, "initial")
XCTAssertEqual(obj2.value, "initial")
obj1.value = "REACT"
XCTAssertEqual(obj1.value, "REACT")
XCTAssertEqual(obj2.value, "REACT")
To remove stream-bindings, just release stream
itself (or call stream.cancel()
).
self.obj1Stream = nil // release stream & its bindings
obj1.value = "Done"
XCTAssertEqual(obj1.value, "Done")
XCTAssertEqual(obj2.value, "REACT")
If you want to observe changes in Swift.Array
or NSMutableArray
,
use DynamicArray
feature in Pull Request #23.
NSNotification
self.stream = Notification.stream("MyNotification", obj1)
|> map { notification -> NSString? in
return "hello" // convert NSNotification? to NSString?
}
(obj2, "value") <~ self.stream
Normally, NSNotification
itself is useless value for binding with other objects, so use Stream Operations e.g. map(f: T -> U)
to convert it.
To understand more about |>
pipelining operator, see Stream Pipelining.
Target-Action
// UIButton
self.buttonStream = self.button.buttonStream("OK")
// UITextField
self.textFieldStream = self.textField.textChangedStream()
^{ println($0) } <~ self.buttonStream // prints "OK" on tap
// NOTE: ^{ ... } = closure-first operator, same as `stream ~> { ... }`
^{ println($0) } <~ self.textFieldStream // prints textField.text on change
Complex example
The example below is taken from
- iOS - ReactiveCocoaćććć£ć¦ćæć - Qiita (well-illustrated)
where it describes 4 UITextField
s which enables/disables UIButton
at certain condition (demo available in ReactKit/ReactKitCatalog):
let usernameTextStream = self.usernameTextField.textChangedStream()
let emailTextStream = self.emailTextField.textChangedStream()
let passwordTextStream = self.passwordTextField.textChangedStream()
let password2TextStream = self.password2TextField.textChangedStream()
let allTextStreams = [usernameTextStream, emailTextStream, passwordTextStream, password2TextStream]
let combinedTextStream = allTextStreams |> merge2All
// create button-enabling stream via any textField change
self.buttonEnablingStream = combinedTextStream
|> map { (values, changedValue) -> NSNumber? in
let username: NSString? = values[0] ?? nil
let email: NSString? = values[1] ?? nil
let password: NSString? = values[2] ?? nil
let password2: NSString? = values[3] ?? nil
// validation
let buttonEnabled = username?.length > 0 && email?.length > 0 && password?.length >= MIN_PASSWORD_LENGTH && password == password2
// NOTE: use NSNumber because KVO does not understand Bool
return NSNumber(bool: buttonEnabled)
}
// REACT: enable/disable okButton
(self.okButton, "enabled") <~ self.buttonEnablingStream!
For more examples, please see XCTestCases.
How it works
ReactKit is based on powerful SwiftTask (JavaScript Promise-like) library, allowing to start & deliver multiple events (KVO, NSNotification, Target-Action, etc) continuously over time using its resume & progress feature (react()
or <~
operator in ReactKit).
Unlike Reactive Extensions (Rx) libraries which has a basic concept of "hot" and "cold" observables, ReactKit gracefully integrated them into one hot + paused (lazy) stream Stream<T>
class. Lazy streams will be auto-resumed via react()
& <~
operator.
Here are some differences in architecture:
Reactive Extensions (Rx) | ReactKit | |
---|---|---|
Basic Classes | Hot Observable (broadcasting)Cold Observable (laziness) | Stream<T> |
Generating | Cold Observable (cloneability) | Void -> Stream<T> (= Stream<T>.Producer ) |
Subscribing | observable.subscribe(onNext, onError, onComplete) |
stream.react {...}.then {...} (method-chainable) |
Pausing | pausableObservable.pause() |
stream.pause() |
Disposing | disposable.dispose() |
stream.cancel() |
Stream Pipelining
Streams can be composed by using |>
stream-pipelining operator and Stream Operations.
For example, a very common incremental search technique using searchTextStream
will look like this:
let searchResultsStream: Stream<[Result]> = searchTextStream
|> debounce(0.3)
|> distinctUntilChanged
|> map { text -> Stream<[Result]> in
return API.getSearchResultsStream(text)
}
|> switchLatestInner
There are some scenarios (e.g. repeat()
) when you want to use a cloneable Stream<T>.Producer
(Void -> Stream<T>
) rather than plain Stream<T>
. In this case, you can use |>>
streamProducer-pipelining operator instead.
// first, wrap stream with closure
let timerProducer: Void -> Stream<Int> = {
return createTimerStream(interval: 1)
|> map { ... }
|> filter { ... }
}
// then, use `|>>` (streamProducer-pipelining operator)
let repeatTimerProducer = timerProducer |>> repeat(3)
But in the above case, wrapping with closure will always become cumbersome, so you can also use |>>
operator for Stream
& Stream Operations as well (thanks to @autoclosure
).
let repeatTimerProducer = createTimerStream(interval: 1)
|>> map { ... }
|>> filter { ... }
|>> repeat(3)
Functions
Stream Operations
For Single Stream
- Transforming
asStream(ValueType)
map(f: T -> U)
flatMap(f: T -> Stream<U>)
map2(f: (old: T?, new: T) -> U)
mapAccumulate(initialValue, accumulator)
(alias:scan
)buffer(count)
bufferBy(stream)
groupBy(classifier: T -> Key)
- Filtering
filter(f: T -> Bool)
filter2(f: (old: T?, new: T) -> Bool)
take(count)
takeUntil(stream)
skip(count)
skipUntil(stream)
sample(stream)
distinct()
distinctUntilChanged()
- Combining
merge(stream)
concat(stream)
startWith(initialValue)
combineLatest(stream)
zip(stream)
recover(stream)
- Timing
delay(timeInterval)
interval(timeInterval)
throttle(timeInterval)
debounce(timeInterval)
- Collecting
reduce(initialValue, accumulator)
- Other Utilities
peek(f: T -> Void)
(for injecting side effects e.g. debug-logging)customize(...)
For Array Streams
mergeAll(streams)
merge2All(streams)
(generalized method formergeAll
&combineLatestAll
)combineLatestAll(streams)
zipAll(streams)
For Nested Stream (
Stream<Stream<T>>
)mergeInner(nestedStream)
concatInner(nestedStream)
switchLatestInner(nestedStream)
For Stream Producer (
Void -> Stream<T>
)prestart(bufferCapacity)
(alias:replay
)times(count)
retry(count)
Helpers
Creating
Stream.once(value)
(alias:just
)Stream.never()
Stream.fulfilled()
(alias:empty
)Stream.rejected()
(alias:error
)Stream.sequence(values)
(a.k.a Rx.fromArray)Stream.infiniteSequence(initialValue, iterator)
(a.k.a Rx.iterate)
Other Utilities
ownedBy(owner: NSObject)
(easy strong referencing to keep streams alive)
Dependencies
References
- Introducing ReactKit // Speaker Deck (ver 0.3.0)
Licence
*Note that all licence references and agreements mentioned in the ReactKit README section above
are relevant to that project's source code only.