Description
A very simple event bus for Swift. Events may have associated data and are fully typed. All publish/subscribe methods are thread-safe.
Causality alternatives and similar libraries
Based on the "Events" category.
Alternatively, view Causality 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. -
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
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 Causality or a related project?
README
Causality
Causality
is a simple thread-safe, in-memory bus for Swift that supports fully-typed Events and States.
In addition, Causality
has provisions for monitoring State
information. State
is similar to Event
, but differ in that:
State
handlers will be called immediately with the last known good value (if one is available)State
handlers will not be called if the state value is identical to the previous value- Whereas an
Event
has an associatedMessage
, aState
has an associatedValue
. - A state's
Value
must conform to the Equatable protocol.
Installation
Swift Package Manager
Add the Causality
package to the dependencies within your application's Package.swift
file. Substitute "x.y.z" with the latest Causality
release.
.package(url: "https://github.com/dannys42/Causality.git", from: "x.y.z")
Add Causality
to your target's dependencies:
.target(name: "example", dependencies: ["Causality"]),
Cocoapods
Add Causality
to your Podfile:
pod `Causality`
Usage
Events
Just an event (no data)
The simplest event to manage has no associated data.
Declare Events
This declares an event called aTriggerEvent
that has no associated data.
struct MyEvents {
static let aTriggerEvent = Causality.Event<Causality.NoMessage>(label: "A Trigger")
}
Subscribe to events
To subscribe to this event:
let subscription = Causality.bus.subscribe(MyEvents.aTriggerEvent) { _ in
print("Event was triggered")
}
Publish events
To publish/post an event of this type:
Causality.bus.publish(MyEvents.aTriggerEvent)
An event with associated data
Events can include data of any type (referred to as a Message
). The event label is fully type specified with the message. So a subscriber will have a fully typed message available to its handler.
Define the Message
A message can be a standard Swift type like Int
, String
, etc. Or it can be a struct
or class
that conform to Causality.Message
. Take care as to whether you want value or reference semantics for messages. Generally, value semantics (i.e. a struct
) will be safer. In this example, we'll declare a struct:
struct InterestingMessage: Causality.Message {
let string: String
let number: Int
}
Declare the event
Events may be declared with an associated Message
. If declared, the Message
is a required typed parameter for publishing an event. And similarly it will be supplied as a typed parameter to subscribers of the event.
Declaring an event with a message:
let MyInterestingEvent = Causality.Event<SomeMessage>(label: "Some Event")
let MyStringEvent = Causality.Event<String>(label: "An event with a String message")
let MyNumberEvent = Causality.Event<Int>(label: "An event with an Int message")
Or categorize your events:
struct MyEvents {
static let MyInterestingEvent = Causality.Event<InterestingMessage>(label: "An interesting Event 1")
static let MyStringEvent = Causality.Event<String>(label: "An event with a String message")
static let MyNumberEvent = Causality.Event<Int>(label: "An event with an Int message")
}
Subscribing and Unsubscribing to the events
Save your subscriptions to unsubscribe later:
let subscription = Causality.bus.subscribe(MyEvents.MyInterestingEvent) { interestingMessage in
print("A message from MyInterestingEvent: \(interestingMessage)")
}
Casaulity.bus.unsubscribe(subscription)
Or unsubscribe from within a subscription handler. Here's an example of a one-shot event handler:
Causality.bus.subscribe(MyEvents.MyStringEvent) { subscription, string in
print("A string from MyStringEvent: \(string)")
subscription.unsubscribe()
}
Publish events
To publish/post an event:
Causality.bus.publish(MyEvents.MyInterestingEvent,
message: InterestingMessage(string: "Hello", number: 42))
Event Buses
Bus Alias
Create aliases for your bus:
let eventBus = Causality.bus
eventBus.publish(MyEvents.MyInterestingEvent,
message: InterestingMessage(string: "Hello", number: 42))
Local Buses
Or create local buses to isolate your events:
let newEventBus = Causality.Bus(label: "My local bus")
newEventBus.publish(MyEvents.interestingEvent,
message: InterestingMessage(string: "Hello", number: 42))
State
Define the State Value
Similar to an Event
, a State
has an associated Value
. Values can be raw types such as Int
, String
, etc. Or they may be struct
or a class
. (Similar to a an event Message
, you'll usually want to use a struct
.) However a Value
must conform to Equatable
.
struct PlayerInfo: Causality.StateValue {
let numberOfLives: Int
let health: Int
let armor: Int
}
Declare the state
Declaring a state with the associated value:
let playerState = Causality.State<PlayerInfo>(label: "Player State")
Or categorize your states:
struct GameStates {
static let playerState1 = Causality.State<PlayerInfo>(label: "Player 1 State")
static let playerState2 = Causality.State<PlayerInfo>(label: "Player 2 State")
}
Subscribing and Unsubscribing to State changes
Save your subscriptions to unsubscribe later:
let subscription = Causality.bus.subscribe(GameStates.playerState1) { state in
print("Player 1 state changed to: \(state)")
}
Casaulity.bus.unsubscribe(subscription)
Or unsubscribe from within a subscription handler. This example will monitor only a single state change:
Causality.bus.subscribe(GameStates.playerState1) { subscription, message in
print("Player 1 state changed to: \(state)")
subscription.unsubscribe()
}
If the state was previously set, the subscription handler will be called immediately with the last known value. The subscription handler will only be called if subsequent .set()
calls have differing values.
Setting State
Causality.bus.set(GameStates.playerState1,
value: PlayerInfo(numberOfLives: 3, health: 75, armor: 10))
Dynamic States
In the game example above, we have one Causality.State variable for every state. But what if we have "n" number of players? In that case, we can use Dynamic States. Dynamic States allows you to parameterize your State. Dynamic States are Codable and require you to define CodingKeys
and to overload the encode()
function to specify "key" parameters. These parameters are used to uniquely identify the state. For example:
class PlayerState<Value: PlayerInfo>: Causality.DynamicState<Value> {
let playerId: Int
init(playerId: Int) {
self.playerId = playerId
}
enum CodingKeys: CodingKey {
case playerId
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.playerId, forKey: .playerId)
}
}
Now to subscribe:
Causality.bus.subscribe(PlayerState<PlayerInfo>(playerId: 1)) { subscription, playerInfo in
print("Current player info is: \(playerInfo))
}
Or to add more organization:
struct GameState {
static func playerState(_ playerId: Int) -> PlayerState<PlayerInfo> {
return PlayerState<PlayerInfo>(playerId: playerId)
}
}
Causality.bus.subscribe(GameState.playerState(1)) { subscription, playerInfo in
print("Current player info is: \(playerInfo)")
}
And to set/update a state:
Causality.bus.set(state: GameState.playerState(1),
value: PlayerInfo(
numberOfLines: 3,
health: 75,
armor: 100))
Similarly, you could use base types of Int
, String
, etc. for the Value
.
let UserNameState = Causality.State<String>(label: "user name state")
Causality.bus.subscribe(UserNameState) { username in
print("Username is now: \(username)")
}
Causality.bus.set(UserNameState, "Mary Jane Doe")
Dynamic Events
Events can be parameterized by defining them in a similar way:
class MyEvent<Message: Causality.Message>: Causality.DynamicEvent<Message> {
let eventId: Int
init(eventId: Int) {
self.eventId = eventId
}
enum CodingKeys: CodingKey {
case eventId
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.eventId, forKey: .eventId)
}
}
Then create the event:
struct MyEvents {
static func event(_ eventId: Int) -> MyEvent<InterestingMessage> {
return MyEvent<InterestingMessage>(eventId: eventId)
}
}
Subscribe to the event:
let subscription = Causality.bus.subscribe(MyEvents.event(1)) { subscription, message in
print("A message from event \(subscription.event.eventId): \(message)")
}
And publish events:
Causality.bus.publish(MyEvents.event(1),
message: InterestingMessage(string: "Hello", number: 42))
API Documentation
For more information visit our API reference.
Related Projects
License
This library is licensed under Apache 2.0. The full license text is available in [LICENSE](LICENSE).
*Note that all licence references and agreements mentioned in the Causality README section above
are relevant to that project's source code only.