2016-04-22 8 views
1

Ich verwende die RSwift-Bibliothek in meiner App. Ich versuche, den Benutzerstandort zu erhalten, um es in einer Netzwerkanforderung zu senden. Um diesen Standort zu erhalten, muss ich Observables verwenden, da die Funktion einen Fehler auslösen muss, wenn der Benutzer den Standort nicht autorisiert hat.RxSwift und CLLocation: Absturz beim Versuch, den Benutzerstandort zu ermitteln

Dieser Algorithmus ist ein Teil eines verketteten Arrays vieler Observables, die in einem anderen Thread als dem Hauptthread ausgeführt wurden (um die UI nicht einzufrieren). Ich brauche die „get Benutzerstandort“ -Funktion in dem Haupt-Thread ausgeführt werden, da, wenn nicht im Hauptthread ausgeführt es zum Absturz bringen und das Crash-Protokoll ist:

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

In dem obigen Code gibt den Geolocation Helfer init ist (Dies ist ein Singleton) und die Methode ausgeführt, um den Benutzerstandort (oder einen Fehler) zu erhalten.

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

Ich sehe in dem RXSwift Geolocation Beispiel (https://github.com/ReactiveX/RxSwift/pull/429) eine Klasse, die es mit Treibern umgehen kann, aber ich brauche wirklich einen Fehler und Treiber haben, kann keine Fehler zurück.

Ich würde mich freuen, wenn mir jemand helfen kann, dies zu erreichen.

Ich habe bereits versucht, ".observeOn (MainScheduler.instance)" zu den 2 Observablen hinzuzufügen, aber es friert die UI ein ... vielleicht gibt es einen besseren Weg, dies ohne Einfrieren der UI zu tun.

Dank

Antwort

0

@romroll, DelegateProxy für jetzt nur noch auf Hauptthread in RxCocoa funktioniert. Versuchen Sie so etwas wie diese

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

Hoffnung zu tun, hilft dies. Wenn nicht, können Sie erreichen mich immer in der RxSwiftSlack, von @sergdort zu nennen, oder jemand anderes :)

+1

Danke für Ihre Antwort @sergdort. Ich habe deinen Vorschlag ausprobiert, aber es hat nicht funktioniert. Ich habe auch versucht, nur das '.observeOn (MainScheduler.instance)' hinzuzufügen, aber ich habe auch das Problem "Executing on backgound thread". Wenn ich den Aufruf des Geolocation-Codes lösche, funktioniert es, und wenn ich nur den Code hinzufüge, der den 'rx_didChangeAuthorizationStatus' aufruft, stürzt er ab. Es scheint wirklich mit dieser Methode verbunden zu sein. – romroll

+0

Ich habe das gleiche Problem gefunden. Wenn sich der Rx-Code im Callback von locationManager (_manager: CLLocationManager, didUpdateLocations: [CLLocation]) befindet, funktionieren observeOn oder subscribeOn nicht wie erwartet. Der gesamte Code wird nur im Hauptthread ausgeführt. Recht seltsam – User9527

0

Sie haben die Erweiterung CLLocationManager + Rx.swift und RxCLLocationManagerDelegateProxy.swift zu einem Projekt zu kopieren, da es nicht mehr Teil von RxCocoa