在反应式编程中创建静态流是个坏主意吗?
Is it bad idea to create static stream in reactive programming?
现在我正在使用 RxSwift 框架开发 iOS。在我的应用程序中,我必须使用用户位置,但我不需要实时更新它。如果每次用户打开应用程序或执行某些定义的操作时更新位置就足够了。因此,如何在缓存最后一个结果的地方实现单例 class 呢?每次更新都会更改缓存的结果并将其接受到流中。 Stream 的默认值是缓存值。然后,需要用户位置的视图将订阅此流。
import Foundation
import Cache
import CoreLocation
import RxSwift
import RxCocoa
class UserLocationManager: NSObject {
private enum Keys: String {
case diskConfig = "Disk Config"
case lastLocation = "User Last Location"
}
// MARK: - Variables
private func cache<T: Codable>(model: T.Type) -> Cache.Storage<T> {
let storage = try! Cache.Storage(diskConfig: DiskConfig(name: Keys.diskConfig.rawValue), memoryConfig: MemoryConfig(expiry: .never), transformer: TransformerFactory.forCodable(ofType: model))
return storage
}
private let locationManager = CLLocationManager()
private var lastPosition: MapLocation? {
get {
do {
return try cache(model: MapLocation.self).object(forKey: Keys.lastLocation.rawValue)
}
catch { return nil }
}
set {
do {
guard let location = newValue else { return }
try cache(model: MapLocation.self).setObject(location, forKey: Keys.lastLocation.rawValue)
}
catch { }
}
}
private let disposeBag = DisposeBag()
static let shared = UserLocationManager()
var locationStream = BehaviorRelay<CLLocationCoordinate2D?>(value: nil)
// MARK: - Methods
func updateLocation() {
if CLLocationManager.locationServicesEnabled() {
locationManager.requestLocation()
}
}
func subscribe() {
locationStream.accept(lastPosition?.clCoordinate2D)
locationStream.subscribe(onNext: { [weak self] location in
guard let `self` = self else { return }
guard let location = location else { return }
self.lastPosition = MapLocation(latitude: location.latitude, longitude: location.longitude)
}).disposed(by: disposeBag)
locationManager.delegate = self
}
// MARK: - Lifecycle
override init() {
super.init()
defer {
self.subscribe()
}
}
}
// MARK: - CLLocationManagerDelegate
extension UserLocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
UserLocationManager.shared.locationStream.accept(location.coordinate)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
}
从概念上讲,拥有一个可以订阅的全球流是没有问题的。但是,您的具体实施令我担心。
关于 Observable 流的一个很酷的事情是它们是惰性的,除非需要,否则不做任何工作,但是您正在编写额外的代码来绕过该功能,我认为没有必要。此外,当应用程序进入后台时存储当前位置,并在应用程序返回前台(可能几周后)时假设这是一个有效位置,这对我来说听起来不合适。
RxCocoa 包已经有一个 CLLocationManager 的 Rx 包装器。在我看来,使用它会简单得多。如果您只需要一个位置更新,则使用 .take(1)
。我倾向于在 take(1) 之前添加一个关于位置准确性的过滤器。
现在我正在使用 RxSwift 框架开发 iOS。在我的应用程序中,我必须使用用户位置,但我不需要实时更新它。如果每次用户打开应用程序或执行某些定义的操作时更新位置就足够了。因此,如何在缓存最后一个结果的地方实现单例 class 呢?每次更新都会更改缓存的结果并将其接受到流中。 Stream 的默认值是缓存值。然后,需要用户位置的视图将订阅此流。
import Foundation
import Cache
import CoreLocation
import RxSwift
import RxCocoa
class UserLocationManager: NSObject {
private enum Keys: String {
case diskConfig = "Disk Config"
case lastLocation = "User Last Location"
}
// MARK: - Variables
private func cache<T: Codable>(model: T.Type) -> Cache.Storage<T> {
let storage = try! Cache.Storage(diskConfig: DiskConfig(name: Keys.diskConfig.rawValue), memoryConfig: MemoryConfig(expiry: .never), transformer: TransformerFactory.forCodable(ofType: model))
return storage
}
private let locationManager = CLLocationManager()
private var lastPosition: MapLocation? {
get {
do {
return try cache(model: MapLocation.self).object(forKey: Keys.lastLocation.rawValue)
}
catch { return nil }
}
set {
do {
guard let location = newValue else { return }
try cache(model: MapLocation.self).setObject(location, forKey: Keys.lastLocation.rawValue)
}
catch { }
}
}
private let disposeBag = DisposeBag()
static let shared = UserLocationManager()
var locationStream = BehaviorRelay<CLLocationCoordinate2D?>(value: nil)
// MARK: - Methods
func updateLocation() {
if CLLocationManager.locationServicesEnabled() {
locationManager.requestLocation()
}
}
func subscribe() {
locationStream.accept(lastPosition?.clCoordinate2D)
locationStream.subscribe(onNext: { [weak self] location in
guard let `self` = self else { return }
guard let location = location else { return }
self.lastPosition = MapLocation(latitude: location.latitude, longitude: location.longitude)
}).disposed(by: disposeBag)
locationManager.delegate = self
}
// MARK: - Lifecycle
override init() {
super.init()
defer {
self.subscribe()
}
}
}
// MARK: - CLLocationManagerDelegate
extension UserLocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
UserLocationManager.shared.locationStream.accept(location.coordinate)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
}
从概念上讲,拥有一个可以订阅的全球流是没有问题的。但是,您的具体实施令我担心。
关于 Observable 流的一个很酷的事情是它们是惰性的,除非需要,否则不做任何工作,但是您正在编写额外的代码来绕过该功能,我认为没有必要。此外,当应用程序进入后台时存储当前位置,并在应用程序返回前台(可能几周后)时假设这是一个有效位置,这对我来说听起来不合适。
RxCocoa 包已经有一个 CLLocationManager 的 Rx 包装器。在我看来,使用它会简单得多。如果您只需要一个位置更新,则使用 .take(1)
。我倾向于在 take(1) 之前添加一个关于位置准确性的过滤器。