DeLorean 0.4.1

DeLorean 0.4.1

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2015年4月
SPM支持 SPM

Junior B. 维护。



DeLorean 0.4.1

  • 作者:
  • bontoJR

DeLorean

DeLorean

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是包含可能在未来某个时刻变得可用的结果的对象。

  • 如果在完成计算之前,我们说该Future未完成。
  • 如果在完成计算时有一个值或异常,我们说该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的FuturePromise实现利用了Try/Catch/Finally语句,依赖于捕获的Throwable(这包括ExceptionError)。在Swift中,我们没有Try/Catch指令,苹果明确表示不会包含它。这种语言设计选择迫使这种实现要求将Result结构作为计算块结束时的值返回。第一个例子显示了如何传递失败的请求返回的错误或成功请求的数据作为结果。

Promise

Future是通过使用future方法启动的异步计算的创建对象。Promise是Future的扩展,可以使用这种新类型创建Future。

Future被定义为只读占位符,但有时我们需要能够在这些占位符中写入。Promise被创建来实现这种行为。Promise只能实现一次,这意味着我们只能成功或失败实现一次Promise,任何对successfailure的进一步调用都将被忽略。

示例

考虑以下示例,其中通过计算生成一个值,并将其传递给另一个需要消耗此值以进行计算的进程。此传递值的流程是通过一个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

库带有很多组合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)
}

FlatMap

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

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
}

等等...

请考虑阅读关于mapfilterrecoverrecoverWithfallbackTo的代码文档。

富尔顿先生

富尔顿先生仍在非常早期的发展阶段。 此模块是德洛林汽车的响应式核心,如果您正在寻找类似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的实例,以及一个值,可以是IntDoubleStringFloatNSDate

以下是一个使用文本框的例子,当字符串长度大于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.swiftOperators.swift的实现最初由Maxwell Swadling完成,并且是Swiftz函数库的一部分。

许可权

MIT许可(MIT)

版权所有 © 2015 小尔·B.(bonto.ch)

特此授予任何获得此软件和相关文档副本(以下称为“软件”)的人,免费使用此软件的权利,不受限制,包括但不限于使用、复制、修改、合并、发布、分发、再许可权和/或销售软件副本的权利,并准许向软件提供方提供软件的人这样做,前提是以下条件

上述版权声明和本许可声明应包含在本软件的所有副本或主要部分中。

本软件按“原样”提供,不提供任何形式的保证,明示或暗示,包括但不限于对适销性、特定用途适用性和非侵权的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论责任是由于合同行为、侵权或其他,均与软件、软件的使用或其它任何形式的软件相关。