有位朋友问了下关于这段 ViewModel 的问题。
在 init
中 如果用 subscribeNext
在用到 weak self
是 有错误 , 如何避免这个错误?
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 和 Observable 。
代码中有一个严重逻辑错误(54 - 57 行):
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
-> isRefreshing
, loading
-> isLoading
error
上面换行换了两行flatMapLatest
没有换到下一行(要换就都换input
中的 Trigger
少了一个 g
API
& Api
不需要 NSObject
。
44 行 .map{Result.Success($0)}
可以改写为 .map(Result.Success)
。
属性中的 refreshTrigger
和 loadNextPageTrigger
貌似是多余了,refresh 要么在初始化中决定,要么就用这个 refreshTrigger
属性。
Result
指定具体的 error
类型处理起来会很累。比如 error
类型的转换,每次都需要指明 error
类型。
相比 ObjectMapper ,SwiftyJSON 是更好的选择。
网络请求到解析成 Model ,这一过程的代码一般大量存在,可以进行复用。具体可以参考 在实践中应用 RxSwift 2 - 使用函数式复用代码 (opens new window) 。
从最开始就不要调用 onError
,具体体现在 EmeAPI.sharedInstance.request
,我想这里应该是调用了 onError
,所以才在后面有 map
成 枚举,catch error
,返回可能存在 error
的枚举。
当然了,笔者曾经也是这样写的,事实上网络层的 Block 回调变成 Observable 完全可以我们自己来写,然后只调用 onNext 和 onCompleted 。 注:本代码是体现出流的感觉的,这是我们在写 Rx 时最重要的一点,建立流的概念。本文如有任何错误或建议,随时欢迎提出来一起讨论。