Git Product home page Git Product logo

unison's Introduction

Unison

First things first: This is the beginning of a community-driven open-source project actively seeking contributions, be it code, documentation, or ideas.

Overview

Unison is tailored to suit modern development practices. Whether you prefer to prototype and preview the UI first, test-drive it or work based on specifications resulting from a technical planning.

Applications build with Unison are consistent and simple, harnessing the power of both Functional Programming (FP) and Object-Oriented Programming (OOP).

By incorporating key principles from these paradigms, Unison offers:

  • Predictability: Pure functions and immutability for consistent, reliable behavior.
  • Testability: Easily perform unit testing and debugging with isolated functions.
  • Concurrency: Safeguard against data races in concurrent environments using immutable data structures.
  • Encapsulation: Enhance code organization and maintainability by grouping data and related methods.
  • Inheritance: Promote modularity and code reuse through class hierarchies.
  • Polymorphism: Increase flexibility and extensibility with a unified interface for various data types.

These principles facilitate the development of robust, reliable, and bug-free Swift applications.

Unison is compatible with all Apple platforms (iOS, macOS, tvOS, and watchOS) and comes with convenient syntax for seamless integration with SwiftUI.

Concepts

The Unison framework is based on the Model-Update-Effect (MUE) pattern. MUE emphasizes unidirectional data flow and the separation of state, business logic, and side effects, fostering maintainable and testable applications.

Unison

Demo

Not in the mood to read more documentation? To see Unison in action, checkout the included demo project that showcases some examples of how to use the framework in real-world-ish scenarios.

Screenshot 2023-06-15 at 21 12 50

To run the demo project, follow these steps:

  1. Clone or downlaod the Unison repository
  2. Open the project using Xcode
  3. Select UnisonDemo target
  4. Build and run the project

Installation

TODO add more details

  1. Add Swift Package dependency
  2. Add Unison CodeGenerator as Build Tool Plugin

How does it work?

State

The State component is a model containing all the information required to render the view. It should be immutable, with all fields declared using the let keyword.

struct YourViewState: Equatable, SmartCopy {
    let userInput: String
}

Unison generates copy functions for each struct that conforms to the SmartCopy protocol.

Implementing the Equatable protocol is required by Unison to determine state changes.

Event

The Event component defines all user interactions that can be captured by the screen. It should be designed to be as granular as possible to enable more precise control over state updates.

enum YourViewEvent {
    case userInputChanged(String)
}

Update

The Update component is the only place where state updates occur. It is designed to be pure, meaning that all functions implemented here are deterministic and therefore easily testable.

Update requires two functions to be implemented: one to handle user input (events), and another to handle results from effects.

typealias Result = UpdateResult<YourViewState, YourEffect>

func handle(event: YourViewEvent, _ currentState: YourViewState) -> Result
func handle(result: YourEffect.Result, _ currentState: YourViewState) -> Result

The return value for an update function can be:

public enum UpdateResult<State, Effect> {
    case noChange
    case newState(state: State)
    case dispatchEffect(state: State, effect: Effect)
}

Effect

The Effect component represents functions that have side effects, such as API calls, persisting data, and navigation. Effects need to have defined results as well.

enum YourEffect: Effect {
    case action(input: String)
    
    enum YourResult {
        case success
        case failure(Error)
    }
}

Effect Handler

The Effect Handler is where code is implemented to perform each effect defined previously.

func handle(_ effect: YourEffect) async -> EffectResult<YourEffect.Result> {

The retrun value for an effect can be:

public enum EffectResult<Result> {
    case repeating(AsyncStream<Result>)
    case single(Result)
    case empty
}

Putting it all together

Every View is constructed with two parameters: an immutable state and a completion that handles user input.

struct YourView: View, UnisonView {
    
    let state: YourViewState
    let handler: (YourViewEvent) -> Void

UnisonView provides a several extension to instantiate and connect your views.

YourView.create(
     initialState: YourViewState(),
     update: YourUpdate.self,
     effectHandler: YourEffectHandler.self
)

Inpsiration

This framework is inpired mainly inspired by Mobius. The intention was to create something more leight-weight and straight-forward to use for iOS.

Contributing

Contributions to Unison are welcome and encouraged! If you have an idea for a feature or improvement, please submit a pull request or open an issue.

License

Unison is available under the MIT license. See the LICENSE file for more information.

Credits

Unison was created by David Scheutz.

unison's People

Contributors

davidscheutz avatar

Stargazers

Marek avatar  avatar  avatar  avatar

Watchers

 avatar

unison's Issues

Figure out Navigation

By definition navigation isn't anything pure and therefore shouldn't be part of the Update.

What about the EffectHandler?

  • Is this the right place?
  • If yes, should the Unison framework know about navigation at all?
  • If yes, should navigation be required to create an EffectHandler?

thinking in progress...

Shared State

How would we implement a scenario where several screens collect data that will be used to perform an action, e.g. a wizard to create a user object?

Should Unison provide a solution for that problem?

Some required functionality

  • coordination (navigating between screens)
  • storing the data outside a specific view
  • performing an action with the data triggered by a user interaction (e.g. final submit button)

thinking in progress...

Composable EffectHandler

The idea is to make handling side effects reusable where the cases of one effect could be handled by several effect handlers.

Some examples

  • displaying an error message
  • performing a common API call.

Some questions

  • Every EffectHandler is constructed with a specific Effect and returns a specific Effect.Result, how do we map between those two types?
  • How to make it clear which case of an effect is handled by which effect handler?
  • How to make sure there are no cases left out?

Add README

For obvious reasons...

Should contains

  • overall concept explained
  • benefits and reasoning behind decision
  • components involved
  • how to use it & examples
  • inspiration

Add Logger

A logger to make events, effects and state changes visible

Multiple Results for a dispatched Effect

Currently an effect is an async operation that can return a result.

This concept comes with some limitations as it doesn't support use cases where an effect would like to return multiple results e.g. a timer for a clock.

Top of mind: Swift.AsyncSequence could be one solution.

thinking in progress...

Demo Example Ideas

โœ… Paginated list with detail page

Demonstrates:

  • Init state with ID
  • Simplicity of page control

Animate data changes

e.g. progress bar

Parallel Work Execution

e.g. load multiple images or other information

Hide internal state properties from the UI

Currently all properties of the State model are exposed to the UI.
In some use cases it is helpful to have state values that aren't relevant for the UI.
It would be nice if we could hide those details...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.