0
import Foundation
import CoreLocation

class LocationOperation: Operation, CLLocationManagerDelegate {

  // MARK: Properties
  private var manager: CLLocationManager?
  private let handler: (CLLocation) -> Void

  // MARK: Initialization
  init(locationHandler: @escaping (CLLocation) -> Void) {
    self.handler = locationHandler
    super.init()
  }

  // MARK: Main
  override func main() {
    DispatchQueue.main.async {
      let manager = CLLocationManager()
      manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
      manager.delegate = self
      manager.startUpdatingLocation()
    }
  }

  override func cancel() {
    DispatchQueue.main.async {
      self.stopLocationUpdates()
      super.cancel()
    }
  }

  private func stopLocationUpdates() {
    manager?.stopUpdatingLocation()
    manager = nil
  }

  // MARK: CLLocationManagerDelegate
  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    guard let location = locations.last,
      location.horizontalAccuracy <= manager.desiredAccuracy else {
        return
    }
    stopLocationUpdates()
    handler(location)
  }

  func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    stopLocationUpdates()
    print("Failure to find location") // handle this eventually
    self.cancel()
  }
}

main finishes executing before CLLocationManager gets a chance to get the location and pass that to the passed in handler. The first attempted solution was to override the isExecuting property and manually set it to true after I call handler(location) in (_:didUpdateLocations) but that's a read only property. This link provided a way to do that, but I wasn't sure about the implementation. How do I stop the operation from finishing before it passes the location to the handler? . Thanks!

Nick Rizzo
  • 15
  • 5

1 Answers1

0

Two serious issues:

  1. manager is declared locally in main() and get destroyed after main() exits.

    Replace

    private var manager: CLLocationManager?
    

    with

    private let manager = CLLocationManager()
    

    and delete the local variable

    let manager = CLLocationManager()

    in main(). I'd even prefer a lazy property.

  2. You have to use an asynchronous Operation as described here

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Good catch! I accidentally left the code where I declared manager in main(). The solution in the link works nicely. Thanks. – Nick Rizzo Sep 12 '18 at 00:56