魏名华

不要偷懒,做更好的自己

Nothing


No Welcome Message

Rxswift

RxSwift

官方文档

ps: 学习笔记只是随便记一记,加快学习速度和映像,没有参考意义,因为其实官方文档的每个行都是重点。

TODOs:

  • [√] 过一遍文档
  • 过一遍playgound和单元测试相关内容
  • 学习demo,抄一遍吧,映像会很深刻
  • 详细研究内容,先参考文档,不懂的再查资料
  • 应用实战
  • 《RxSwift Reactive Programming with Swift》这本书真神,解答一切疑惑,尤其是实战上的,要看完

ps: 没有实战研究太枯燥,过一遍文档后,会一边实战一边研究

why

Rx支持以声明方式构建apps。

  1. view和data绑定

  2. 失败重试: 比如网络请求失败重试;

  3. Delegates

  4. KVO

  5. Notifications

  6. Transient state: 以搜索为例,程序中有很多的状态需要处理,而RX可以轻松解决,怎么解决的要有空再看看;

  7. Compositional disposal: 以列表中的cell图片显示为例,程序中有很多细节处理,,而RX可以轻松解决,怎么解决的要有空再看看;

  8. Aggregating network requests: 请求的合并;

  9. State: 单向数据流?? 没搞懂;

  10. Easy integration: 易于集成,以URLSession为例,介绍了怎么讲请求转化Observable

extension Reactive where Base: URLSession {
    public func response(request: URLRequest) -> Observable<(Data, HTTPURLResponse)> {
        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(data, httpResponse))
                observer.on(.completed)
            }

            task.resume()

            return Disposables.create(with: task.cancel)
        }
    }
}
  1. Benefits

简单来说,使用Rx可以是你的代码:

  • 可组合 <- 因为Rx是作曲的昵称
  • 可重复使用的 <- 因为它是可组合的
  • 声明性 <- 因为定义是不可变的,只有数据发生变化
  • 可理解和简洁 <- 提高抽象级别并消除瞬态
  • 稳定 <- 因为Rx代码经过了彻底的单元测试
  • 较少有状态 <- 因为您将应用程序建模为单向数据流
  • 没有泄漏 <- 因为资源管理很容易
  1. It’s not all or nothing

Getting Started

  1. Observables aka Sequences

Every Observable sequence is just a sequence. The key advantage for an Observable vs Swift’s Sequence is that it can also receive elements asynchronously.This is the kernel of RxSwift, documentation from here is about ways that we expand on that idea.

  • Observable(ObservableType) is equivalent to Sequence
  • ObservableType.subscribe method is equivalent to Sequence.makeIterator method.
  • Observer (callback) needs to be passed to ObservableType.subscribe method to receive sequence elements instead of calling next() on the returned iterator.

貌似是说Observable sequence其实就是sequence,只是能接收异步的元素,所以会有一堆函数式的操作,比如map,filter等等;而subscribe就相当于遍历。

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>)
}
  1. Disposing

另一种方式终止observed sequence

2.1 Dispose Bags

Dispose bags are used to return ARC like behavior to RX.

When a DisposeBag is deallocated, it will call dispose on each of the added disposables.

好像是被用于ARC。比如一个ViewController可以申明属性let disposeBag = DisposeBag(), 这样ViewController销毁,导致disposeBag销毁,这样就清空所有的observerd sequence

2.2 Take until

Additional way to automatically dispose subscription on dealloc is to use takeUntil operator.

sequence
    .takeUntil(self.rx.deallocated)
    .subscribe {
        print($0)
    }
  1. Implicit Observable guarantees

subscribe的执行过程不会被任何event打断

  1. Creating your own Observable (aka observable sequence)

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)
  })

create Observable

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)
    })
  1. Creating an Observable that performs work

demo

  1. Sharing subscription and share operator
  • demo

  • share与非share的区别

share:每次subscribe,会共用create闭包的内容,即多次subscribe,create闭包不会执行多次 非share:每次subscribe,都会独立执行create闭包的内容,各不干扰

  • share的作用:很多observable,会bind多次,比如SimpleValidationViewController.swift中,用户名输入框的校验,即绑定密码框是否可以,又绑定用户名提示是否显示,share可以共用事件,实现事件发出1次,多个observer监听,并实现业务逻辑绑定,避免无意义的校验事件多次发出

subscribe

  1. Operators

