Coder Social home page Coder Social logo

Comments (7)

bobgodwinx avatar bobgodwinx commented on July 17, 2024 1

Hi @mcaylus The delegate is set when you actually start subscribing to it and it is set on the given CoreLocationManager. This is how Rx works in relation to all other delegates methods.
Now let me clarify the blurry path. The func requestLocation() requires that you set a Delegate before you trigger it. Doing the same operation imperatively gives you that same error. According to the documentation " Only one location fix is reported to the delegate, after which location services are stopped" This in practice means it does require a delegate immediately to hand off the location information. So IMHO there is a pre-check within the CoreLocationManager that throws the error *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Delegate must respond to locationManager:didUpdateLocations: However you can still obtain the same result by using startUpdatingLocation() and stopUpdatingLocation()

from rxcorelocation.

bobgodwinx avatar bobgodwinx commented on July 17, 2024

Hi @mcaylus I just tried your code you should change it a little bit take a look at this example it should work for your needs:

class ViewController: UIViewController {
    
    let bag = DisposeBag()
    let manager = CLLocationManager()

    private var _service: CoreLocationService {
        self.manager.requestWhenInUseAuthorization()
        self.manager.startUpdatingLocation()
        return CoreLocationService(locationManager: self.manager)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        _service.requestLocation().subscribe(onSuccess: {_coordinate in
            print(_coordinate.latitude)
        }, onError: {_error in
            print(_error.localizedDescription)
        }).disposed(by: bag)
    }
}

protocol LocationService { }
class CoreLocationService: LocationService {

    private let locationManager: CLLocationManager
    private let disposeBag = DisposeBag()

    init(locationManager: CLLocationManager) {
        self.locationManager = locationManager
    }

    public func requestLocation() -> Single<CLLocationCoordinate2D> {
        return Single.deferred {
            return Observable.create { [unowned self] (observer) in
                let locationError = self.locationManager
                    .rx
                    .didError
                    .subscribe( onNext: { (_, error) in
                        observer.onError(error)
                    })

                let location = self.locationManager
                    .rx
                    .didUpdateLocations
                    .filter { $1.count > 0 }
                    .map { $1.last!.coordinate }
                    .subscribe(observer)

                //self.locationManager.requestLocation()

                let cancel = Disposables.create(with: {
                    self.locationManager.stopUpdatingLocation()
                })

                return CompositeDisposable(cancel, locationError, location)
                }.take(1).asSingle()
        }
    }
}

from rxcorelocation.

 avatar commented on July 17, 2024

Hi @bobgodwinx thanks for taking time to get back to me. It look like from your implementation that the .deferred is not needed anymore but the issue relate to usage of requestLocation. Can you explain me why this method can't be used in this context? I wanted to use it as it requests a one-time delivery of the user’s current location which is what I need.

I have simplify my code by merging two stream:

func requestLocation() -> Single<CLLocationCoordinate2D> {
        let locationStream: Observable<CLLocationCoordinate2D> = self.locationManager
            .rx
            .didUpdateLocations
            .filter { $1.count > 0 }
            .map { $1.last!.coordinate }

        let errorStream: Observable<CLLocationCoordinate2D> = self.locationManager
                .rx
                .didError
                .flatMap { return Observable.error($0.error) }

        return Observable.merge(locationStream, errorStream)
            .take(1).asSingle()
            .do(onSubscribe: { [unowned self] in self.locationManager.startUpdatingLocation() },
                onDispose: { [unowned self] in self.locationManager.stopUpdatingLocation() })
    }

I still have some blurry parts:

  • Can you provide me more insight on when the CoreLocationManager delegate methods are set (this relates to the error I got in my log)?
  • Does this implementation differ from extensions provided on the RxSwift official repository?

from rxcorelocation.

 avatar commented on July 17, 2024

Hi @bobgodwinx thanks for the hint. From what you are saying I should be able to call requestLocation on onSubscribed: instead of onSubscribe: then but this will crash as well. Do you see a potential bug here? Is implementation of RxCoreLocation in sync with the extensions in RxSwift repo?

from rxcorelocation.

bobgodwinx avatar bobgodwinx commented on July 17, 2024

@mcaylus I can't really follow your last comment here is the link to the implementation and I can't find any bug within. Please kindly review it and if you find anything wrong that we can change please feel free to send in a PR and I will review it asap. Thanks

from rxcorelocation.

 avatar commented on July 17, 2024

Let me compare the behavior on the RxSwift repo for extensions on CoreLocation with the extensions provided here on this repo and I will get back to you.

from rxcorelocation.

nteissler avatar nteissler commented on July 17, 2024

Hey @mcaylus I was running into a similar crash and log message going through a modified version of Ray Wenderlich's RxSwift's Chapter 13. I was using the implementation @bobgodwinx linked to above, but still getting a crash when I called locationManager.requestLocation. His answer:

Now let me clarify the blurry path. The func requestLocation() requires that you set a Delegate before you trigger it. Doing the same operation imperatively gives you that same error. According to the documentation " Only one location fix is reported to the delegate, after which location services are stopped" This in practice means it does require a delegate immediately to hand off the location information

cleared it up for me. Additionally, if the locationManager.rx.didUpdateLocations is subscribed to, then that subscription is disposed, it looks like the delegate proxy is torn down enough to also throw that NSException. In my case, I needed something that would keep the subscription alive, even when there were no subscribers. As usual RxSwift has the thing: ConnectableObservable.publish()

let currentLocation = locationManager.rx.didUpdateLocations.map { locations in
    return locations.first!
}
.filter { location in
    return location.horizontalAccuracy < kCLLocationAccuracyHundredMeters
}.publish()

// Set the delegate by subscribing before `requestLocation` can be called.
// This subscription is disposed of immediately and doesn't consume the CLLocation
// requested becuase the observable is shared and the coordination is set up so that
// location isn't requested until the observable that is bound to the UI is subscribed as well.
currentLocation.take(1).debug("Ensure Delegate is Set").subscribe(onNext: { location in
    print("debugged location")
}).disposed(by: bag)
currentLocation.connect().disposed(by: bag)

let geoInput = geolocationButton.rx.tap.asObservable()
    .do(onNext: {
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.requestLocation()
    }).share()

from rxcorelocation.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.