我试图定期(每10秒)调用一个返回模型的Json对象的API:
struct MyModel {
var messagesCount: Int?
var likesCount: Int?
}
如果messageCount
或likesCount
值更改,请更新UI。
我尝试了Timer解决方案,但发现它有点混乱,我想要使用RxSwift和RxAlamofire的清洁解决方案。
由于我是Rx的新手,因此非常感谢任何帮助。
答案 0 :(得分:3)
欢迎使用StackOverflow!
为此需要很多运算符,我建议您在ReactiveX Operator page上查找它们,每当我忘记某件事时都会进行检查。
首先,确保MyModel
符合Decodable
,以便可以从JSON响应中构造它(请参阅Codable)。
let willEnterForegroundNotification = NotificationCenter.default.rx.notification(.UIApplicationWillEnterForeground)
let didEnterBackgroundNotification = NotificationCenter.default.rx.notification(.UIApplicationDidEnterBackground)
let myModelObservable = BehaviorRelay<MyModel?>(value: nil)
willEnterForegroundNotification
// discard the notification object
.map { _ in () }
// emit an initial element to trigger the timer immediately upon subscription
.startWith(())
.flatMap { _ in
// create an interval timer which stops emitting when the app goes to the background
return Observable<Int>.interval(10, scheduler: MainScheduler.instance)
.takeUntil(didEnterBackgroundNotification)
}
.flatMapLatest { _ in
return RxAlamofire.requestData(.get, yourUrl)
// get Data object from emitted tuple
.map { $0.1 }
// ignore any network errors, otherwise the entire subscription is disposed
.catchError { _ in .empty() }
}
// leverage Codable to turn Data into MyModel
.map { try? JSONDecoder().decode(MyModel.self, from: $0) } }
// operator from RxOptional to turn MyModel? into MyModel
.filterNil()
.bind(to: myModelObservable)
.disposed(by: disposeBag)
然后,您可以仅将数据流继续输入到UI元素中。
myModelObservable
.map { $0.messagesCount }
.map { "\($0) messages" }
.bind(to: yourLabel.rx.text }
.disposed(by: disposeBag)
我没有运行此代码,因此此处可能存在一些错别字/缺少转换,但这应该为您指明正确的方向。随时要求澄清。如果真的是Rx的新手,我建议您通过Getting Started guide。这很棒! Rx非常强大,但是花了我一段时间才能掌握。
正如@ daniel-t所指出的,使用Observable<Int>.interval
时不需要背景/前景簿记。
答案 1 :(得分:3)
CloakedEddy与他的回答非常接近,值得投票。但是他使它变得比必要的更为复杂。 Interval在内部使用DispatchSourceTimer,当应用程序转到后台并返回到前台时,它将自动停止并重新启动。他还记得要抓住错误以阻止流停止展开,所以他做得非常出色。
我假设以下代码位于AppDelegate或高级协调器中。此外,myModelSubject
是ReplaySubject<MyModel>
(使用ReplaySubject<MyModel>.create(bufferSize: 1)
创建,应放置在视图控制器可以访问或传递给视图控制器的位置。
Observable<Int>.interval(10, scheduler: MainScheduler.instance) // fire at 10 second intervals.
.flatMapLatest { _ in
RxAlamofire.requestData(.get, yourUrl) // get data from the server.
.catchError { _ in .empty() } // don't let error escape.
}
.map { $0.1 } // this assumes that alamofire returns `(URLResponse, Data)`. All we want is the data.
.map { try? JSONDecoder().decode(MyModel.self, from: $0) } // this assumes that MyModel is Decodable
.filter { $0 != nil } // filter out nil values
.map { $0! } // now that we know it's not nil, unwrap it.
.bind(to: myModelSubject) // store the value in a global subject that view controllers can subscribe to.
.disposed(by: bag) // always clean up after yourself.