RxSwift: ReactiveX for Swift

原创
2016/09/13 20:29
阅读数 252

Rx可以让你的应用处于一个声明的方式来使用!

Bindings :绑定

1.可以非常轻松的将两个textfiled的text绑定起来。也就是按照一定的意愿组合起来

Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
    .map { "Greetings, \($0)" }
    .bindTo(greetingLabel.rx_text)

分析:1.combineLatest 是将firstName以及lastName的内容组合起来然后在中间添加一个空格   

           2.map 遍历刚刚组合出来的字符串 前面添加Greetings

           3.然后跟greetingLabel的text绑定起来 

总结:也就是说上面这几行代码就可以将firstName以及lastName通过组合外加变化的方式赋值给greetingLabel

2.同样的UITableview以及UICollectionView也可以做类似的处理

viewModel
    .rows
    .bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
        cell.title = viewModel.title
        cell.url = viewModel.url
    }
    .addDisposableTo(disposeBag)

分析 : 1.将ViewModel的每一行都绑定一个WikipediaSearchCell

            2.在闭包中返回了tableview,cell,以及Viewmodel,可以进行相应的操作

            3.addDisposableTo当所有者需要释放时,取消监听

总结 :如果你听说过MVVM模式的话,函数式编程也就是上面那种模式将会实现你想要的

官方建议总是使用.addDisposableTo(disposeBag), 虽然这个在简单的绑定中没有必要。

Retries :重试

如果API不会失败那么将是美妙的。但是很不幸的是他们经常失败,下面让我们来看看这个API方法

func doSomethingIncredible(forWho: String) throws -> IncredibleThing

如果你使用这个函数像以前一样。那么它将会很难得去重试当它失败的时候。更不用说复杂的模型exponential backoffs。当然那很有可能。但是这个代码可能包括很多过渡的状态是你并不关心的,并且那些也无关紧要。

思想上,你想要去抓住这些本质的东西,然后重试。并且可以使用这些到任何一个操作上面

那么使用Rx将会非常简单

doSomethingIncredible("me")
    .retry(3)

你可以简单的创造自定义的重试操作

Delegates : 代理

停止使用这些单调乏味无实际意义的方法吧,例如:

public func scrollViewDidScroll(scrollView: UIScrollView) { [weak self] // what scroll view is this bound to?
    self?.leftPositionConstraint.constant = scrollView.contentOffset.x
}

使用

self.resultsTableView
    .rx_contentOffset
    .map { $0.x }
    .bindTo(self.leftPositionConstraint.rx_constant)

KVO :Key-Value-Observer

停止使用 :当键值观察者仍然在注册中而'TickTock'已经销毁时,观察的信息将会被泄漏,甚至也许会错误的对应到其他的对象上面去

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context

使用rx_observe 和 rx_observeWeakly

view.rx_observe(CGRect.self, "frame")
    .subscribeNext { frame in
        print("Got new frame \(frame)")
    }

 

someSuspiciousViewController
    .rx_observeWeakly(Bool.self, "behavingOk")
    .subscribeNext { behavingOk in
        print("Cats can purr? \(behavingOk)")
    }

 Notifications :通知

停止使用

@available(iOS 4.0, *)
public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol

而是采用 

NSNotificationCenter.defaultCenter()
    .rx_notification(UITextViewTextDidBeginEditingNotification, object: myTextView)
    .map { /*do something with data*/ }
    ....

 

过渡状态 :

这里也有很多问题关于过渡状态的当使用异步编程时。一个非常典型的例子就是一个自动完成的搜索框

如果你以前写自动完成代码没有使用Rx,那么出现的第一个问题需要被解决的就是当ab的搜索的网络请求正在进行,c又输入了。那么这个请求应该被取消。OK,那也不会非常难去解决。你也许仅用一个额外的变量来控制这个即将到来的请求就可以了。

接下来一个问题是如果这个请求失败了。你需要去做麻烦的重试逻辑。一对或者多个可以捕获重试请求来清除,这个问题可是可以被解决

如果编码能够现实等待一段时间再去发送请求到服务器那将是极好的。毕竟,我们不想去浪费我们的服务以防有些人正在码很长的字符。一段额外的计时器字段也许需要

这里仍然有一个问题,那就是查询正在执行需要显示出来,而且那将会被展示以防我们失败即使我们尝试了很多次。

把上面这些问题码出来并且进行适当的测试将会非常繁琐。这里有一段一样逻辑的代码是用Rx写的

searchTextField.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query in
        API.getSearchResults(query)
            .retry(3)
            .startWith([]) // clears results on new search term
            .catchErrorJustReturn([])
    }
    .subscribeNext { results in
      // bind to ui
    }

