2016-04-22 4 views
1

Я использую библиотеку RSwift в своем приложении. Я пытаюсь получить местоположение пользователя, чтобы отправить его по сетевому запросу. Чтобы получить это местоположение, мне нужно использовать Observables, потому что функция должна выдать ошибку, если пользователь не разрешил местоположение.RxSwift и CLLocation: сбой при попытке получить местоположение пользователя

Этот алгоритм является частью конкатенированного массива многих Observables, запущенных в другом потоке, чем основной поток (чтобы не замораживать пользовательский интерфейс). Мне нужно, чтобы выполнить функцию «получить местоположение пользователя» в главном потоке, потому что, если не выполняется в главном потоке его падение и журнал крах:

fatal error: Executing on backgound thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread 

В приведенном выше коде, есть геолокации помощник инициализации (это singleton) и метод, выполненный для получения местоположения пользователя (или ошибки).

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() }) 
} 

Я вижу в RXSwift геолокации примере (https://github.com/ReactiveX/RxSwift/pull/429) класса, который может обрабатывать его с драйверами, но мне действительно нужно иметь ошибку и драйверы не могут возвращать ошибки.

Буду признателен, если кто-то может помочь мне в том, как этого добиться.

Я уже пытался добавить «.observeOn (MainScheduler.instance)» к 2 наблюдаемым, но он замораживает пользовательский интерфейс. Возможно, есть лучший способ сделать это без замораживания пользовательского интерфейса.

Благодаря

ответ

0

@romroll, DelegateProxy работает только на главном потоке в RxCocoa сейчас. Попробуйте сделать что-то вроде этого

.map({ _ in CLLocationManager.authorizationStatus() }) 
     .observeOn(MainScheduler.instance) 
     .concat(locationManager.rx_didChangeAuthorizationStatus) 
     .observeOn(someOtherScheduler) 
     .distinctUntilChanged() 

Надежда, это помогает. Если нет, то вы всегда можете связаться со мной в RxSwiftSlack, упоминая @sergdort, или кто-то еще :)

+1

Спасибо за ваш ответ @sergdort. Я попробовал ваше предложение, но оно не сработало. Я также попытался только добавить '.observeOn (MainScheduler.instance)', но у меня также есть проблема «Выполнение на backgound thread». Когда я удаляю весь вызов кода Geolocation, он работает, и когда я добавляю только код, вызывающий 'rx_didChangeAuthorizationStatus', он сбой. По-видимому, это связано с этим методом. – romroll

+0

Я нашел ту же проблему. Если Rx-код находится в обратном вызове locationManager (_ manager: CLLocationManager, didUpdateLocations location: [CLLocation]), функции observOn или subscribeOn не работают так, как мы ожидали, весь код выполняется только в основном потоке. Довольно странно – User9527

0

Вы должны скопировать расширение CLLocationManager + Rx.swift и RxCLLocationManagerDelegateProxy.swift к вашему проекту, как это уже не часть RxCocoa

Смежные вопросы