RxSwift实战操作·Realm应用
此部分对于对于
ReactiveX
就不单独介绍了,详情请转至:ReactiveX website或者RxSwift GitHub repo
从RxRealm入手
我们可以通过CocoaPods简单的安装使用RxRealm
这个extension:pod ‘RxRealm’,或者自行安装,详情参考文档source code from GitHub。
这个库是封装好的Realm信号量集合。我们可以通过简单的订阅Results
对象来刷新表视图,这仅仅只需要asObservable()
操作。
1 | let realm = try! Realm() |
asObservable()
在集合有改变的时候会发出信号,返回为Observable<Self>
,收到信号的同时会得到最终改变过的集合,任由你处理。
如果你只是想拿到变动的集合,只需要使用asObservableChangeset()
即可,返回形式为Observable<(Self, RealmChangeset?)>
,代码如下:
1 | realm.objects(Lap) |
如果你仅仅是想处理一些中间情况或者筛选等等之类的操作,asObservableArray()
最适合不过来:
1 | realm.objects(Lap) |
看起来是不是So easy???好,让我们来做个简单的小应用,包你分分钟上手RxRealm
,从此以后爷爷奶奶都不用你担心你获取Realm
操作的想哭了。
GitHub Search应用
这里我们手把手新建一个Github搜索应用,通过GitHub’s JSON API来做查询,让应用可以模糊搜索匹配Repository,是时候展现Realm + Rx
真正的实力了。
懒魔福利:源码。
做这个简单的小应用可以自行搭建工程,自行采用适合的架构即可,不一定非要MVVM,只需要能搜索到如下三种语言结果即可:
接口交互
稍微提醒下,以下几部分代码都是基于你对RxSwift和RxCocoa有所了解,如果还有不了解的请转至文首部分。
我们首先要做的工作就是把输入条件转化为接口查询条件,需要做的就是把输入条件和选择条件合并为一个信号条件,最后用作接口调用的参数即可:
1 | let input = Observable.combineLatest( |
我们将输入的最新文本(长度至少3个字符,节省网络请求)以及segment中的最后一个选定标题。并将它们组合成元组,以供订阅。
接下来让我们订阅输入并对GitHub进行网络调用:
1 | input.throttle(0.5, scheduler: MainScheduler.instance) |
通过调节输入频率来节约网络请求,并生成合适的URL来做网络请求,利用rx_JSON做数据转换。
最后只需要做简单的数据解析,转换成realm对象保存即可。
1 | ... // Code from above |
我们切换到后台调度队列,然后将JSON映射做数据解析即可。
注意:如果JSON与预期属性不匹配,会crash,此代码仅用于测试用,生产中要处理解析错误的情况。
最后,我们可以将Repo对象存储到Realm中。此时,代码已经在后台线程上运行(并且不再更改线程,Realm对象是线程安全的),最后订阅一盘就可以用来存储对象了:
1 | ... // Code from above |
在最后一段代码中,我们从当前线程中开一个Realm实例,添加所有的返回对象,并将订阅添加到dispose包中。
现在我们已经订购了驱动整个序列的订阅:从验证和限制用户输入,到制作异步网络调用,解析和转换JSON,最后创建Realm对象并将它们存储在本地。
接下来我们看下如何使用RxRealm并使用Realm的内置通知。
利用RxRealm驱动UI
接下来,我们将对用户输入进行另一次订阅,过滤存储在Realm中的对象,并在视图控制器的表视图中显示结果。
这里我们把表叔图刷新的逻辑抽出来,减少代码冗余:
1 | input |
现在,任何时候用户输入一个有效的搜索词,就会调用bindTableView(_, language: _)
。
但是,当用户删除搜索词时会发生什么?我们再为这种情况添加另一个订阅:
1 | query.rx_text.filter {$0.utf8.count <= 2} |
如果搜索项短于3个字符,则会bindTableView(_, language: _)
用nil参数调用。
接着是bindTableView逻辑:
1 | private var repos: Results<Repo>? |
如果给bindTableView传递nil参数,代码将置空repos并刷新表视图(隐藏任何以前的结果可见)。
接下来让我们找到用户感兴趣的结果:
1 | ... |
然后我们订阅变化的存储对象用于显示:
1 | ... |
现在,当用户输入搜索词时,会发生什么情况:
- 该应用将向GitHub发起网络请求
- 该应用将显示搜索词的本地搜索结果
- 当JSON解析完成后,会被存储到本地
- 结果的更改将通过上述订阅完成,因此也能更新表格视图
Nice,该应用具有本地缓存,因此它可以脱机工作以及在线工作。所以,它会显示即时结果,如果本地没有任何缓存,它也会处理来自网络的响应!
最后我们把搜索到服务端返回的结果,保存并显示出来:
1 | // Table view magic |
为了尽快展示本地搜索结果,我们调用tableView.reloadData()。相反,为了使通过网络返回的新结果更加明显,我们使用动画进行更新。
注意:因为在这个演示中我们只添加到本地缓存中,所以我们只关心changes.inserted。如果你还期望对象更新或删除,你应该处理changes.updated和changes.deleted。有关更多如何使用变更通知,请查看此帖。
测试
让我们运行应用并搜索“animation”并尝试“Swift”和“Objective-C”之间切换来观察结果:
接着我们来测试本地和网络同时筛选的情况,根据第一条的关键字Easy来搜索:
正如看到的,本地结果会立刻展示,网络请求得到的结果会通过动画的形式展现出来。
最后测试删除结果,并清空列表:
结尾
这里就展示完了通过Observable.changeset(from: objects)
来处理变更通知的基本操作,对于changes.updated和changes.deleted可以自行尝试,当然这个扩展库远不止这些,还有比如批量添加Realm.rx.add()
和批量删除Realm.rx.delete()
这种骚操作也是很容易信手拈来的。