这里不需要其他额外的标志或者字段。Rx将会处理所有这些过渡的混乱的状态

 

Compositional disposal

让我们假设有这么一个情况:你需要展示模糊的图像在tableview上面。首先这些图片需要从URL上面获取下来,然后编码,然后模糊化

如果一个cell已经存在了可视的tableview里面 那么 整过过程会被取消将会是极好的。毕竟带宽和模糊的进程耗时是非常昂贵的

一旦一个cell进入可视的范围后不会立马请求图片也是极好的,因为用户划的非常快的话。那么将会有很多请求被发送和取消。

限制并发的图片操作的数量也将会是很好的,毕竟模糊图片是一个昂贵的操作

因此我们可以使用Rx:

// this is a conceptual solution
let imageSubscription = imageURLs
    .throttle(0.2, scheduler: MainScheduler.instance)
    .flatMapLatest { imageURL in
        API.fetchImage(imageURL)
    }
    .observeOn(operationScheduler)
    .map { imageData in
        return decodeAndBlurImage(imageData)
    }
    .observeOn(MainScheduler.instance)
    .subscribeNext { blurredImage in
        imageView.image = blurredImage
    }
    .addDisposableTo(reuseDisposeBag)

这段代码可以做到上面所有的一起。当imageSubscription被取消了。那么它将取消所有依赖的异步的操作。确保没有流氓的图片被绑定到UI上面

Aggregating network requests 

你是否需要发送两个请求然后合成结果当他们都完成之后呢?

是的,这就是Zip 操作

let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<Friends> = API.getFriends("me")

Observable.zip(userRequest, friendsRequest) { user, friends in
    return (user, friends)
}
.subscribeNext { user, friends in
    // bind them to the user interface
}

那么如果APIs返回的数据在子线程,但是绑定却需要发送在主线程呢。observeOn将可以实现

let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<[Friend]> = API.getFriends("me")

Observable.zip(userRequest, friendsRequest) { user, friends in
    return (user, friends)
}
.observeOn(MainScheduler.instance)
.subscribeNext { user, friends in
    // bind them to the user interface
}

这里还有更多的实用例子是Rx的闪光点

 

State

允许多态改变的语言容易获得全局状态并且改变他。但是不可控的全局状态的变化将很容易导致组合爆炸combinatorial explosion.

但是另一方面。当它被一种聪明的方式使用时,专横的语言将将会以一种更高效的方法编码已更接近与硬件

和组合爆炸做斗争常用的方式就是保持状态越简单越好。使用 unidirectional data flows 数据导出模型

这就是Rx的闪光点

Rx函数式专横世界里面的甜点。它使您能够使用不可变的定义和纯函数的可变状态的进程快照可靠的组合方式。

 

Easy integration

如何你才能创建你自己的观察者呢。那将会非常简单。下面的代码来自RxCocoa。你所需要做的就是去除HTTP请求使用NSURLSession

extension NSURLSession {
    public func rx_response(request: NSURLRequest) -> Observable<(NSData, NSURLResponse)> {
        return Observable.create { observer in
            let task = self.dataTaskWithRequest(request) { (data, response, error) in
                guard let response = response, data = data else {
                    observer.on(.Error(error ?? RxCocoaURLError.Unknown))
                    return
                }

                guard let httpResponse = response as? NSHTTPURLResponse else {
                    observer.on(.Error(RxCocoaURLError.NonHTTPResponse(response: response)))
                    return
                }

                observer.on(.Next(data, httpResponse))
                observer.on(.Completed)
            }

            task.resume()

            return AnonymousDisposable {
                task.cancel()
            }
        }
    }
}

 

Benefits :

简而言之,使用Rx将会使你的代码:

1.可组合  因为Rx就是可组合的昵称

2.可复用 因为Rx可组合

3.声明性 因为定义是不可改变的。仅仅数据改变

4.可理解并且精确 提高了抽象的水平并且移除了过渡状态

5.稳定性 因为Rx 代码是可以通过单元测试的

6.更少的状态性  因为你的模型化应用时单向的数据流向

7.没有内存泄漏  因为资源管理非常简单 

 

It's not all or nothing 

在你的应用中尽可能多的使用Rx那将是不错的注意

但是如果你不知道所有的操作符。无论那里是否存在一些操作符刚好符合你的特定的需求

当然所有的Rx操作符都是基于数学和直观的

好消息是大概10-15个操作符包含了所有的使用情形

这里有一列表Rx操作符  all Rx operators 和一大群 被RxSwift支持的操作符currently supported RxSwift operators.

如果你所需要的操作符不在列表中。那么创造你自己的操作符吧。

如果你在自定义操作符中遇到任何问题。来这里试试看吧 jump out of Rx monads

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部