DeLorean 是一个 Swift 的功能性响应式工具包。
// 目前不支持 Swift 1.2
DeLorean 是一个框架(仍处于 WIP),它致力于在 Swift 中以函数式风格提供异步编程一些基本功能的完整实现。该框架希望覆盖以下两个主要概念:
Swift 正在成为函数式编程语言,引起了大量争论。本框架已被创建来以函数式风格实现两个主要概念:并行非阻塞操作(未来/承诺)和响应式编程(流)。
这两个不同领域可以协同工作是因为未来和承诺是适用于明确操作的概念,如 HTTP 请求或 JSON 反序列化。另一方面,响应式编程非常适合处理具有随机输入的操作,如 UI 交互(例如:TextField 输入)、位置管理器更新、外部配件交互、WebSockets 等...
您可以自由地为 DeLorean 的开发做出贡献,请检查仓库,添加您想要的特性并创建拉取请求。此仓库使用 GitFlow 工作流程,如果您不了解如何使用,请在发送拉取请求之前检查。不允许向 master 分支 发起拉取请求(如果不是针对极其紧急的热修复)。请考虑为功能创建单独的测试,以便未来的更新可以避免破坏当前正在工作的代码。
我强烈建议您阅读以下资源,以了解这个框架的灵感来源:
或者查看以下在线课程
该框架正在开发中,当前状态将定期更新。当前的开发状态是
虫洞发生器已完成,包括处理承诺和未来的所有主要操作,例如:flatMap、zip、then、fallbackTo 等...
融合模块仍在早期开发阶段,请现在不要使用此模块。
对 Carthage 和 CocoaPods 的支持尚未测试或包含。
这个概念非常简单,Future是一种可以为当前需要计算的某个结果创建的占位符对象。Future提供了一种以高效和非阻塞的方式并行执行多个操作的好方法。默认情况下,Future的结果是并发计算的,之后可以收集。以这种方式组合任务往往会产生更安全、异步、非阻塞的并行代码。虽然Future是只读的占位符,Promise是Future的扩展,也支持写入。
注意:此实现满足Scala兼容
,因此关于Future和Promise的Scala文档(Scala的Future和Promise文档)也是有效的。
Future是包含可能在未来某个时刻变得可用的结果的对象。
所以,要求Future的计算结果有以下几种情况:
nil
。Value(t)
。Error(e)
。一旦计算出结果,所有已注册的回调将被触发。
使用Future
的基本示例是下载文件,解析和显示。
future {
var response: NSURLResponse?
var error : NSError? = nil
if let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: &error) {
return Result(data)
}
return Result(error!)
}.onSuccess(){ (result: NSData) in
//Function parseDataAndUpdateUI just parse the data and basically displays the result
self.parseDataAndUpdateUI(result)
return
}.onFailure(){ (e: NSError) in
//Function displayError just displays the error
self.displayError(e)
return
}
Future
也可以用来异步计算Void
块。
futureVoid {
self.performLongTask() // defined as: func performLongTask() {...}
}
Scala/Java的Future
和Promise
实现利用了Try/Catch/Finally
语句,依赖于捕获的Throwable
(这包括Exception
和Error
)。在Swift中,我们没有Try/Catch
指令,苹果明确表示不会包含它。这种语言设计选择迫使这种实现要求将Result
结构作为计算块结束时的值返回。第一个例子显示了如何传递失败的请求返回的错误或成功请求的数据作为结果。
Future是通过使用future方法启动的异步计算的创建对象。Promise是Future的扩展,可以使用这种新类型创建Future。
Future被定义为只读占位符,但有时我们需要能够在这些占位符中写入。Promise被创建来实现这种行为。Promise只能实现一次,这意味着我们只能成功或失败实现一次Promise,任何对success
或failure
的进一步调用都将被忽略。
考虑以下示例,其中通过计算生成一个值,并将其传递给另一个需要消耗此值以进行计算的进程。此传递值的流程是通过一个Promise
实现的。
let stringURL = "http://rate-exchange.appspot.com/currency?from=USD&to=EUR"
var response: NSURLResponse?
var error : NSError? = nil
let contentRequest = NSURLRequest(URL: NSURL(string: stringURL)!)
let p = Promise<NSData>()
let f = p.future()
futureVoid {
if let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: &error) {
p.success(data)
} else {
p.failure(error!)
}
}
f.onFailure(){ (e: NSError) in
//Function displayError just displays the error
self.displayError(e)
}
f.onSuccess{ data in
//Process the data and eventually display it
self.displayData(data)
}
库带有很多组合Future的方法,以下是一些示例
我们可以使用then
函数来链式调用Future。
// Example response:
// {"to": "CHF", "rate": 0.96462400000000004, "from": "USD"}
let stringURL = "http://rate-exchange.appspot.com/currency?from=USD&to=CHF"
future {
var response: NSURLResponse?
var error : NSError? = nil
let contentRequest = NSURLRequest(URL: NSURL(string: stringURL)!)
if let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: &error) {
return Result(data)
}
return Result(error!)
}.then { (data: NSData) in
var error : NSError? = nil
if let dict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as! Dictionary<String, AnyObject>? {
return Result(dict)
}
return Result(error!)
}.onSuccess(){ (result: Dictionary<String, AnyObject>) in
self.processData(result)
}.onFailure(){ error in
self.handleError(e)
}
Fmap
通过将一个函数应用于此Future成功的结果来创建一个新的Future,该函数的结果将是一个新的Future。
let f = future {
return Result(fibonacci(35)) //returns 9'227'465
}.fmap{ value in
return future {
return Result(value + fibonacci(35)) //returns 9'227'465
}
}
f.onSuccess() { value in
// value is 18'454'930 (9'227'465 * 2)
}
Zip
函数将此和that Future的值组合起来,并创建一个新的Future,其中包含它们结果的元组。
如果此Future失败,则结果Future将以相同的错误失败。否则,如果that Future失败,则结果Future将以that中存储的错误失败。
警告!当前Swift的实现无法与纯泛型元组一起使用。为了解决这个问题,我们正在使用数组来对当前的结果元组进行封装。一旦Swift能够正确处理泛型和元组,我们将用更好的实现来更改此方法。
// Example response:
// {"to": "CHF", "rate": 0.96462400000000004, "from": "USD"}
let usdToChfURL = "http://rate-exchange.appspot.com/currency?from=USD&to=CHF"
let chfToEurURL = "http://rate-exchange.appspot.com/currency?from=CHF&to=EUR"
let usdToChf : Future<NSData> = future {
var response: NSURLResponse?
var error : NSError? = nil
let contentRequest = NSURLRequest(URL: NSURL(string: usdToChfURL)!)
if let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: &error) {
return Result<NSData>(data)
}
return Result(error!)
}
let chfToEur : Future<NSData> = future {
var response: NSURLResponse?
var error : NSError? = nil
let contentRequest = NSURLRequest(URL: NSURL(string: chfToEurURL)!)
if let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: &error) {
return Result<NSData>(data)
}
return Result(error!)
}
let zipped = usdToChf.zip(that: chfToEur)
zipped.onSuccess { value in
let usdToChfData = value[0].0
let chfToEurData = value[0].1
// Process both values
}
请考虑阅读关于map
、filter
、recover
、recoverWith
和fallbackTo
的代码文档。
富尔顿先生仍在非常早期的发展阶段。 此模块是德洛林汽车的响应式核心,如果您正在寻找类似Rx扩展提供实现的实现,您可能已经找到了。
注意:经过几小时的思想挣扎,已经决定不保持观察者、可观察的等名称。我不认为它们非常适合Cocoa世界。关于它的反馈将非常受赞赏。
发射器
是一种发射事件的实体。事件可以是任何类型,从简单的Int
到复杂对象,包括UI事件。发射器可以用作跟踪用户位置,产生当前位置的坐标,或者它可以用来响应用户在按钮、滑块或文本框上的输入。
要创建类型为Int
的发射器
,您可以直接使用简单的初始化方法。
let emitter = Emitter<Int>()
然后您可以发射值,完成发射器或使其失败。
emitter.emit(5)
// Error requires an instance of NSError
emitter.error(e)
emitter.complete()
发射器是可组合的,这意味着我们可以合并、连接、串联等两个或更多的发射器,生成新的发射器。
订阅
是一种通用的处理基于推送的机制。在其他资源中,它也被称为观察者。订阅
是一个表示通知或事件处理器(也称为消费者)的对象。
订阅的最佳方式是在发射器
上调用subscribe
方法。
let subscription = Subscription<Int>()
emitter.subscribe(subscription)
另一种方式是请求从发射器
中获取一个空订阅。
let subscription = emitter.subscription()
在iOS中,UIControl
的子类有一个默认的发射器
对象,它会触发类型为ControlEvent
的对象。一个ControlEvent
需要一个关键字,是UIControlEvents
的实例,以及一个值,可以是Int
、Double
、String
、Float
或NSDate
。
以下是一个使用文本框的例子,当字符串长度大于3个字符时才会触发搜索
// e is an instance of UIControlEvents<String>
searchTextField?.emitter.filter({ e in
return count(e.value) > 3
}).subscription().onNext() { event in
self.search(term: event.value)
}
我们也可以用相同的流程过滤掉UIButtons
searchBtn.emitter.filter({e in return e.key == .TouchUpInside}).subscription().onNext(){ _ in
self.search(term: searchTextField?.text)
}
调度程序
提供了一组方法来访问常用的执行上下文,并且作为所有调度程序的基本类。
预定义的调度程序包括
/// The scheduler where all UI events are performed
public static let UIScheduler = Scheduler(ExecutionContext.mainContext)
/// The default scheduler
public static let Default = Scheduler()
/// The background scheduler for very time consuming producers
public static let BackgroundScheduler = Scheduler(ExecutionContext.concurrentContext)
注意:UI调度程序在主队列上调度。
Result.swift
和Operators.swift
的实现最初由Maxwell Swadling完成,并且是Swiftz函数库的一部分。
MIT许可(MIT)
版权所有 © 2015 小尔·B.(bonto.ch)
特此授予任何获得此软件和相关文档副本(以下称为“软件”)的人,免费使用此软件的权利,不受限制,包括但不限于使用、复制、修改、合并、发布、分发、再许可权和/或销售软件副本的权利,并准许向软件提供方提供软件的人这样做,前提是以下条件
上述版权声明和本许可声明应包含在本软件的所有副本或主要部分中。
本软件按“原样”提供,不提供任何形式的保证,明示或暗示,包括但不限于对适销性、特定用途适用性和非侵权的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论责任是由于合同行为、侵权或其他,均与软件、软件的使用或其它任何形式的软件相关。