Memory error implementing CLLocationManagerDelegate in a separate class - ios

I try to move CLLocationManagerDelegate implementation to a separate class (file) in order not to clutter ViewController code but get memory error every time EXC_BAD_ACCESS (code=1, address=0xc)
What am I doing wrong here?
Here's my implementation:
class ViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = LocationManagerDelegate()
// >=iOS8
if (locationManager.respondsToSelector(Selector("requestWhenInUseAuthorization"))) {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.startUpdatingLocation()
}
}
}
class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
// …
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
// …
}
}

Delegates normally are weak so there is no object retaining your delegate and that's the cause of your Bad memory access error.
You should do something similar to this:
class ViewController: UIViewController {
let locationManager = CLLocationManager()
//instantiate and hold a strong reference to the Core Location Manager Delegate
//Normally you don't need this because the delegate is self
let locationManagerDelegate = LocationManagerDelegate()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self.locationManagerDelegate
// >=iOS8
if (locationManager.respondsToSelector(Selector("requestWhenInUseAuthorization"))) {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.startUpdatingLocation()
}
}
}
class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
// …
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
// …
}
}

Related

Issue with CLLocationManager and CLLocationManagerDelegate

I'm running Xcode 10 and iOS 12
I'm getting this warning on each of the delegate methods coded in the class extension of my CLLocationManager singleton:
Instance method 'locationManager(:didChangeAuthorization:)' nearly matches optional requirement 'locationManager(:didChangeAuthorization:)' of protocol 'CLLocationManagerDelegate'
Here is the code:
import Foundation
import CoreLocation
public class PhysicalLocationManager: NSObject {
/*--------------------------------------------------------------------------------*/
//MARK: - Create Singleton Shared Instance
/*--------------------------------------------------------------------------------*/
static let sharedInstance: PhysicalLocationManager = {
let instance = PhysicalLocationManager()
return instance
}()
let locationMgr: CLLocationManager
/*--------------------------------------------------------------------------------*/
//MARK: - Initialization
/*--------------------------------------------------------------------------------*/
override init() {
locationMgr = CLLocationManager()
locationMgr.distanceFilter = kCLDistanceFilterNone
locationMgr.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
super.init()
locationMgr.delegate = self
}
func enableBasicLocationServices() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationMgr.requestWhenInUseAuthorization()
break
case .restricted, .denied:
// Disable location features
// TODO: disableMyLocationBasedFeatures()
break
case .authorizedWhenInUse, .authorizedAlways:
// Enable location features
enableWhenInUseFeatures()
break
}
}
func enableWhenInUseFeatures() {
locationMgr.startUpdatingLocation()
if CLLocationManager.locationServicesEnabled() {
locationMgr.requestLocation()
}
}
}
extension PhysicalLocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("\(manager)\tCLLocationManager, didChangeAuthorization\n\(status)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("\(manager)\tCLLocationManager, didUpdateLocations\n\(locations)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
// locationManager.stopUpdatingLocation()
}
}
Can anyone see what I am doing wrong here?
Thanks,
Because your PhysicalLocationManager class is public, the delegate methods need to be public as well. Simply add public in front of the three delegate methods and the warnings go away.

How to track set locationManager.delegate the right way?

I tried to use CoreLocation with Swift to track the user's location:
(Below you can find the code of may ViewController.swift file.)
But the code doesn't seems working as I've expected, because I'm still getting the same error every-time I'm launching the application:
I'm sure this is the problem why I'm not able to get a result from the locationManager() function that should print out the current location.
It says "Cannot assign value of type 'ViewController' to type 'CLLocationManagerDelegate?'"
import UIKit
import CoreLocation
class ViewController: UIViewController, WKUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
Does anyone now how to fix this problem? - Any help would be very appreciated, thanks a million in advance.
You simply need to declare conformance to CLLocationManagerDelegate. You can either do this directly in the class declaration just as you did with WKUIDelegate or in an extension of ViewController.
class ViewController: UIViewController, WKUIDelegate, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
With extension:
class ViewController: UIViewController, WKUIDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}

