Swift 反应式编程。
Signal<T>
重命名为 Stream<T>
merge
、mergeAll
、mergeInner
重命名为始终使用 **All
(stream-array) 和 **Inner
(nested-stream) 命名约定|>
和流生产者管道操作符 |>>
代替点方法链接语法。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.Array
或 NSMutableArray
的更改,请使用 拉取请求 #23 中的 DynamicArray
功能。
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>.Producer
(Void -> 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)
(mergeAll
和 combineLatestAll
的一般化方法)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)
(易于强引用以保持流存活)