Coder Social home page Coder Social logo

spotify / mobius.swift Goto Github PK

View Code? Open in Web Editor NEW
547.0 27.0 40.0 1.01 MB

A functional reactive framework for managing state evolution and side-effects [Swift implementation]

License: Apache License 2.0

Swift 99.02% Shell 0.61% Objective-C 0.37%
ios swift functional-reactive-programming mobius state-management

mobius.swift's Introduction

Mobius.swift

codecov License

Mobius is a functional reactive framework for managing state evolution and side-effects. It emphasizes separation of concerns, testability, and isolating stateful parts of the code.

Mobius.swift is the Swift and Apple ecosystem focused implementation of the original Mobius Java framework. To learn more, see the wiki for a user guide. You can also watch a talk from an Android @Scale introducing Mobius.

This repository contains the core Mobius framework and add-ons for common development scenarios and testing.

Compatibility

Environment details
📱 iOS 10.0+
🛠 Xcode 12.0+
🐦 Language Swift 5.0

Installation

Mobius can be built for all Apple platforms using the Swift Package Manager.

Add the following entry to your Package.swift:

.package(url: "https://github.com/spotify/Mobius.swift", from: "0.5.0")

Mobius in Action - Building a Counter

The goal of Mobius is to give you better control over your application state. You can think of your state as a snapshot of all the current values of the variables in your application. In Mobius, we encapsulate all of the state in a data structure which we call the Model.

The Model can be represented by whatever type you like. In this example we’ll be building a simple counter, so all of our state can be contained in an Int:

typealias CounterModel = Int

Mobius does not let you manipulate the state directly. In order to change the state, you have to send the framework messages saying what you want to do. We call these messages Events. In our case, we’ll want to increment and decrement our counter. Let’s use an enum to define these cases:

enum CounterEvent {
    case increment
    case decrement
}

Now that we have a Model and some Events, we’ll need to give Mobius a set of rules which it can use to update the state on our behalf. We do this by giving the framework a function which will be sequentially called with every incoming Event and the most recent Model, in order to generate the next Model:

func update(model: CounterModel, event: CounterEvent) -> CounterModel {
    switch event {
    case .increment: return model + 1
    case .decrement: return model - 1
    }
}

With these building blocks, we can start to think about our applications as transitions between discrete states in response to events. But we believe there still one piece missing from the puzzle – namely the side effects which are associated with moving between states. For instance, pressing a “refresh” button might put our application into a “loading” state, with the side effect of also fetching the latest data from our backend.

In Mobius, we aptly call these side effects Effects. In the case of our counter, let’s say that when the user tries to decrement below 0, we play a sound effect instead. Let’s create an enum that represents all the possible effects (which in this case is only one):

enum CounterEffect {
    case playSound
}

We’ll now need to augment our update function to also return a set of effects associated with certain state transitions. This looks like:

func update(model: CounterModel, event: CounterEvent) -> Next<CounterModel, CounterEffect> {
    switch event {
    case .increment: 
        return .next(model + 1)
    case .decrement:
        if model == 0 {
            return .dispatchEffects([.playSound])
        } else {
            return .next(model - 1)
        }
    }
}

Mobius sends each of the effects you return in any state transition to something called an Effect Handler. Let’s make one of those now:

import AVFoundation

private func beep() {
    AudioServicesPlayAlertSound(SystemSoundID(1322))
}

let effectHandler = EffectRouter<CounterEffect, CounterEvent>()
    .routeCase(CounterEffect.playSound).to { beep() }
    .asConnectable

Now that we have all the pieces in place, let’s tie it all together:

let application = Mobius.loop(update: update, effectHandler: effectHandler)
    .start(from: 0)

Let’s start using our counter:

application.dispatchEvent(.increment) // Model is now 1
application.dispatchEvent(.decrement) // Model is now 0
application.dispatchEvent(.decrement) // Sound effect plays! Model is still 0

This covers the fundamentals of Mobius. To learn more, head on over to our wiki.

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

mobius.swift's People

Contributors

anawara avatar balestrapatrick avatar caipre avatar dflems avatar jensayton avatar jeppes avatar kmcbride avatar louisdebaere avatar mjburghard avatar perploug avatar pettermahlen avatar rastersize avatar stephencelis avatar viktorgardart avatar zvonicek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mobius.swift's Issues

Broken swift package

imported as swift package all 4 suggested items. The result is app doesn't work because a lot of errors:
The package product 'CasePaths' requires minimum platform version 13.0 for the iOS platform, but this target supports 11.0
The target OS of the project is 13.0+

Import instructions

When I import your library as package it suggests MobiusCore, MobiusExtras, MobiusNimble and MobiusTest. Which ones should import?

Sample app

Is there a sample app that uses mobius swift? Just like the android one.

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.