Flask 1.1.31

Flask 1.1.31

Eonfluxor 维护。



Flask 1.1.31

alternate text

CocoaPods compatible Swift 4.0 platforms Build Status

什么是Flask Reactor?

Flask是Swift中单向数据流架构的多平台(iOS | OSX | tvOS)实现,它通过直观的API提供了一组独特的功能,这些功能超越了任何类似框架。

虽然Flux架构很抽象,但解释Flask却很容易


Reactors 允许您 Mix Substances 并对其 State Changes 进行 React


很简单对吧?

为了保持这种自然直觉,《Flux Stores》被称为 Substance。在Flask中,一个 Substance 将代表您应用程序中的任何同质数据结构(例如,一个源、设置或应用程序本身)。

Flask允许通过统一的架构实现Redux和Fluxor模式的样式,尽管这种集成可能听起来很复杂,但使用Flask它也很容易表达。


Flask让您能够创建对称为 Mixers 的环境事件做出反应的 ReactiveSubstances,以及意为在特定 Reactor 中被 Mixed 的普通 Substances


这是在 Fluxor 模式中的 Reactive Stores 作为 ReactiveSubstanceRedux 模式中的 Store Reducers 作为普通 Substance 之间的一种直接类比。

使用化学语义是接近flux-reactive模式的一种直观方法。毕竟,《React这个词在化学领域中本身就有定义。

目录

动机

Flask Reactor 是一个综合工具箱的基础,定义了“Flask 模式”作为在 Apple 的 Swift 平台上创建响应式应用的方式。因此,Flask 通过直观的 API 提供了最强大的功能集。

Flask 不仅超越了响应式模式的创新,还使用直观且易于实现的物理世界类比来开发表达式 API。与此同时,核心技术提供了 Redux 和 Fluxor 模式的统一实现,并提供了其他框架(如 async-flux、锁和嵌套键支持)中不可用的先进功能。这些功能是开发高级框架(如 FlaskNavigation 路由器或 FlaskAPI 管理器)所必需的。

尽管 Flask 功能强大,但其实现与最流行的框架(例如 ReSwift)一样简单。代码复杂度取决于您自己的实现,是可选的和渐进式的。

为什么选择 Flask?

Flask 提供了类似框架中找不到的独特功能

  • 链式突变
  • 捆绑多个存储
  • 使用嵌套键的 Struct reductions
  • 使用 FlaskNSRef 实现 NSObject 指针变化减少
  • 支持嵌套键的 NSDictionary 和 Dictionary 减少
  • Flux 锁和独占分发
  • 自动存档到 UserDefaults
  • 自动处理的管理附件
  • 友好的高级 API
  • 访问低级 API 以实现更细粒度的控制
  • 混合使用 Redux 和 Fluxor 模式

CocoaPods

如果您使用 CocoaPods,只需将 Flask 添加到您的 Podfile

pod 'Flask'

然后导入模块

import Flask

架构

image

上面的流程图是 Flask 管道的高级概述。

  • 绿色是 Reactor 组件
  • 蓝色是 Flux 组件
  • 粉色是 Substance 组件
  • 黄色是 State 表示

如何工作?

这些是用于与 Flask 交互的主要组件

  • 物质:您应用程序中任何数据结构
  • 混合器:定义数据变更是事务闭包
  • 反应器:将物质混合以产生反应
  • 通量:连接管道的单向总线

组件

Flask API

以下是一些您将更频繁使用的 API 高级方法

// Managing your reactor instance
Flask.attachReactor(to:mixing:)
Flask.detachReactor(from:)
Flask.getReactor(at:)

// Dispatching a substancer mixer
Flask.substances(reactTo:payload:)

// Creating non-reactive substances
Flask.newSubstance(definedBy:)
Flask.newSubstance(definedBy:named:archive:)

查看这些方法以了解更多关于低级 API 的信息。

物质

物质在最小化初始化时带有 State 结构。反应性物质需要一个额外的 SubstanceMixer 枚举来方便处理环境混合器。

  • 物质:使用 Flask.mix 便于突变
  • 反应性物质:类似于物质,但还会观察全局物质混合器。

考虑以下示例

状态

struct AppState : State {
    enum prop : StateProp{
        case counter
    }
    var counter = 0
}

物质

class Feed : Substance<AppState> {}

反应性物质

enum Mixers : SubstanceMixer {
    case Login
}
class App : ReactiveSubstance<AppState,Mixers> {
    
    override func defineMixers(){
        define(mix: . Login) { (payload, react, abort)  in
            self.prop.counter = self.prop.counter + 1
            react()
        }
    }
    
}

