A lightweight, event-driven architectural framework

Last update: Apr 12, 2022

Trellis

Swift Xcode MIT

Trellis features a declarative DSL that simplifies service bootstrapping:

let cluster = try await Bootstrap {
    Group {
        Store(model: IdentityModel.self)
            .mutate(on: IdentityAction.self) { model, action, send in
                // ...
            }
            .mutate(on: StartUpAction.self) { model, action, send in
                // ...
            }
            .with(model: identityModel)
        Store(model: ArticlesModel.self)
            .mutate(on: ArticlesAction.self) { model, action, send in
                // ...
            }
            .with(model: articlesModel)
    }
    .emit(using: notificationsStream)
    .transformError {
        ErrorAction.error($0)
    }
    .observe(on: IdentityAction.self) {
        // ...
    }
}

This sets up two services managing the identity of the user and his articles. The resulting cluster exposes only one function, send, which can be used to interact with the services without explicitly know which service handles which action.

try await cluster.send(action: StartUpAction.appDidCompleteLaunching)

Most of the time we won't declare services like this. Instead, we'd write a custom service wrapping each store:

// IdentityService.swift
struct IdentityService: Service {
    var body: some Service {
        Store(model: IdentityModel.self)
            .mutate(on: IdentityAction.self) { model, action, send in
                // ...
            }
            .mutate(on: StartUpAction.self) { model, action, send in
                // ...
            }
    }

// SomeOtherFile.swift
let cluster = try await Bootstrap {
    IdentityService()
        .with(model: identityModel)
}

Notice how the actual model is injected from outside the service, enabling dependency injection.

Index

Installation

Using Swift Package Manager:

.package(name: "Trellis",
         url: "https://github.com/valentinradu/Trellis.git",
         .upToNextMinor(from: "0.3.0-beta"))

Getting started

Actions and services

Services are entities that react to actions. They form a tree-like structure that allows each parent service to delegate actions to its children. Most of the entities in Trellis are services.

Modifiers

Modifiers change the behavior of a service. Most modifiers, like .serial() will traverse the service tree and apply to all sub-services under it, while some, like .mutate(on:) only make sense when applied to the service immediately under it. For more info about modifiers check the appropriate section below.

Groups

Groups are inert services that pass actions to their children without taking any other additional steps. They're mostly used to apply a modifier (e.g. emit(using:consumeAtBootstrap:)) to multiple services or to bypass the number of maximum sub-services (8) a service can have.

Stores

Each store encapsulates a model, which in turn, handles a set of tasks (and their associated data) that go together well. Stores allow you to use and mutate the wrapped model each time an action is sent to the cluster.

Modifiers

.emit(using:consumeAtBootstrap:) - Takes an external source of events (async stream) that outputs actions and feeds them to all services under it. When setting `

.transformError(transformHandler:) - Turns all errors originating from services under it into actions and feeds them back into the cluster. If the transformed error throws again, the operation will fail and the send(action:) function with throw.

.concurrent() - Executes all services under it in a concurrent fashion. This is the default.

.serial() - Executes all services under it one after the other. Ideal for cases where you want to something, like the identity of the user, before allowing other services to process the action.

.bootstrap(bootstrapHandler:) - Called right after service creation, it gives services the possibility to initialize state or bootstrap models before handling any actions.

.observe(observeHandler:) - Called each time an action is received. Ideal for logging and updating external (e.g. presentation layer) state.

.mutate(on:mutateHandler:) - Called each time an action is received. Inside the handler you can mutate the model depending on the received action and send other actions to further processing.

.with(model:) - Sets the model for all sub-services under it.

Concurrency

Trellis uses the Swift concurrency model and guarantees that the services will be always built and bootstrapped on the main thread. There is on other guarantee, and for this reason, all models should be actors.

Testing

With Trellis, unit testing is mostly focused around the models. However, if you wish to also test the service integration, it's easy to do so. You can simply replace the model with a mocked version and the cluster send function with one that records actions instead:

// SomeTest.swift
let cluster = try await Bootstrap {
    IdentityService()
        .with(model: mockedIdentityModel)
        .environment(\.send, recordingSend)
}

try await cluster.send(action: StartUpAction.appDidCompleteLaunching)
// Assert the state of the mocked identity model and the recorded actions

License

MIT License

GitHub

https://github.com/valentinradu/Trellis
You might also like...

Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.

Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.

Reactive extensions to Cocoa frameworks, built on top of ReactiveSwift. ⚠️ Looking for the Objective-C API? 🎉 Migrating from RAC 4.x? 🚄 Release Road

May 12, 2022

An observables framework for Swift

An observables framework for Swift

🐌 snail A lightweight observables framework, also available in Kotlin Installation Carthage You can install Carthage with Homebrew using the followin

Mar 11, 2022

Two-way data binding framework for iOS. Only one API to learn.

BindKit A simple to use two-way data binding framework for iOS. Only one API to learn. Supports Objective-C, Swift 5, Xcode 10.2, iOS 8 and above. Shi

Nov 16, 2021

A Swift framework for reactive programming.

CwlSignal An implementation of reactive programming. For details, see the article on Cocoa with Love, CwlSignal, a library for reactive programming. N

Mar 15, 2022

Open source implementation of Apple's Combine framework for processing values over time.

Open source implementation of Apple's Combine framework for processing values over time.

OpenCombine Open-source implementation of Apple's Combine framework for processing values over time. The main goal of this project is to provide a com

May 19, 2022

This Repository holds learning data on Combine Framework

Combine Framework List of Topics Welcome, every section in this repo contains a collection of exercises demonstrating combine's utilization as well as

Mar 17, 2022

Binding - Data binding framework (view model binding on MVVM) written using propertyWrapper and resultBuilder

Binding Data binding framework (view model binding on MVVM) written using @prope

Mar 23, 2022

🌾 Harvest: Apple's Combine.framework + State Machine, inspired by Elm.

🌾 Harvest: Apple's Combine.framework + State Machine, inspired by Elm.

NOTE: This repository has been discontinued in favor of Actomaton. 🌾 Harvest Apple's Combine.framework (from iOS 13) + State Machine, inspired by Elm

Apr 26, 2022

An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps

PeerKit An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps Usage // Automatically detect and attach to

May 18, 2022

An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps

PeerKit An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps Usage // Automatically detect and attach to

May 18, 2022

An Event View based on Apple's Event Detail View. Written in Swift 3. Supports ARC, Autolayout and editing via StoryBoard.

An Event View based on Apple's Event Detail View. Written in Swift 3. Supports ARC, Autolayout and editing via StoryBoard.

An Event View based on Apple's Event Detail View. Written in Swift 3. Supports ARC, Autolayout and editing via StoryBoard. Installation CocoaPods PTEv

Feb 14, 2021

Event management iOS app for organizers using Open Event Platform

Event management iOS app for organizers using Open Event Platform

Open Event Organizer iOS App Event management app for organizers using Open Event Platform Roadmap Make the app functionality and UI/UX similar to the

May 15, 2022

Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers

Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers

Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers. It's magic. Features Animate a

May 8, 2022

🔌 Non-blocking TCP socket layer, with event-driven server and client.

🔌  Non-blocking TCP socket layer, with event-driven server and client.

Original authors Honza Dvorsky - http://honzadvorsky.com, @czechboy0 Matthias Kreileder - @matthiaskr1 At the request of the original authors, we ask

May 16, 2022

MQTTNIO - Non-blocking, event-driven Swift client for MQTT build on SwiftNIO

This library has support for WebSocket connections and TLS. It runs on all platforms Swift NIO runs on (e.g. macOS, iOS, Linux, etc.).

Apr 13, 2022

🔌 Non-blocking TCP socket layer, with event-driven server and client.

🔌  Non-blocking TCP socket layer, with event-driven server and client.

Original authors Honza Dvorsky - http://honzadvorsky.com, @czechboy0 Matthias Kreileder - @matthiaskr1 At the request of the original authors, we ask

May 16, 2022

🔌 Non-blocking TCP socket layer, with event-driven server and client.

🔌  Non-blocking TCP socket layer, with event-driven server and client.

Original authors Honza Dvorsky - http://honzadvorsky.com, @czechboy0 Matthias Kreileder - @matthiaskr1 At the request of the original authors, we ask

May 16, 2022

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

Sep 29, 2021

An advanced Swift (IOS Native) application that uses SOLID architectural principles, consumes a RESTFUL Service, downloads & images using best practices.

An advanced Swift (IOS Native) application that uses SOLID architectural principles, consumes a RESTFUL Service, downloads & images using best practices.

dog-playground-ios An advanced Swift (IOS Native) application that uses SOLID architectural principles, consumes a RESTFUL Service, downloads & im

Jan 10, 2022
iOS app for open event

CircleCI Code Quality Chat Open Event iOS iOS app for Open Event Introduction This is an iOS app developed for FOSSASIA in mind. The Open Event Projec

May 15, 2022
Open Event Orga iOS App
Open Event Orga iOS App

Open Event Organizer iOS App Event management app for organizers using Open Event Platform Roadmap Make the app functionality and UI/UX similar to the

May 22, 2022
MVVM + FLUX iOS Instagram client in Swift, eliminates Massive View Controller in unidirectional event/state flow manner
MVVM + FLUX iOS Instagram client  in Swift, eliminates Massive View Controller in unidirectional event/state flow manner

CZInstagram MVVM + FLUX iOS Instagram client in Swift, eliminates Massive View Controller in unidirectional event/state flow manner. Unidirectional Da

May 21, 2022
A Swift event bus for UIWebView/WKWebView and JS.
A Swift event bus for UIWebView/WKWebView and JS.

An event bus for sending messages between UIWebView/WKWebView and embedded JS. Made with pure Swift. Features Easy, fast and reliable event bus system

Apr 10, 2022
🎧 Protocol driven object observation

Listenable Swift object that provides an observable platform for multiple listeners. Requirements iOS 9.0+ Xcode 9.x+ Swift 4 Installation Listenable

Dec 24, 2021
RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way.
RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way.

About Architecture concerns RxReduce Installation The key principles How to use RxReduce Tools and dependencies Travis CI Frameworks Platform Licence

Jan 29, 2022
Simple and lightweight Functional Reactive Coding in Swift for the rest of us
Simple and lightweight Functional Reactive Coding in Swift for the rest of us

The simplest Observable<T> implementation for Functional Reactive Programming you will ever find. This library does not use the term FRP (Functional R

May 11, 2022
Lightweight observations and bindings in Swift
Lightweight observations and bindings in Swift

What is Hanson? Hanson is a simple, lightweight library to observe and bind values in Swift. It's been developed to support the MVVM architecture in o

Dec 27, 2021
📬 A lightweight implementation of an observable sequence that you can subscribe to.
📬 A lightweight implementation of an observable sequence that you can subscribe to.

Features Lightweight Observable is a simple implementation of an observable sequence that you can subscribe to. The framework is designed to be minima

Apr 11, 2022
Simple, lightweight swift bindings

Bindy Just a simple bindings. Installation Add pod 'Bindy' to your podfile, and run pod install SPM is supported too. Usage For now, Bindy has a coupl

Mar 3, 2021