MobiusNimble 0.3.0

MobiusNimble 0.3.0

Jens AytonDan FlemingPatrick Balestra 维护。



 
依赖于
MobiusCore= 0.3.0
MobiusTest= 0.3.0
Nimble~> 8.0.7
 

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 管理器

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

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

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

Mobius 仅支持使用 CocoaPods 构建 iOS。对于其他平台,请使用 Swift Package 管理器。

在您的 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 管理器。

在您的 Cartfile 中添加以下条目

github "spotify/Mobius.swift" "0.3.0"

有关说明,请参阅 Carthage 文档

注意:目前Carthage还没有在单个仓库中指定子规格的方法。因此,Carthage会自动拉取用于在《MobiusNimble》中提供测试辅助工具的依赖。如果您不打算使用它,可以直接在项目中不链接这个库。

《Mobius实战——构建一个计数器》

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

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

typealias CounterModel = Int

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

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 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的基础知识。想了解更多,请访问我们的维基

状态

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

行为准则

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