Reputation: 6680
I'm seeing this error in the console and not sure what the cause is? I'm requesting user location once.
2018-09-12 20:04:26.912292-0400 Watch Extension[40984:3250895] [Client] Failure to deallocate CLLocationManager on the same runloop as its creation may result in a crash
Code below:
import CoreLocation
class WorkoutLocationManager: NSObject, CLLocationManagerDelegate {
private var locationManager: CLLocationManager?
public var formattedWorkoutAddress: String?
public func getWorkoutLocation() {
guard CLLocationManager.locationServicesEnabled() else {
print("User does not have location services enabled")
return
}
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
let locationAuthorizationStatus = CLLocationManager.authorizationStatus()
switch locationAuthorizationStatus {
case .authorizedAlways:
print("location authorized Always")
locationManager?.requestLocation()
case .authorizedWhenInUse:
print("location authorized When in Use")
locationManager?.requestLocation()
case .denied:
print("location authorization denied")
case .notDetermined:
print("location authorization not determined")
case .restricted:
print("location authorization restricted")
}
}
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.first else { return }
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(currentLocation) { (placemarksArray, error) in
if let unwrappedError = error {
print("Geocoder error: \(unwrappedError)")
}
guard let placemarksArrayUnwrapped = placemarksArray else { return }
if placemarksArrayUnwrapped.count > 0 {
if let placemark = placemarksArray?.first {
let name = placemark.name ?? ""
let subLocality = placemark.subLocality ?? ""
let locality = placemark.locality ?? ""
let state = placemark.administrativeArea ?? ""
// print("address1:", placemark.thoroughfare ?? "")
// print("address2:", placemark.subThoroughfare ?? "")
// print("city:", placemark.locality ?? "")
// print("state:", placemark.administrativeArea ?? "")
// print("zip code:", placemark.postalCode ?? "")
// print("country:", placemark.country ?? "")
let workoutLocationAsString = (name + " " + subLocality + " " + locality + " " + state)
print("workoutLocationAsString = \(workoutLocationAsString)")
self.formattedWorkoutAddress = workoutLocationAsString
} else { print("no placemark")}
} else { print("placemark.count = 0")}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("location manager error = \(error)")
}
}
usage:
private func handleWorkoutSessionState(didChangeTo toState: HKWorkoutSessionState,
from fromState: HKWorkoutSessionState) {
switch (fromState, toState) {
case (.notStarted, .running):
workoutLocationManager.getWorkoutLocation()
Upvotes: 4
Views: 1935
Reputation: 388
CLLocationManager
must be created in a run loop. If you don't do that, you will receive the following warning from CoreLocation
:
A location manager was created on a dispatch queue executing on a thread other than the main thread. It is the developer's responsibility to ensure that there is a run loop running on the thread on which the location manager object is allocated. In particular, creating location managers in arbitrary dispatch queues (not attached to the main queue) is not supported and will result in callbacks not being received.
In your case, it looks like your locationManager
it's being created in the main thread
, but an instance of WorkoutLocationManager
is being used on some other thread, which causes it to be deallocated in this thread, and consequently, deallocating locationManager
in the very same thread.
One option is to make sure that your instance of WorkoutLocationManager
will be created and used only in the main thread.
Another option, that I haven't fully tested, is to try forcing the creation and deallocation of locationManager
in the main thread, like so:
class WorkoutLocationManager: NSObject {
var locationManager: CLLocationManager!
override init() {
super.init()
self.performSelector(onMainThread: #selector(initLocationManager), with: nil, waitUntilDone: true)
}
@objc private func initLocationManager() {
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
}
@objc private func deinitLocationManager() {
locationManager = nil
}
deinit {
self.performSelector(onMainThread: #selector(deinitLocationManager), with: nil, waitUntilDone: true)
}
}
Let me know what you think about it.
Upvotes: 5