混合器

Flask 数据突变使用混合器执行。混合器是事务闭包,其中您可以访问和修改给定物质属性。有两种类型的混合器

  • 物质混合器:这些是在枚举中声明的,作为 ReactiveSubstace 子类定义的一部分传递。内部,物质可以根据需要 transcriptionally 订阅在 State 事务上下文中响应内部数据突变的案例。
  • Flask混合器 这些可以在任何地方通过访问与给定 Reactor 实例绑定的物质来使用。调用 reactor.mix(substance) 允许你在可以修改物质属性的 State Transaction 上下文中声明一个包含对请求物质的弱指针的闭包。

因此,虽然 物质混合器 可以应用于具有相同观察者的所有物质,但 Flask混合器 旨在进行更具体的转换,并且通过使用 ChainReaction 优势在于可以修改多个物质。

考虑以下示例

全局物质混合器。高级API

  Flask.substances(reactTo:EnvMixers.Login, payload:["username":"foo"])

Flask混合器。高级API

 Flask.getReactor(attachedTo:self)
            .mixing(self.substance!) { (substance) in
                substance.prop.counter = 10
            }.with(self.substance!) { (substance) in
                substance.prop.text = "text"
            }.andReact()

反应器

反应器 Reactor 实例(或简称 reactor)通过传递一个弱 owner 引用和一组 [物质] 来由 ReactorManager 工厂初始化。内部,框架负责处理实例被 owner 设置为 nil 的懒加载解绑和销毁。

每个 Reactor 必须定义一个处理闭包,用于接收更改回调。你可以在这里看到示例实现

    let reactor = Flask.reactor(attachedTo:owner,mixing:substance)
        
    reactor.handler={owner, reaction in
        reaction.on(AppState.prop.counter, { (change) in
            expectation.fulfill()
        })
    }

上述实现被巧妙地通过 FlaskReactor 协议包裹,这种情况下实现看起来像这样

class ViewController: UIViewController, FlaskReactor  {

    func flaskReactions(reaction: FlaskReaction) {
        
        reaction.on(AppState.prop.counter) { (change) in
                print("counter = \(substance.state.counter)")
        }

  override func viewDidLoad() {
       
        Flask.attachReactor(to:self, mixing:[substance, Subs.appReactive])
        
    }
}

Flux

在Flask中,Flux 是一个单向单次派遣的总线,它确保所有混合操作都将以原子方式进行。

所有这些都在幕后发生,你不需要与 Flux 交互,只需派遣 SubstanceMixer 事件即可。

但是,可以使用 FluxLock 暂停 Flux,并在主 Flus 暂停的过程中进行独家混合。更重要的是,你可以创建异步锁,在反应闭包中执行的操作稍后释放。

下面将介绍有关锁的更多内容。

Flux是新来的吗?

你可以在 Lin Clark 的这篇教学文章中了解有关Flux的更多信息。 Flux的卡通指南

以及官方文档: 来自Facebook的Flux

实现

以下是关于如何在Redux和Fluxor模式相关实现Flask Reactor的快速参考。

Redux样式

这是类似于ReSwift的基本实现摘要。

Substance.swift

struct AppState : State{
    
    enum prop: StateProp{
        case counter, text
    }
    
    var counter = 0
    var text = ""
}

ViewController

class ViewController: UIViewController, FlaskReactor  {
   
    func flaskReactions(reaction: FlaskReaction) {
        
        reaction.on(AppState.prop.counter) { (change) in
            print("counter = \(substance.state.counter)")
        }
        reaction.on(AppState.prop.text) { (change) in
            print("text = \(substance.state.text)")
        }
        
    }
   
    override func viewDidLoad() {
        
        Flask.attachReactor(to:self, mixing:[substance])
        produceTestReaction()
    }

    func produceTestReaction(){
        
        Flask.getReactor(attachedTo:self)
            .mixing(self.substance) { (substance) in
                substance.prop.counter = 10
            }.with(self.substance) { (substance) in
                substance.prop.text = "changed!"
            }.andReact()
        
    }

}

Fluxor样式

Fluxor模式需要更多的设置,但是对于共享物质来说非常方便。

清单

enum EnvMixers : SubstanceMixer {
    case Login
    case Logout
}

class Subs {
    static let appReactive = AppReactiveSubstance()
}

Substance.swift

struct AppState : State {
    
    enum prop : StateProp{
        case counter, title
    }
    
