摘要

有位朋友问了下关于这段 ViewModel 的问题。
init 中 如果用 subscribeNext 在用到 weak self 是 有错误 , 如何避免这个错误?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import Result
import ObjectMapper
class PhgViewModel: NSObject {
let refreshTrigger = PublishSubject<Void>()
let loadNextPageTrigger = PublishSubject<Void>()
let elements = Variable<[Brand]>([])
let page = Variable(1)
let hasNextPage = Variable(true)
let refreshing = Variable(false)
let loading = Variable<Bool>(true)
let error = PublishSubject<ErrorType>()
init(
input: (
refreshTriger: Observable<Void>,
loadMoreTriger: Observable<Void>,
)
) {
let refreshRequest :Observable<Result<[Brand],NSError>> = input.refreshTriger
.map {1}
.flatMapLatest { page -> Observable<Result<[String : AnyObject],NSError>> in
EmeAPI.sharedInstance.request(PhgApi.getList(begin: page , length: 10))
.observeOn(Mainscheduler.instance)
.mapRegMessageData([String : AnyObject])
.map{Result.Success($0)}
.catchError{ error in
let errir = error as NSError
let right = Result<[String : AnyObject], NSError>> in
return Observable.just(right)
}
}.flatMapLatest { response -> Observable<Result<[Brand],NSError>> in
return Observable.create { observer -> Disposable in
switch response {
case .Success(let relDict):
if let brand = Mapper<Brand>().mapArray(relDict["list"]){
observer.onNext(Result.Success(brand))
}
observer.onNext(Result.Failure(NSError.init(domain: "获取数据错误", code: 100, userInfo: nil)))
case .Failure(let error):
observer.onNext(Result.Failure(error))
}
return NoDisposable.instance
}
}
refreshRequest.subscribeNext{ [weak self] result in
switch result {
case .Success(let branch):
self?.page.value = 2
self?.elements.value = brands
case .Failure( let error) :
print(error)
}
}
...

编译错误

先解决编译错误。

不能调用 self 是因为初始化没有完成,可能这个问题不会被一下子发现。仔细看下代码,会发现 RhgViewModel 继承自 NSObject ,也就是说还没有调用 super.init 方法。在初始化最开始加入 super.init() 就可以了。

注:从这段代码中可以看到 NSObejct 是没有存在的必要,我们可以考虑去掉。如果是因为这里面还有一些 Objective-C 的 delegate 之类的代码,可以尝试实现一个 ProxyDelegate 解决。

Driver

Driver 不是必备的,当然可以不用,具体可以参考后面的文章 Driver 和 Observable

逻辑错误

代码中有一个严重逻辑错误(54 - 57 行):

1
2
3
4
if let brand = Mapper<Brand>().mapArray(relDict["list"]){
observer.onNext(Result.Success(brand))
}
observer.onNext(Result.Failure(NSError.init(domain: "获取数据错误", code: 100, userInfo: nil)))

不论是否成功,后面都会收到一个获取数据错误Error
以及这里没有调用 observer.onCompleted()

代码规范

在继续改进的话题,谈一谈代码规范也是比较好的,有如下几点问题:

  • 使用了 NSError 的同时使用了 ErrorType
  • 属性 error 没有清晰的表明是什么 error (虽然可能目的是表明统一下 error
  • refreshing -> isRefreshingloading -> isLoading
  • 属性 error 上面换行换了两行
  • 空格不规范(两个空格,一个空格,不空格等等)
  • 第二个 flatMapLatest 没有换到下一行(要换就都换
  • input 中的 Trigger 少了一个 g
  • API & Api

改进建议

不需要 NSObject

44 行 .map{Result.Success($0)} 可以改写为 .map(Result.Success)

属性中的 refreshTriggerloadNextPageTrigger 貌似是多余了,refresh 要么在初始化中决定,要么就用这个 refreshTrigger 属性。

Result 指定具体的 error 类型处理起来会很累。比如 error 类型的转换,每次都需要指明 error 类型。

相比 ObjectMapper ,SwiftyJSON 是更好的选择。

网络请求到解析成 Model ,这一过程的代码一般大量存在,可以进行复用。具体可以参考 在实践中应用 RxSwift 2 - 使用函数式复用代码

从最开始就不要调用 onError ,具体体现在 EmeAPI.sharedInstance.request ,我想这里应该是调用了 onError ,所以才在后面有 map 成 枚举,catch error ,返回可能存在 error 的枚举。

当然了,笔者曾经也是这样写的,事实上网络层的 Block 回调变成 Observable 完全可以我们自己来写,然后只调用 onNext 和 onCompleted 。
注:本代码是体现出流的感觉的,这是我们在写 Rx 时最重要的一点,建立流的概念。本文如有任何错误或建议,随时欢迎提出来一起讨论。