ReactKit 0.12.0

ReactKit 0.12.0

测试已测试
语言语言 SwiftSwift
许可 MIT
发布上次发布2015年9月
SPM支持 SPM

Maintained by Yasuhiro Inami.



ReactKit 0.12.0

  • By
  • Yasuhiro Inami

Swift 反应式编程。

版本 0.10.0 更新日志 (2015/04/23)

  • [x] 将 Signal<T> 重命名为 Stream<T>
  • [x] 将 Stream 运算符例如 mergemergeAllmergeInner 重命名为始终使用 **All (stream-array) 和 **Inner (nested-stream) 命名约定
  • [x] 添加流管道操作符 |> 和流生产者管道操作符 |>> 代替点方法链接语法。
  • [x] 添加许多有用的流操作(比如 distinct)。

这是一个 破坏性更改。请参阅 #26版本 0.10.0 发行说明 了解更多信息。

如何安装

请参见 Wiki 页面

示例

对于 UI 示例,请参见 ReactKit/ReactKitCatalog

键值观察

// create stream via KVO
self.obj1Stream = KVO.stream(obj1, "value")

// bind stream via KVC (`<~` as binding operator)
(obj2, "value") <~ self.obj1Stream

XCTAssertEqual(obj1.value, "initial")
XCTAssertEqual(obj2.value, "initial")

obj1.value = "REACT"

XCTAssertEqual(obj1.value, "REACT")
XCTAssertEqual(obj2.value, "REACT")

要移除流绑定,只需释放 stream 本身(或调用 stream.cancel())。

self.obj1Stream = nil   // release stream & its bindings

obj1.value = "Done"

XCTAssertEqual(obj1.value, "Done")
XCTAssertEqual(obj2.value, "REACT")

如果您想观察 Swift.ArrayNSMutableArray 的更改,请使用 拉取请求 #23 中的 DynamicArray 功能。

NSNotification

self.stream = Notification.stream("MyNotification", obj1)
    |> map { notification -> NSString? in
        return "hello" // convert NSNotification? to NSString?
    }

(obj2, "value") <~ self.stream

通常,NSNotification 本身是无用的值,用于与其他对象绑定,因此请使用如 map(f: T -> U)流操作将其转换。

要了解更多关于 |> 管道操作符的信息,请参阅 流管道

目标-操作

// UIButton
self.buttonStream = self.button.buttonStream("OK")

// UITextField
self.textFieldStream = self.textField.textChangedStream()

^{ println($0) } <~ self.buttonStream     // prints "OK" on tap

// NOTE: ^{ ... } = closure-first operator, same as `stream ~> { ... }`
^{ println($0) } <~ self.textFieldStream  // prints textField.text on change

复杂示例

以下示例来自

其中描述了4个 UITextField,在特定条件下启用/禁用 UIButton (在 ReactKit/ReactKitCatalog 中可获取演示)

let usernameTextStream = self.usernameTextField.textChangedStream()
let emailTextStream = self.emailTextField.textChangedStream()
let passwordTextStream = self.passwordTextField.textChangedStream()
let password2TextStream = self.password2TextField.textChangedStream()

let allTextStreams = [usernameTextStream, emailTextStream, passwordTextStream, password2TextStream]

let combinedTextStream = allTextStreams |> merge2All

// create button-enabling stream via any textField change
self.buttonEnablingStream = combinedTextStream
    |> map { (values, changedValue) -> NSNumber? in

        let username: NSString? = values[0] ?? nil
        let email: NSString? = values[1] ?? nil
        let password: NSString? = values[2] ?? nil
        let password2: NSString? = values[3] ?? nil

        // validation
        let buttonEnabled = username?.length > 0 && email?.length > 0 && password?.length >= MIN_PASSWORD_LENGTH && password == password2

        // NOTE: use NSNumber because KVO does not understand Bool
        return NSNumber(bool: buttonEnabled)
    }

// REACT: enable/disable okButton
(self.okButton, "enabled") <~ self.buttonEnablingStream!

更多示例,请参阅 XCTestCases。

如何工作

ReactKit 是基于强大的 SwiftTask(JavaScript Promise-like)库,它允许您使用其 resume & progress 特性(ReactKit 中的 react()<~ 操作符)在一段时间内连续地启动和传输多个事件(KVO、NSNotification、目标-操作等)。

