Reactor 提供了一个具有最小配置的 数据模型层。它利用以下元素来实现这一点
Reactor 然后使用流(由 ReactorFlow<T>
表示),通常在应用程序中可以看到。例如
该特定流程由 Reactor 内置提供。未来我们将提供更多。
流程不过是事件的流,在我们的情况下,它由不同的部分组成(网络、解析和持久化)。
ReactorFlow<T>
的不寻常流程。为了使 Reactor 工作,您需要确保您的模型对象符合 Mappable
协议。此协议允许您编码和解码对象。这是解析从网络获取的对象并将其存储在磁盘上所必需的。
以Author
结构体为例(可以在单元测试中找到),第一步是使Author
结构体符合Mappable
协议。
struct Author {
let name: String
}
extension Author: Mappable {
static func mapToModel(object: AnyObject) -> Result<Author, MappedError> {
guard
let dictionary = object as? [String: AnyObject],
let name = dictionary["name"] as? String
else { return Result(error: MappedError.Custom("Invalid dictionary @ \(Author.self)\n \(object)"))}
let author = Author(name: name)
return Result(value: author)
}
func mapToJSON() -> AnyObject {
return ["name": self.name]
}
}
注意:上面的实现只是一个例子,您可以使用您喜欢的任何方式。
第一个函数mapToModel
允许创建模型对象(JSONmapToJSON
是它的逆过程(模型
第二步是
let baseURL = NSURL(string: "https://myApi.com")!
let configuration = FlowConfiguration(persistenceConfiguration: .Enabled(withPath: "path_to_persistence"))
let flow: ReactorFlow<Author> = createFlow(baseURL, configuration: configuration)
let reactor: Reactor<Author> = Reactor(flow: flow)
现在,你已经准备好了reactor
,它公开了两个函数
func fetch(resource: Resource) -> SignalProducer<T, Error>
func fetchFromNetwork(resource: Resource) -> SignalProducer<T, Error>
我们发现这是最常见的两种场景
最后一部分是Resource
,它只是一个封装请求如何执行的结构体
为了额外灵活,您可以使用CoreConfiguration
和FlowConfiguration
协议。
CoreConfiguration
协议定义了Reactor的行为
public protocol CoreConfiguration {
/// When enabled, you should pass the path where it will be stored
/// Otherwise it's disabled
var persistenceConfiguration: PersistenceConfiguration { get }
/// If the `saveToPersistenceFlow`, should be part of the flow.
/// Should be `false` when the flow shouldn't
/// wait for `saveToPersistenceFlow` to finish (for example it takes
/// a long time).
/// Note: if you set it as `false` and it fails, the failure will be
/// lost, because it's not part of the flow, but injected instead .
/// `true` by default.
var shouldWaitForSaveToPersistence: Bool { get }
}
FlowConfiguration
协议定义了Reactor Flow的创建方式
public protocol FlowConfiguration {
/// If persistence should be used.
/// `true` by default.
var usingPersistence: Bool { get }
/// If reachability should be used.
/// `true` by default.
var shouldCheckReachability: Bool { get }
/// If the parser should be strict or prune the bad objects.
/// Pruning will simply remove objects that are not parseable, instead
/// of erroring the flow. Strict on the other hand as soon as it finds
/// a bad object will error the entire flow.
/// Note: if you receive an entire batch of bad objects, it will default to
/// an empty array. Witch leads to not knowing if the server has no results or
/// all objects are badly formed.
/// `true` by default.
var shouldPrune: Bool { get }
}
FlowConfiguration
协议在以下方法中使用
public func createFlow<T where T: Mappable>(baseURL: NSURL, configuration: FlowConfigurable) -> ReactorFlow<T>
public func createFlow<T where T: Mappable>(connection: Connection, configuration: FlowConfigurable) -> ReactorFlow<T>
public func createFlow<T where T: SequenceType, T.Generator.Element: Mappable>(baseURL: NSURL, configuration: FlowConfigurable) -> ReactorFlow<T>
public func createFlow<T where T: SequenceType, T.Generator.Element: Mappable>(baseURL: NSURL, configuration: FlowConfigurable) -> ReactorFlow<T>
这些是方便的提供即用ReactorFlow
的方法。请注意,如果您想使用自定义持久性(CoreData,Realm,SQLite等),您应自行创建ReactorFlow
。原因是因为默认的持久性类(InDiskPersistence.swift
)需要一个路径来保存数据。这可能与其他方法不合适(请查阅使用第三方依赖关系部分)。
如果持久数据不合适,您可以
let baseURL = NSURL(string: "https://myApi.com")!
let configuration = FlowConfiguration(persistenceConfiguration: .Disabled)
let flow: ReactorFlow<Foo> = createFlow(baseURL, configuration: configuration)
let reactor: Reactor<Foo> = Reactor(flow: flow)
至于mapToJSON
函数,您可以简单地返回一个NSNull
func mapToJSON() -> AnyObject {
return NSNull()
}
为了充分利用Reactor,请记住以下几点(这些都是ReactorFlow<T>
的属性)
var networkFlow: Resource -> SignalProducer<T, Error>
var loadFromPersistenceFlow: Void -> SignalProducer<T, Error>
var saveToPersistenceFlow: T -> SignalProducer<T, Error>
所有三个属性都故意设置为可变(var
),这样您就可以扩展特定的行为。例如,您可能对loadFromPersistenceFlow
失败的原因感兴趣并记录下来。在默认流中,这是不可能的,因为如果loadFromPersistenceFlow
失败,网络流将启动,错误将丢失。
完成这种方法的一种方式是创建默认流,然后扩展它
let reactorFlow: ReactorFlow<Author> = ...
let extendedPersistence = reactorFlow.loadFromPersistenceFlow().on(failure: { error in print(error) })
reactorFlow.loadFromPersistenceFlow = { extendedPersistence }
您可以进一步分解流,因为所有核心组件都在公共API中公开。具体来说
Reactor提供的默认流(介绍)您可以自由使用,但并非绑定。在创建自己的流时,请牢记以下事项
Reactor<T>
的fetch
函数不变
loadFromPersistenceFlow
始终先调用。如果它失败,将调用fetchFromNetwork
。Reactor<T>
的fetchFromNetwork
函数不变
networkFlow
)将始终首先被调用,如果调用成功,则会被saveToPersistenceFlow
跟随。Reactor与其他依赖项配合得相当好,并且从您的角度来说,需要做的努力最小。在前一节中,我们看到了一个ReactorFlow
的三个基本部分。
var networkFlow: Resource -> SignalProducer<T, Error>
var loadFromPersistenceFlow: Void -> SignalProducer<T, Error>
var saveToPersistenceFlow: T -> SignalProducer<T, Error>
正如所提到的,我们鼓励您根据需求对它们进行修改。对于第三方依赖项,您必须做到这一点。以下是一个如何使Alamofire兼容性的示例步骤。
NSError
成为一个Error
。您可以使用mapError
操作符。然后将其转换为Error.Network
。有了所有这些,最后一部分是
let persistenceHandler = InDiskPersistenceHandler<MyModel>(persistenceFilePath: persistencePath)
let loadFromPersistence = persistenceHandler.load
let saveToPersistence = persistenceHandler.save
let reactorFlow: ReactorFlow<MyModel> = ReactorFlow(network: myNetworkFlow, loadFromPersistenceFlow: loadFromPersistence, saveToPersistence: saveToPersistence)
createFlow
家族方法在内部遵循这种方法,因此你应该查看它们。
其他第三方依赖项将遵循相同的方法
ReactorFlow
此方法的另一个好处是,它强加了代码的清晰分离和解耦。如果您将持久性与网络耦合,那么使用Reactor将相当困难。
Reactor根据MIT许可版2.0授权。您可以在此处查看许可文件
版权所有 © 2015 MailOnline
抬头图片来自Henrique Macedo。