MobiusCore 0.3.0

MobiusCore 0.3.0

Jens AytonDan FlemingPatrick Balestra 维护。



Mobius.swift

CocoaPods Compatible Carthage Compatible codecov License

Mobius 是一个功能性的响应式框架,用于管理状态演变和副作用。它强调关注点的分离、可测试性和隔离代码中有状态的部分。

Mobius.swift 是针对 Swift 和 Apple 生态系统重构的原始 Mobius Java 框架。要了解更多信息,请参阅wiki中的用户指南。您还可以观看一个在 Android @Scale 介绍 Mobius 的演讲

此仓库包含核心 Mobius 框架以及用于常见开发场景和测试的插件。

兼容性

环境 详情
📱iOS 10.0+
🛠Xcode 11.0+
🐦语言 Swift 5.0

安装

Mobius.swift 支持大多数流行的依赖管理器。选择您首选的方法查看说明

Swift Package Manager

Mobius 可以使用 Swift Package Manager 为所有 Apple 平台构建。

将以下条目添加到您的 Package.swift

.package(url: "https://github.com/spotify/Mobius.swift.git", .upToNextMajor(from: "0.3.0"))
CocoaPods

Mobius 只能使用 CocoaPods 为 iOS 构建应用程序。对于其他平台,请使用 Swift Package Manager。

在您的 Podfile 中添加以下条目

pod 'MobiusCore', '0.3.0'

可选地,您还可以选择集成 MobiusExtrasMobiusNimbleMobiusTest

pod 'MobiusExtras', '0.3.0'
pod 'MobiusNimble', '0.3.0'
pod 'MobiusTest', '0.3.0'
Carthage

Mobius 只能使用 Carthage 为 iOS 构建应用程序。对于其他平台,请使用 Swift Package Manager。

在您的 Cartfile 中添加以下条目

github "spotify/Mobius.swift" "0.3.0"

以下是在 Carthage 文档中解释的额外步骤。

注意:在此刻 Carthage 没有在单个仓库中指定 subspec 的方法。因此,Carthage 会自动拉取用于在 MobiusNimble 中提供测试辅助器的依赖。如果您不打算使用它,可以简单地选择不在项目中链接此库。

Mobius 在行动 - 构建计数值器

Mobius 的目标是让您更好地控制应用程序状态。您可以将状态视为应用程序中所有变量当前值的快照。在 Mobius 中,我们将所有状态封装在一个我们称为 模型 的数据结构中。

模型 可以表示您喜欢的任何类型。在这个例子中,我们将构建一个简单的计数器,因此我们所有的状态都可以包含在一个 Int 中。

typealias CounterModel = Int

Mobius 不允许您直接操作状态。为了改变状态,您必须向框架发送消息,说明您想做什么。我们把这些消息称为 事件。在我们的情况下,我们想增加和减少计数器。让我们使用 enum 来定义这些情况。

enum CounterEvent {
    case increment
    case decrement
}

现在我们有了 模型 和一些 事件,我们需要给 Mobius 一套规则,它可以使用这些规则代替我们更新状态。我们通过给框架一个函数来完成,该函数将按顺序被每个传入的 事件 和最新的 模型 调用,以生成下一个 模型

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

有了这些构建块,我们开始将应用程序视为由事件触发的离散状态之间的转换。但我们相信拼图中仍然有一块缺失——那就是与状态转换相关的副作用。例如,按下“刷新”按钮可能会将应用程序放入“加载”状态,同时副作用是从我们的后端获取最新的数据。

在 Mobius 中,我们恰当地将这些副作用称为 效应。在我们的计数器的情况下,让我们假设当用户尝试将计数器减到 0 以下时,我们将播放一个音效。让我们创建一个 enum 来代表所有可能的效果(在这个情况下只有一个)。

enum CounterEffect {
    case playSound
}

我们现在需要扩展我们的 update 函数,以便它还能返回一组与某些状态转换关联的效应。这看起来像:

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 将您在任何状态转换中返回的每个效应发送到一个我们称为 效应处理程序 的地方。现在让我们做一个吧。

import AVFoundation

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

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

现在所有部件都已经就位,让我们把它们全部连接起来。

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

让我们开始使用我们的计数器。

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

这涵盖了 Mobius 的基础知识。要了解更多信息,请访问我们的 wiki

状态

Mobius.swift 正在接近 1.0 版本发布。我们已经在部署的功能中内部使用这个框架,但最近做了一些破坏性更改。版本 0.3.0 与之前的 0.2.0 版本不兼容,并包含一些较小更改的过时向后兼容包装器。这些过时版本将被移除,并进行一些其他添加更改,以形成 Mobius 1.0。

开发

  1. 克隆
  2. 启动项目
    ./Tools/bootstrap.sh
  3. 使用Xcode打开Mobius.xcodeproj。
  4. ????
  5. 创建PR

行为准则

本项目遵守开放行为准则。通过参与,你应遵守本准则。