与拥有“热”和“冷”可观察对象基本概念的响应式扩展(Rx)库不同,ReactKit 将它们优雅地整合为一个 热 + 暂停(懒加载)流 Stream<T> 类。懒加载流将通过 react()<~ 操作符自动恢复。

以下是架构的一些差异:

响应式扩展(Rx) ReactKit
基本类 热可观察对象(广播)
冷可观察对象(懒加载)
Stream<T>
生成 冷可观察对象(可克隆性) Void -> Stream<T>
(等价于 Stream<T>.Producer)
订阅 observable.subscribe(onNext, onError, onComplete) stream.react {...}.then {...}
(方法链式)
暂停 pausableObservable.pause() stream.pause()
丢弃 disposable.dispose() stream.cancel()

流管道化

流可以通过使用 |> 流管道化操作符流操作 组合。

例如,一个非常常见的使用 searchTextStream增量搜索技术 看起来可能如下所示:

let searchResultsStream: Stream<[Result]> = searchTextStream
    |> debounce(0.3)
    |> distinctUntilChanged
    |> map { text -> Stream<[Result]> in
        return API.getSearchResultsStream(text)
    }
    |> switchLatestInner

在某些场景(例如 repeat())中,您可能希望使用可克隆的 Stream<T>.ProducerVoid -> Stream<T>)而不是简单的 Stream<T>。在这种情况下,您可以使用 |>> 流生产者管道化操作符

// first, wrap stream with closure
let timerProducer: Void -> Stream<Int> = {
    return createTimerStream(interval: 1)
        |> map { ... }
        |> filter { ... }
}

// then, use `|>>`  (streamProducer-pipelining operator)
let repeatTimerProducer = timerProducer |>> repeat(3)

但在上述情况下,使用闭包包裹总是会变得繁琐,因此您也可以使用 |>> 操作符用于 Stream流操作(感谢 @autoclosure)。

let repeatTimerProducer = createTimerStream(interval: 1)
    |>> map { ... }
    |>> filter { ... }
    |>> repeat(3)

函数

流操作

  • 单流

    • 变换
      • asStream(ValueType)
      • map(f: T -> U)
      • flatMap(f: T -> Stream<U>)
      • map2(f: (old: T?, new: T) -> U)
      • mapAccumulate(initialValue, accumulator)(别名为 scan
      • buffer(count)
      • bufferBy(stream)
      • groupBy(classifier: T -> Key)
    • 筛选
      • filter(f: T -> Bool)
      • filter2(f: (old: T?, new: T) -> Bool)
      • take(count)
      • takeUntil(stream)
      • skip(count)
      • skipUntil(stream)
      • sample(stream)
      • distinct()
      • distinctUntilChanged()
    • 组合
      • merge(stream)
      • concat(stream)
      • startWith(initialValue)
      • combineLatest(stream)
      • zip(stream)
      • catch(stream)
    • 计时
      • delay(timeInterval)
      • interval(timeInterval)
      • throttle(timeInterval)
      • debounce(timeInterval)
    • 收集
      • reduce(intialValue, accumulator)
    • 其他工具
      • peek(f: T -> Void)(用于注入副作用,例如调试日志)
      • customize(...)
  • 对于数组流

    • mergeAll(streams)
    • merge2All(streams)mergeAllcombineLatestAll 的一般化方法)
    • combineLatestAll(streams)
    • zipAll(streams)
  • 对于嵌套流(《Stream<Stream<T>》)

    • mergeInner(nestedStream)
    • concatInner(nestedStream)
    • switchLatestInner(nestedStream)
  • 对于流生产者(《Void<- Stream<T>》)

    • prestart(bufferCapacity)(别名为 replay
    • repeat(count)
    • retry(count)

助力

  • 创建

    • Stream.once(value)(别名为 just
    • Stream.never()
    • Stream.fulfilled()(别名为 empty
    • Stream.rejected()(别名为 error
    • Stream.sequence(values)(即 Rx.fromArray)
    • Stream.infiniteSequence(initialValue, iterator)(即
  • 其他工具

    • ownedBy(owner: NSObject)(易于强引用以保持流存活)

依赖

参考

许可证

MIT