    var counter = 0
    var title = ""
    
}

class AppReactiveSubstance : ReactiveSubstance<AppState,EnvMixers> {
    
    override func defineMixers(){
        
        define(mix: .Login) { (payload, react, abort)  in
            self.prop.title = "signed"
            react()
        }
    }  
}

ViewController

class ViewController: UIViewController, FlaskReactor  {
       
    func flaskReactions(reaction: FlaskReaction) {
             
        reaction.on(AppState.prop.title) { (change) in
            print("global title = \(Subs.appReactive.state.title)")
        }
        
    }
 
    override func viewDidLoad() {
        
        Flask.attachReactor(to:self, mixing:[Subs.appReactive])
        produceTestReaction()
    }

    func produceTestReaction(){
    
        Flask.substances(reactTo:EnvMixers.Login, payload:["username":"foo"])
 
     }
}

特别功能

链式反应

调用mix()(又称mixing())将返回一个Flask ChainReaction实例,该实例可以进一步链式调用直到解决。一个ChainReaction具有以下方法

  • mix(substance:)
  • react()
  • abort()

要继续链式调用,只需再次调用mix(或其任何别名)。你必须在调用react()abort()(或其任何别名)之前解决事务(否则你的Flask将无法执行进一步的mix事务)。

使用高级API

 Flask.getReactor(attachedTo:self)
            .mixing(self.substanceA) { (substance) in
                substance.prop.counter = 10
                
            }.with(self.substanceB) { (substance) in
                substance.prop.text = "text"
                
            }.andReact()

使用低级API

   reactorInstance
       .mix(self.substanceA) { (substance) in
            substance.prop.counter = 10
            
        }.mix(self.substanceB) { (substance) in
            substance.prop.text = "text"
            
        }.react()

需要时,您可以创建一个 FluxLock。这将暂停执行任何混合操作,包括 ReactiveSubstancesChainReactions。您可以创建许多锁,但您必须负责释放它们,否则会变得过于活跃。

let lock = Flask.lock()
        
// perform operations while the flux is paused

lock.release()

使用锁进行异步混合

有时您需要执行一个特定的混合操作,这需要暂停所有其他混合操作,直到 FlaskReaction 解析完成。

使用 ReactiveSubstance 来执行这一点非常简单

  • 只需创建一个 FluxLock 并传递您全局的 EnvMixer 名称。
  • 像往常一样执行您的 ReactiveSubstance 混合
  • 然后在 FluxReaction 实例中的 FluxReactor,您将在 reaction.onLock? 收到对您的锁的指针,以便您可以释放它。

示例

在锁定流量上请求混合

        Flask.lock(withMixer: EnvMixers.AsyncAction)

异步释放

 reaction.on(AppState.prop.asyncResult) { (change) in
 
            //pass the reaction to an async block 
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
                //release when the operation is completed
                reaction.onLock?.release()
            });
            
   }
        

嵌套结构

可以使用嵌套结构并观察它们的更改。关于结构体的限制是所有属性都必须符合Codable协议。

如果您需要支持将对象作为顶级属性或嵌套传递,请阅读有关嵌套字典的部分。

func testStruct(){
        
        let expectation = self.expectation(description: "testStruct")
        let expectation2 = self.expectation(description: "testStruct")
        let expectation3 = self.expectation(description: "testStruct")
        
        struct nestedTestStruct:Codable{
            var foo = "bar"
            var object = FlaskNSRef(NSObject())
        }
        
        struct testStruct:Codable{
            var counter = 10
            var nest = nestedTestStruct()
        }
        
        struct state : State{
            var info = testStruct()
        }
        
        let NAME = "substanceTest\( NSDate().timeIntervalSince1970)"
        let mySubstance = Flask.newSubstance(definedBy: state.self,named:NAME, archive:false)
        mySubstance.shouldArchive = true
        
        let owner:TestOwner = TestOwner()
        let reactor = Flask.reactor(attachedTo:owner, mixing:mySubstance)

        
        reactor.handler = { owner, reaction in
            
            mySubstance.archiveNow()
            
            reaction.on("info", { (change) in
                expectation.fulfill()
            })
            reaction.on("info.counter", { (change) in
                expectation2.fulfill()
            })
            reaction.on("info.nest.foo", { (change) in
                expectation3.fulfill()
            })
        }
        
        reactor.mix(mySubstance) { (substance) in
            substance.prop.info.counter = 90
            substance.prop.info.nest.foo = "mutated"
            }.andReact()
        
        wait(for: [expectation,expectation2,expectation3], timeout: 2)
        
        let expectation4 = self.expectation(description: "must preserve after archive")
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
           
            let archivedSubstance = Flask.newSubstance(definedBy: state.self,named:NAME,archive:true)
            XCTAssert(archivedSubstance.state.info.nest.foo == "mutated", "Must preserve value")
            expectation4.fulfill()
        }
        
        wait(for: [expectation4], timeout: 4)
    }