Rx.playground有演示,另外,Rx.playground是很好的使用playground的模板

7.1 Custom operators

通过demo看,还蛮简单

7.2 Life happens

没看懂

  1. Playgrounds

  2. Error handling

没看懂

  1. Debugging Compile Errors

编译错误,说的是swift类型推断偶尔抽风

  1. Debugging

介绍了使用debug operator

11.1 Enabling Debug Mode

这个要参考一下

post_install do |installer|
   installer.pods_project.targets.each do |target|
      if target.name == 'RxSwift'
         target.build_configurations.each do |config|
            if config.name == 'Debug'
               config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['-D', 'TRACE_RESOURCES']
            end
         end
      end
   end
end
  1. Debugging memory leaks

没看懂

  1. KVO

13.1 rx.observe

监听strong属性

13.2 rx.observeWeakly

监听weak属性

13.3 Observing structs

RxCocoa仅做好了CGRect, CGSize and CGPoint的监听

  1. UI layer tips

14.1 Threading

必须在主线程更新UI,observeOn(MainScheduler.instance)

URLSession extensions don’t return result on MainScheduler by default.

14.2 Error

没看懂

14.3 Sharing subscription

没看懂share操作时间干啥的

  1. Making HTTP requests

  2. RXDataSources

… is a set of classes that implement fully functional reactive data sources for UITableViews and UICollectionViews.

Traits (formerly Units)

  1. General

Traits are simply a wrapper struct with a single read-only Observable sequence property.

struct Single<Element> {
    let source: Observable<Element>
}

struct Driver<Element> {
    let source: Observable<Element>
}
...
  1. RxSwift traits

2.1 Single

A Single is a variation of Observable that, instead of emitting a series of elements, is always guaranteed to emit either a single element or an error.

2.2 Completable

A Completable is a variation of Observable that can only complete or emit an error. It is guaranteed to not emit any elements.

2.3 Maybe

A Maybe is a variation of Observable that is right in between a Single and a Completable. It can either emit a single element, complete without emitting an element, or emit an error.

  1. RxCocoa traits

3.1 Driver

version_1:

let results = query.rx.text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
    }

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

version_2:

let results = query.rx.text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .observeOn(MainScheduler.instance)  // results are returned on MainScheduler
            .catchErrorJustReturn([])           // in the worst case, errors are handled
    }
    .share(replay: 1)                           // HTTP requests are shared and results replayed
                                                // to all UI elements

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

version_3:

let results = query.rx.text.asDriver()        // This converts a normal sequence into a `Driver` sequence.
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .asDriver(onErrorJustReturn: [])  // Builder just needs info about what to return in case of error.
    }

results
    .map { "\($0.count)" }
    .drive(resultCount.rx.text)               // If there is a `drive` method available instead of `bind(to:)`,
    .disposed(by: disposeBag)              // that means that the compiler has proven that all properties
                                              // are satisfied.
results
    .drive(resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

3.2 Signal

没看懂

  1. ControlProperty / ControlEvent

也没看懂

testing

  1. Testing custom operators
  2. Testing operator compositions (view models, components)

比较短,一定要搞懂哦

tips

  • 使用纯函数
  • 使用内置operators
  • 创建自己的convenience operators
  • 创建自己的operators可以使用内置operators
  • Always use operators to compose subscriptions.
  1. 避免嵌套subscribe
textField.rx.text.subscribe(onNext: { text in
    performURLRequest(text).subscribe(onNext: { result in
        ...
    })
    .disposed(by: disposeBag)
})
.disposed(by: disposeBag)
  1. Preferred way of chaining disposables by using operators. 使用disposables最好的方式是使用disposed这个operator?
textField.rx.text
    .flatMapLatest { text in
        // Assuming this doesn't fail and returns result on main scheduler,
        // otherwise `catchError` and `observeOn(MainScheduler.instance)` can be used to
        // correct this.
        return performURLRequest(text)
    }
    ...
    .disposed(by: disposeBag) // only one top most disposable

Math Behind Rx

更深入的东西

Hot and Cold Observables

Hot Observables: 不依赖于是否有subscribed,比如tap coordinates, mouse coordinates, UI control values, current time

Cold Observables: Don’t use resources (don’t produce heat) until observer subscribes. 比如Async operations, HTTP Connections, TCP connections, streams

Comparison with ReactiveCocoa

不知所云

find compatible

RxSwift全家桶

References