get user location and updates

I am trying to get user location in my iPhone app. It has specified in info.plist needed permission (use location even if app is not offline). But still I do not see that my delegate is triggered at least once meaning I do not get even current location. Here is the code:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager();
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.notDetermined) {
self.locationManager.requestAlwaysAuthorization();
}
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0]
let long = userLocation.coordinate.longitude;
let lat = userLocation.coordinate.latitude;
print(lat)
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!)
{
locationManager.stopUpdatingLocation()
if ((error) != nil)
{
print(error)
}
}
Could somebody point me out possible problems?
Try to remove locationManager.stopUpdatingLocation() in locationManager(manager: CLLocationManager!, didFailWithError error: NSError!). It should work.

locationManager:didUpdateLocations never called

I have this code in my ViewController:
#IBAction func testButton(sender: UIButton) {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
print("\(CLLocationManager.locationServicesEnabled())")
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
print("Started updating location")
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let long = userLocation.coordinate.longitude
let lat = userLocation.coordinate.latitude
print("\(long), \(lat)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Location update failed: \(error)")
}
NSLocationWhenInUseUsageDescription in my plist. It should just work according to all information I could find, but it just outputs
true
Started updating location
Is edit 2 of this question true? If so, what would be the best way to move this out of the viewController?
Before startUpdatingLocation(), have to ask for permission using requestWhenInUseAuthorization() first.
So your codes in ViewController will be similar like the following or you can download a sample project here.
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.testButton()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func testButton() {
if CLLocationManager.authorizationStatus() != .AuthorizedWhenInUse {
self.locationManager.requestWhenInUseAuthorization()
return
}
self.locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let long = userLocation.coordinate.longitude
let lat = userLocation.coordinate.latitude
print("\(long), \(lat)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Location update failed: \(error)")
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
self.testButton()
}
}
}

Shared instance of CLLocationManager

I was trying to create simple shared instance of CLLocationManager for my app.
There was no problem creating shared instance:
import UIKit
import CoreLocation
protocol LocationHandlerDelegate: class {
func locationHandlerDidUpdateLocation(location: CLLocation?)
func locationHandlerDidFailWithError(error: NSError)
}
class LocationHandler: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var location: CLLocation?
weak var delegate: LocationHandlerDelegate?
static let sharedInstance = LocationHandler()
override init() {
super.init()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
if CLLocationManager.authorizationStatus() == .NotDetermined {
locationManager.requestWhenInUseAuthorization()
}
}
// MARK: -
// MARK: - CLLocationManagerDelegate functions
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
delegate?.locationHandlerDidUpdateLocation(locations.last)
self.location = locations.last
locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
delegate?.locationHandlerDidFailWithError(error)
locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedAlways || status == .AuthorizedWhenInUse {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
}
}
}
But for some reason when I call my shared instance from another class:
class TodayViewController: UIViewController, CLLocationManagerDelegate, LocationHandlerDelegate {
super.viewDidLoad() {
LocationHandler.sharedInstance.delegate = self
if #available(iOS 9.0, *) {
LocationHandler.sharedInstance.locationManager.requestLocation()
} else {
LocationHandler.sharedInstance.locationManager.startUpdatingLocation()
}
}
...
// MARK: - LocationHandlerDelegate function
func locationHandlerDidUpdateLocation(location: CLLocation?) {
if let location = location {
print("Current lcoation: \(location)")
}
else {
// ...
}
}
func locationHandlerDidFailWithError(error: NSError) {
print("Error finding lcoation: \(error.localizedDescription)")
}
}
None of CLLocationManagerDelegate functions in my LocationHandler are called.
But if in TodayViewController I write
LocationHandler.sharedInstance.locationManager.delegate = self
instead of
LocationHandler.sharedInstance.delegate = self
and I implement CLLocationManagerDelegate functions in my TodayViewController instead of LocationHandler then those delegate functions are called. So I guess there may be some problem with instancing? Or am I missing something else?

Resources