NSObjects 和字典

对象和归档:请注意,NSObjects 不可序列化,并由 SubstanceSerializer 映射为 nil

如果您需要监视 NSObject 的更改,可以使用 FlaskRef。这将对您的对象进行包装,使其成为支持 Codable 协议的类,该协议是您 State 结构所必需的。

当监视这些键时,您将收到有关指向此对象的指针更改的通知。这将允许您监视像 UIViewController 这样的 UI 对象的更改。

struct myState : State{
   var object = FlaskRef( NSObject() )
}

如果需要处理嵌套对象的层次结构,请使用 FlaskDictRef。使用 NSDictionary 初始化这个类的实例,然后您将能够监视嵌套键的更改。

  • 在您的状态中创建一个 FlaskDictRef 属性
  • 通过将您的字典包装在 FlaskDictRef(Dictionary) 中来分配新值
  • 使用点语法观察您的嵌套键的更改。

示例

创建一个 FlaskDictRef 属性

struct AppState : State {
    
    enum prop : StateProp{
        case info
    }
    
    var info:FlaskDictRef?
}

分配值

 reactor.mix(substance){ (substance) in
 
          let data:NSDictionary = [
            "foo":"bar",
            "nest":[
                "data":"some"
            ]
            
        ]
        
       substance.prop.info = FlaskDictRef(data)
       
  }.react()

观察更改

 reaction.on("info.nest.data", { (change) in
      print(change.newValue()!)
 })

归档

归档是在您不需要在数据中执行查询或关系操作时,相对于SQL-Lite或CoreData的绝佳替代方案。请注意,此功能依赖于UserDefaults作为存储目标。

默认情况下,归档是关闭的。要启用归档,只需将两个额外参数传递给SubstanceReactiveSubstance初始化器即可。

let substance = MySubstanceClass(name:"uniqueName",archive:true)

名称必须唯一,所以请确保为您的应用程序使用适当的名字约定。

检测到更改后,物质在2秒后将被归档。通过使用属性Substance.shouldArchive,您可以在实例化后禁用归档。

您可以通过覆盖以下方法在你的Substance子类中进一步自定义此过程:

    override func archiveKeySpace()->String{
        return "1"
    }
    
    override func archiveKey()->String{
        return "Fx.\(archiveKeySpace()).\(name())"
    }
    
    override func archiveDelay()->Double{
        return 2.0
    }
    
    override func archiveDisabled()->Bool{
        return !shouldArchive
    }

内部状态属性

如果您想忽略某些State属性用于更改减少,只需在变量名前使用_前缀即可。

struct AppState : State {
    
    var _internal = "`_` use this prefix for internal vars "
    
}

如果您在状态中执行附加计算,这可能很有用。

底层API

在幕后,大多数高级功能依赖于在主Flask类上调用状态方法。

您都可以在这里找到

purgeFluxQueue()
purgeFlasks()

instance(attachedTo:mixing:)
instance(attachedTo:mixing:)

lock()
lock(withMixer:)
lock(withMixer:payload:)
removeLocks()

applyMixer(_:payload:)

attachReactor(to:mixing:)
detachReactor(from:)

您还可以访问包含所有附加Reactor实例的ReactorManager

reactors
purge()

文档

示例项目

本仓库中包含一个示例项目,位于

  • FlaskSample/

请确保运行 Pod install 以创建工作区。

该示例应用程序同时实现两种模式,以供进一步参考。

测试用例

上述示例项目还附带数十个标准 Xcode 测试单元测试用例。这是一个学习更多实现模式的好来源。

这些测试在每个部署时都会与 Travis-CI 自动运行,您可以通过上方查看健康状况以确保顺利。

代码片段

我们正在准备更多实用示例,并乐于展示您的作品!

API 文档

请注意,文档仍在制作中。

在此处提供了 Jazzy 生成的文档:[文档](https://eonfluxor.github.io/reactor/)

有问题吗?

如果您需要任何帮助,请访问我们的 GitHub 问题。如果您无法从档案中找到任何解决方案,请随时提交问题。

您也可以通过以下方式联系我们

[电子邮箱保护隐私]

关于

FlaskHassan Uriostegui 创建。