I am using RSwift library in my app. I am trying to get the user location in order to send it in a network request. To get this location, I need to use Observables because the function must throw an error in case of user did not authorize location.
This algorithm is a part of a concatenated array of many Observables ran in an another thread than the main thread (in order to not freeze the UI). I need to execute the "get user location" function in the main thread because if not executed in the main thread it crash and the crash log is :
fatal error: Executing on backgound thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread
In the above code, there is the geolocation helper init (this is a singleton) and the method executed to get the user location (or an error).
private var authorized: Observable<Bool?>
private var location: Observable<CLLocationCoordinate2D>
private let locationManager = CLLocationManager()
private init() {
locationManager.desiredAccuracy = LocationOptions.desiredAccuracy
authorized = Observable.just(false)
.map({ _ in CLLocationManager.authorizationStatus() })
.concat(locationManager.rx_didChangeAuthorizationStatus)
.distinctUntilChanged()
.map({ authorizedStatus in
switch authorizedStatus {
case .AuthorizedAlways, .AuthorizedWhenInUse:
return true
case .NotDetermined:
return nil
case .Restricted, .Denied:
return false
}
})
.catchErrorJustReturn(false)
location = locationManager.rx_didUpdateLocations
.filter { $0.count > 0 }
.map({ return $0.last!.coordinate })
}
func getLocation() -> Observable<Location> {
return authorized
.flatMap({ authorized -> Observable<CLLocationCoordinate2D> in
guard let authorized = authorized else {
self.locationManager.requestAlwaysAuthorization()
return Observable.empty()
}
if authorized {
self.startUpdating()
return self.location
} else {
return Observable.error(
NSError(
domain:"",
code: 0,
userInfo:nil )
)
}
})
// We just need one position updated
.take(1)
.map({ coordinate in
self.stopUpdating()
let location = Location(longitude: coordinate.longitude, latitude: coordinate.latitude, latestUpdatedDate: NSDate())
if location.isValid() {
saveLocation(location)
}
return location
})
.doOnError({ error in self.stopUpdating() })
}
I see in the RXSwift geolocation example ( https://github.com/ReactiveX/RxSwift/pull/429 ) a class that can handle it with Drivers but I really need to have an error and Drivers cannot return errors.
I would appreciate if someone can help me on how to achieve this.
I already tried to add " .observeOn(MainScheduler.instance) " to the 2 observables but it freezes the UI.. maybe there is a better way to do this without freezing UI.
Thanks
@romroll, DelegateProxy
works only on Main thread in RxCocoa for now. Try to do something like this
.map({ _ in CLLocationManager.authorizationStatus() })
.observeOn(MainScheduler.instance)
.concat(locationManager.rx_didChangeAuthorizationStatus)
.observeOn(someOtherScheduler)
.distinctUntilChanged()
Hope, this helps. If not, you can always reach me in the RxSwiftSlack , by mentioning @sergdort, or somebody else :)
您必须将扩展CLLocationManager + Rx.swift和RxCLLocationManagerDelegateProxy.swift复制到您的项目,因为它不再是RxCocoa的一部分
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.