title | categories | tags | toc | author_profile | sidebar | ||||
---|---|---|---|---|---|---|---|---|---|
开始使用 RxSwift |
|
|
true |
false |
|
信号即序列
RxSwift
项目尝试与 ReactiveX.io 项目保持一致,一般的跨平台的文档和指导同样适用于 RxSwift
观察者模式(Observable<Element>
sequence)和普通的 sequence 的等价是 Rx 中最重要的概念。
每个 Observable
sequence 就是一个 sequence。Observable
相对于 Swift 的 Sequence
的优势在于可以异步地接收元素。这是 RxSwift 的核心,接下来的文档都是对这个想法的扩展。
Observable
(ObservableType
) 等价于Sequence
ObservableType.subscribe
方法等价于Sequence.makeIterator
- Observer (callback) 需要被传递给
ObservableType.subscribe
方法来接受序列元素而不是对返回的iterator
调用next()
Sequences 是一个简单熟悉的概念,容易可视化
人类是拥有庞大视觉大脑皮层的生物,可以很容易理解可视化之后的概念
我们可以通过在序列更高级的操作上模拟每个 Rx 操作符内部的事件状态机的方式来提升我们的认知
如果我们在构建异步的系统但是没有使用 Rx,很可能意味着我们的代码有很多的状态机和暂态需要去模拟,而不是以一种抽象的方式
列表和序列大概是数学家和程序员首先要学习的概念之一
下面是一个数字序列:
--1--2--3--4--5--6--| // terminates normally
下面是一个字符序列:
--a--b--a--a--a---d---X // terminates with error
有些序列是有限的,有些序列是无限的,比如按钮的点击事件产生的序列:
---tap-tap-------tap--->
这些被称为弹珠图,更多的弹珠图在http://rxmarbles.com
下面举例说明 Rx 中 sequence 的语法:
next* (error | completed)?
表达的信息如下:
- Sequences 可以有 0 个或者多个元素
- 一个
error
或者completed
事件被接收之后,这个序列就不能产生其他的元素了
Rx 中使用推送接口(回调)来描述序列
enum Event<Element> {
case next(Element) // next element of a sequence
case error(Swift.Error) // sequence failed with error
case completed // sequence terminated successfully
}
class Observable<Element> {
func subscribe(_ observer: Observer<Element>) -> Disposable
}
protocol ObserverType {
func on(_ event: Event<Element>)
}
当一个序列发送 completed
或 error
事件的时候,这个序列用来计算元素占用的所有的内部资源将被释放
如果要取消序列元素的产生并且立马释放资源,对返回的订阅调用 dispose
方法
如果一个序列会在有限的时间内结束,不调用 dispose
或者 disposed(by: disposeBag)
不会造成永久的资源泄漏。然而,这些资源会在序列完成之前一直被使用,序列完成的情况包括结束了元素的产生或者返回了错误。
如果一个序列不会自己终止,比如按钮的连续点击,资源会一直被占用直到 dispose
被手动调用,在 disposeBag
中被自动调用,使用了 takeUntil
操作符,或者其它的方式。
使用 DisposeBag
或者 takeUntil
操作符来保证资源被正确清理是鲁棒性比较好的一种方式。我们推荐在生产中使用它们,即使序列会在有限的时间内结束
如果你好奇为什么 Swift.Error
不是范型,你可以在这里找到解释
还有另外的方式可以终止被订阅的序列。当我们结束了一个序列想要释放用来计算和传递元素所占用的资源时,可以通过一个订阅调用 dispose
下面是一个使用 interval
操作符的例子:
let scheduler = SerialDispatchQueueScheduler(qos: .default)
let subscription = Observable<Int>.interval(.milliseconds(300), scheduler: scheduler)
.subscribe { event in
print(event)
}
Thread.sleep(forTimeInterval: 2.0)
subscription.dispose()
This will print:
0
1
2
3
4
5
通常来说你不需要手动调用
dispose
方法,这里仅仅是为了说明
手动调用 dispose
通常来说可能会造成坏的代码,可以用更好的方式来清理订阅,比如 DisposeBag
,takeUntil
操作符,或者其它的机制。
所以在 dispose
方法调用之后还有有东西打印吗?答案是:看情况
- 如果
scheduler
是 serial scheduler (如:MainScheduler
) 并且dispose
方法在同一个 serial scheduler 上调用,答案就是 no - 否则就是 yes
你可以在这里找到更多关于 scheduler 的信息
简单来说你有两个并行的处理过程:
- 一个在产生元素
- 另一个在清理订阅
“在调用 dispose
之后还可以有东西打印吗?”的问题在上面所说的两个处理过程运行在不同的 schedulers 上的时候根本就说不通。
看看其它的例子来验证一下(observeOn
在这里有解释)
假设我们有如下的代码:
let subscription = Observable<Int>.interval(.milliseconds(300), scheduler: scheduler)
.observeOn(MainScheduler.instance)
.subscribe { event in
print(event)
}
// ....
subscription.dispose() // called from main thread
在 dispose
调用之后,不会再有东西打印。这是可以保证的
再看下面的例子:
let subscription = Observable<Int>.interval(.milliseconds(300), scheduler: scheduler)
.observeOn(serialScheduler)
.subscribe { event in
print(event)
}
// ...
subscription.dispose() // executing on same `serialScheduler`
在 dispose
调用之后,不会再有东西打印。这是可以保证的
Dispose bags 给了 Rx 类似于 ARC 的表现
当一个 DisposeBag
被释放的时候,它会对添加进 DisposeBag
中的每一个 disposable
对象都调用一次 dispose
方法
DisposeBag
没有 dispose
方法,所以不能手动调用 dispose 来清理资源,如果需要立刻清理资源,可以创建一个新的 bag 来替换原来的
self.disposeBag = DisposeBag()
这样可以清理原来的引用并清理资源
如果还是想要手动地显式清理,可以使用 CompositeDisposable
。它有我们期望的和 dispose
一样的表现,但是实现的不同是,当调用 dispose
方法时,它会立马清理新加进来的 disposable
对象
还可以使用 takeUntil
操作符来实现在 dealloc
的时候自动清理订阅
sequence
.takeUntil(self.rx.deallocated)
.subscribe {
print($0)
}
还有一些另外的保证,所有的序列 producers(Observable
s)必须遵守
它们在哪个线程生成元素并不重要,但是如果它们生成了一个元素然后把生成的元素发送到观察者 observer.on(.next(nextElement))
,它们不能再发送下一个元素知道 observer.on
方法执行结束
并且在 .next
没有结束的时候 Producers 不能发送终止事件 .completed
或者 .error
简言之,考虑下面的例子:
someObservable
.subscribe { (e: Event<Element>) in
print("Event processing started")
// processing
print("Event processing ended")
}
打印顺序一定是:
Event processing started
Event processing ended
Event processing started
Event processing ended
Event processing started
Event processing ended
绝对不会出现:
Event processing started
Event processing started
Event processing ended
Event processing ended
关于 Observable
有一个重要的事情需要理解
当一个 observable 被创建的时候,它并没有执行任何工作
比如说一个网络请求的 observable 被创建,实际并没有发出网络请求,被订阅之后才发起请求
Observable
可以通过很多种方式生成元素,有些会引起副作用,有些会进入现有的运行进程中,比如鼠标的点击事件等。
但是,如果你只是调用一个方法返回了 Observable
,并没有执行序列的生成并且也没有副作用。Observable
仅仅定义了序列该如何生成和什么使用什么参数生成。序列的实际生成发生在 subscribe
方法调用之后。
看下面的例子,假设你有一个类似原型的方法:
func searchWikipedia(searchTerm: String) -> Observable<Results> {}
let searchForMe = searchWikipedia("me")
// no requests are performed, no work is being done, no URL requests were fired
let cancel = searchForMe
// sequence generation starts now, URL requests are fired
.subscribe(onNext: { results in
print(results)
})
有很多方法可以创建自定义的 Observable
序列,最简单的方式大概就是使用 create
方法
RxSwift 提供了一个方法来创建一个返回一个元素给订阅者的序列,这个方法就是 just
,下面是我们自己的实现。
具体的实现
func myJust<E>(_ element: E) -> Observable<E> {
return Observable.create { observer in
observer.on(.next(element))
observer.on(.completed)
return Disposables.create()
}
}
myJust(0)
.subscribe(onNext: { n in
print(n)
})
上面的代码将会打印:
0
那什么是 create
方法呢?
它只是一个可以让你容易使用 Swift 的闭包实现 sbuscribe
方法的便利方法。就像 subscribe
方法一样,create
方法需要一个参数,observer
,然后返回 disposable
对象
序列的这种实现实际上是同步的,它将会生成元素然后在 subscribe
调用返回 disposable
之前终止。因此,返回什么 disposable
并不重要,生成元素的过程不会被打断。
当生成同步序列的时候,通常返回的 disposable
对象是 NopDisposable
的单例。
来看另一个例子,创建 observable
返回一个数组中的元素
具体的实现如下
func myFrom<E>(_ sequence: [E]) -> Observable<E> {
return Observable.create { observer in
for element in sequence {
observer.on(.next(element))
}
observer.on(.completed)
return Disposables.create()
}
}
let stringCounter = myFrom(["first", "second"])
print("Started ----")
// first time
stringCounter
.subscribe(onNext: { n in
print(n)
})
print("----")
// again
stringCounter
.subscribe(onNext: { n in
print(n)
})
print("Ended ----")
输出结果是:
Started ----
first
second
----
first
second
Ended ----
尝试一下更有意思的事情,创建一个前面用到过的 interval
操作符
下面的实现等价于 dispatch queue schedulers
func myInterval(_ interval: DispatchTimeInterval) -> Observable<Int> {
return Observable.create { observer in
print("Subscribed")
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
timer.schedule(deadline: DispatchTime.now() + interval, repeating: interval)
let cancel = Disposables.create {
print("Disposed")
timer.cancel()
}
var next = 0
timer.setEventHandler {
if cancel.isDisposed {
return
}
observer.on(.next(next))
next += 1
}
timer.resume()
return cancel
}
}
let counter = myInterval(.milliseconds(100))
print("Started ----")
let subscription = counter
.subscribe(onNext: { n in
print(n)
})
Thread.sleep(forTimeInterval: 0.5)
subscription.dispose()
print("Ended ----")
输出结果是
Started ----
Subscribed
0
1
2
3
4
Disposed
Ended ----
如果写成下面这样
let counter = myInterval(.milliseconds(100))
print("Started ----")
let subscription1 = counter
.subscribe(onNext: { n in
print("First \(n)")
})
let subscription2 = counter
.subscribe(onNext: { n in
print("Second \(n)")
})
Thread.sleep(forTimeInterval: 0.5)
subscription1.dispose()
Thread.sleep(forTimeInterval: 0.5)
subscription2.dispose()
print("Ended ----")
输出结果是
Started ----
Subscribed
Subscribed
First 0
Second 0
First 1
Second 1
First 2
Second 2
First 3
Second 3
First 4
Second 4
Disposed
Second 5
Second 6
Second 7
Second 8
Second 9
Disposed
Ended ----
每个对 subscription
的订阅者通常生成自己独立的元素序列。操作符默认是无状态的。无状态的操作符远远多余有状态的
如果我们希望多个 observer 通过同一个订阅共享事件(元素)呢?
有两个事情需要定义:
- 如何去处理在新的订阅者订阅之前已经被传递过的元素(重传最后一个,重传所有,重传最后的 n 个)
- 如何决定什么时候触发共享的订阅(引用数,手动触发,其它的算法)
通常的选择是 replay(1).refCount()
的组合,也就是 share(replay: 1)
let counter = myInterval(.milliseconds(100))
.share(replay: 1)
print("Started ----")
let subscription1 = counter
.subscribe(onNext: { n in
print("First \(n)")
})
let subscription2 = counter
.subscribe(onNext: { n in
print("Second \(n)")
})
Thread.sleep(forTimeInterval: 0.5)
subscription1.dispose()
Thread.sleep(forTimeInterval: 0.5)
subscription2.dispose()
print("Ended ----")
This will print
Started ----
Subscribed
First 0
Second 0
First 1
Second 1
First 2
Second 2
First 3
Second 3
First 4
Second 4
# First 5,应该没有这行输出,原文档错误
Second 5
Second 6
Second 7
Second 8
Second 9
Disposed
Ended ----
现在只有一个
Subscribed
和Disposed
事件
URL observables 的表现也是等价的
下面是用 Rx 对 HTTP 请求的封装,和 interval
的模式非常相近
extension Reactive where Base: URLSession {
public func response(request: URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)> {
return Observable.create { observer in
let task = self.base.dataTask(with: request) { (data, response, error) in
guard let response = response, let data = data else {
observer.on(.error(error ?? RxCocoaURLError.unknown))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response)))
return
}
observer.on(.next((httpResponse, data)))
observer.on(.completed)
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
}
RxSwift 实现了很多操作符
所有操作符的弹珠图可以查看http://reactivex.io/
几乎所有的操作符都在 Playgrounds 中有演示
如果你需要一个操作符但是又不知道怎么找到它,可以查看操作符决策树
有两种方式可以可以创建自定义的操作符
内部的代码都是用了高度优化版本的操作符,所以他们不是最好的辅导材料。这也是为什么鼓励使用标准操作符的原因
幸运的有一种简单的方式去创建操作符。创建新的操作符实际上就是创建 observable
,并且前面的章节已经描述了怎么做
下面我们来看看如何实现一个未优化过的 map
操作符
extension ObservableType {
func myMap<R>(transform: @escaping (E) -> R) -> Observable<R> {
return Observable.create { observer in
let subscription = self.subscribe { e in
switch e {
case .next(let value):
let result = transform(value)
observer.on(.next(result))
case .error(let error):
observer.on(.error(error))
case .completed:
observer.on(.completed)
}
}
return subscription
}
}
}
调用我们自定义的 map
:
let subscription = myInterval(.milliseconds(100))
.myMap { e in
return "This is simply \(e)"
}
.subscribe(onNext: { n in
print(n)
})
输出的结果:
Subscribed
This is simply 0
This is simply 1
This is simply 2
This is simply 3
This is simply 4
This is simply 5
This is simply 6
This is simply 7
This is simply 8
...
如果在某些情况下用自定义的操作符难以解决问题呢?你可以跳出 Rx 的单一世界,用命令式编程的方式执行任务,然后使用 Subject
再将结果再传递给 Rx
这不是一件可以经常做的事情,可能会导致不良代码,但是还是可以实现的。
let magicBeings: Observable<MagicBeing> = summonFromMiddleEarth()
magicBeings
.subscribe(onNext: { being in // exit the Rx monad
self.doSomeStateMagic(being)
})
.disposed(by: disposeBag)
//
// Mess
//
let kitten = globalParty( // calculate something in messy world
being,
UIApplication.delegate.dataSomething.attendees
)
kittens.on(.next(kitten)) // send result back to rx
//
// Another mess
//
let kittens = BehaviorRelay(value: firstKitten) // again back in Rx monad
kittens.asObservable()
.map { kitten in
return kitten.purr()
}
// ....
当你这么做的时候,其他人可能会在其它地方些这样的代码👇
kittens
.subscribe(onNext: { kitten in
// do something with kitten
})
.disposed(by: disposeBag)
所以尽量不要这样做
如果你不确定某些操作符如何工作,playgrounds 几乎包括了所有的操作符,并且准备了小例子来描述他们的表现
使用 playgrounds:
- 打开 Rx.xcworkspace
- build RxSwift 的 macOS scheme
- 打开 Rx.playground
查看 playgrounds 中例子的结果,需要打开 Assistant Editor
,View > Assistant Editor > Show Assistant Editor
有两种错误处理机制
错误处理非常直接,如果一个序列因为错误终止,那么所有依赖的序列都会因为错误终止,符合通常的短路逻辑
你可以使用 catch
操作符来恢复 observable
的失败,catch
操作符中还在基本的 error
信息基础上有各种各样的补充信息,可以让你以更细节的方式恢复序列
还可以使用 retry
操作符来重试错误的序列
当编写优雅的 RxSwift/RxCocoa
代码的时候,你可能强烈依赖于编译器来推断 Observable
的类型,这是 Swift 优秀的原因,但是有时候也会令人泄气
images = word
.filter { $0.containsString("important") }
.flatMap { word in
return self.api.loadFlickrFeed("karate")
.catchError { error in
return just(JSON(1))
}
}
如果编译器报告了错误,可以先增加 catch
的返回值声明
images = word
.filter { s -> Bool in s.containsString("important") }
.flatMap { word -> Observable<JSON> in
return self.api.loadFlickrFeed("karate")
.catchError { error -> Observable<JSON> in
return just(JSON(1))
}
}
如果不成功,可以继续添加更多的类型声明直到定位到了错误
images = word
.filter { (s: String) -> Bool in s.containsString("important") }
.flatMap { (word: String) -> Observable<JSON> in
return self.api.loadFlickrFeed("karate")
.catchError { (error: Error) -> Observable<JSON> in
return just(JSON(1))
}
}
建议首先增加闭包的返回值和参数的声明
通常在你解决了错误之后,你就可以删除添加的声明以保持代码的整洁
只使用调试器是有用的,但是通常使用 debug
操作符将会更加的有效率。debug
操作符会打印所有的事件到标准输出并且你可以添加自定义的标签
debug
的表现类似于探针,下面是一个使用例子:
let subscription = myInterval(.milliseconds(100))
.debug("my probe")
.map { e in
return "This is simply \(e)"
}
.subscribe(onNext: { n in
print(n)
})
Thread.sleepForTimeInterval(0.5)
subscription.dispose()
will print
[my probe] subscribed
Subscribed
[my probe] -> Event next(Box(0))
This is simply 0
[my probe] -> Event next(Box(1))
This is simply 1
[my probe] -> Event next(Box(2))
This is simply 2
[my probe] -> Event next(Box(3))
This is simply 3
[my probe] -> Event next(Box(4))
This is simply 4
[my probe] dispose
Disposed
创建自定义版本的 debug
操作符也比较简单
extension ObservableType {
public func myDebug(identifier: String) -> Observable<Self.E> {
return Observable.create { observer in
print("subscribed \(identifier)")
let subscription = self.subscribe { e in
print("event \(identifier) \(e)")
switch e {
case .next(let value):
observer.on(.next(value))
case .error(let error):
observer.on(.error(error))
case .completed:
observer.on(.completed)
}
}
return Disposables.create {
print("disposing \(identifier)")
subscription.dispose()
}
}
}
}
为了可以 Debug memory leaks using RxSwift.Resources
或者 Log all HTTP requests automatically,你必须开启 Debug 模式
为了开启 debug 模式,一个 TRACE_RESOURCES
标志必须加到 RxSwift target 的 build settings 中,在 Other Swift Flags 选项下
对于如何在 Cocoapods 和 Carthage 中添加 TRACE_RESOURCES
标志的进一步的讨论和介绍,查看 #378
在 debug 模式下,Rx 用 Resources.total
追踪所有分配的资源
如果你想添加一些资源泄漏的检测逻辑,最简单的方法就是周期输出 RxSwift.Resources.total
/* add somewhere in
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil)
*/
_ = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.subscribe(onNext: { _ in
print("Resource count \(RxSwift.Resources.total)")
})
测试内存泄漏最有效率的方式是:
- 打开你的页面并使用
- 关闭你的页面
- 观察初始的资源数量
- 再次打开你的页面并使用
- 关闭你的页面
- 观察最终的资源数量
如果开始的资源数量和最终的资源数量不同,那么就有可能出现了内存泄漏
两次打开页面的原因是第一次打开强制加载了 lazy
的资源
KVO 是一种 Objective-C 的机制,意味着它不是伴随着类型安全来构建的,Rx 也在尝试解决一些这样的问题
Rx 有两种内建的支持 KVO 的方式
// KVO
extension Reactive where Base: NSObject {
public func observe<E>(type: E.Type, _ keyPath: String, options: KeyValueObservingOptions, retainSelf: Bool = true) -> Observable<E?> {}
}
#if !DISABLE_SWIZZLING
// KVO
extension Reactive where Base: NSObject {
public func observeWeakly<E>(type: E.Type, _ keyPath: String, options: KeyValueObservingOptions) -> Observable<E?> {}
}
#endif
一个观察 UIView
的 frame 的例子
警告:UIKit 不服从 KVO, 但它可以工作
view
.rx.observe(CGRect.self, "frame")
.subscribe(onNext: { frame in
...
})
or
view
.rx.observeWeakly(CGRect.self, "frame")
.subscribe(onNext: { frame in
...
})
rx.observe
更高效,因为它这是对 KVO 机制的一个简单封装,但是使用场景也更加局限
- 可以用在观察
self
开始的或者所有权图(retainSelf = false
) 的祖先(持有关系)开始的 path - 可以用在观察所有权图(
retainSelf = false
)的后代(持有关系)开始的 path - path 只能包含
strong
修饰的属性,否则可能会有因为在 dealloc 之前没有取消注册 KVO 而导致 crash
E.g.
self.rx.observe(CGRect.self, "view.frame", retainSelf: false)
rx.observeWeakly
有时相对于 rx.observe
会有一点慢,因为在弱引用的时候需要处理对象的释放
rx.observe
适用的场景 rx.observeWeakly
都可以用,另外还可以用在
- 因为它不会 retain 观察的对象,它可以用在任何持有关系不明确的对象上
- 可以用来观察
weak
属性
E.g.
someSuspiciousViewController.rx.observeWeakly(Bool.self, "behavingOk")
KVO 是一个 OC 的机制,所以它严重依赖于 NSValue
RxCocoa 内建了 KVO 观察 CGRect
, CGSize
和 CGPoint
结构体的支持
当观察其它结构体的时候,需要手动从 NSValue
提取出这些结构体
这里是一些例子,描述了如何通过实现 KVORepresentable
协议来对其它结构体提取 KVO 观察机制和 rx.observe*
方法
当绑定到 UIKit 的 control 上时,你的 Observable
必须要满足 UI 层的一些条件
Observable
需要在 MainScheduler
(UIThread) 发送值,这是 UIKit/Cocoa 的要求
让你的 API 在 MainScheduler
返回结果通常是一个好主意。如果你尝试在后台线程绑定一些东西到 UI 上时,在 Debug 的 build 模式下,RxCocoa 通常会报错一个错误来提示你
你需要添加 observeOn(MainScheduler.instance)
来解决这个问题
URLSession extension 默认不在 MainScheduler
返回结果
你不能绑定失败到 UIKit control 上,因为那是未定义的表现
如果你不知道 Observable
是否会失败,你可以使用 catchErrorJustReturn(valueThatIsReturnedWhenErrorHappens)
来保证它不会失败,但是当错误发生之后,它的序列仍会完成
如果希望序列继续生产元素,就需要一些版本的 retry
操作符
你通常希望在 UI 层共享订阅,你不希望不同的 HTTP 请求绑定同样的数据到多个 UI 控件上去
假设你有下面的实现:
let searchResults = searchText
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query in
API.getSearchResults(query)
.retry(3)
.startWith([]) // clears results on new search term
.catchErrorJustReturn([])
}
.share(replay: 1) // <- notice the `share` operator
通常你想要的是计算之后共享搜索的结果,这就是 share
的意义
UI 层中,在变换链的最后添加 shard
通常是一个好的规则,因为你最想共享计算的结果。你不想在绑定 searchResults
到多个 UI 控件的时候触发不同的 HTTP 连接
也看一下 Driver
单元。它是被设计用来透明地封装这些 share
调用的,确保元素在主线程被观察并且没有错误绑定到 UI 上
制作 HTTP 请求是人们尝试的第一件事
首先你要创建 URLRequest
对象来表示需要被完成的工作,Request 确定请求类型(GET, POST…),是否有请求体,请求参数等
下面是一个简单的 GET 请求的例子
let req = URLRequest(url: URL(string: "http://en.wikipedia.org/w/api.php?action=parse&page=Pizza&format=json"))
如果你想要仅执行了这个请求而不是和其它的 observable
组合,下面的代码就可以实现
let responseJSON = URLSession.shared.rx.json(request: req)
// no requests will be performed up to this point
// `responseJSON` is just a description how to fetch the response
let cancelRequest = responseJSON
// this will fire the request
.subscribe(onNext: { json in
print(json)
})
Thread.sleep(forTimeInterval: 3.0)
// if you want to cancel request after 3 seconds have passed just call
cancelRequest.dispose()
URLSession extensions 默认不会在 MainScheduler
返回结果
如果你想要更底层的 response,你可以:
URLSession.shared.rx.response(myURLRequest)
.debug("my request") // this will print out information to console
.flatMap { (data: NSData, response: URLResponse) -> Observable<String> in
if let response = response as? HTTPURLResponse {
if 200 ..< 300 ~= response.statusCode {
return just(transform(data))
}
else {
return Observable.error(yourNSError)
}
}
else {
rxFatalError("response = nil")
return Observable.error(yourNSError)
}
}
.subscribe { event in
print(event) // if error happened, this will also print out error to console
}
在 debug 模式下,RxCocoa 默认会记录所有的 HTTP 请求到控制台中,如果你想改变这个表现,可以设置 Logging.URLRequests
过滤器
// read your own configuration
public struct Logging {
public typealias LogURLRequest = (URLRequest) -> Bool
public static var URLRequests: LogURLRequest = { _ in
#if DEBUG
return true
#else
return false
#endif
}
}
是一个实现了 UITableView
s 和 UICollectionView
全功能交互数据源的类集合
RxDataSources 打包在这里.
如何使用它们的全功能示范在 RxExample 项目