Why isn't locationManager didUpdateLocation working? - ios

With CoreLocation.Foundation added to the BuildPhase and imported at the top of the file, I can get location information if I put the following into a view controller with a button:
#IBAction func locationButton(sender: AnyObject) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
It goes on to locationManager didUpdateLocations with a CLGeocoder().reverseGeocodeLocation completionHandler that displays the location info in another function - this works.
BUT, it doesn't work when I try to transfer this same code in my data model. I set up the model with the following:
import CoreLocation
class Record: NSObject, CLLocationManagerDelegate
{
let locationManager = CLLocationManager()
Because there's no button, I've put the locationManager code into:
override init()
{
iD = NSUUID().UUIDString
super.init()
if (CLLocationManager.locationServicesEnabled())
{
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
switch CLLocationManager.authorizationStatus() {
case .AuthorizedWhenInUse, .AuthorizedAlways:
locationManager.startUpdatingLocation()
case .NotDetermined:
locationManager.requestWhenInUseAuthorization() // or request always if you need it
case .Restricted, .Denied:
print("tell users that they need to enable access in settings")
default:
break
}
print("Location services available")
if CLLocationManager.authorizationStatus() == .NotDetermined
{
print("Still Not Determined")
}
} else { print("Location services not available") }
}
I get 'Location services available'. But the following code never prints anything to console, nor does it call the function toSetLocationStamped.
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)
{
print("started location man")
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: //pass the location co-ordinates
{
(placemarks, error) -> Void in
if (error != nil)
{
println("Reverse geocoder failed with error" + error.localizedDescription)
return
}
if placemarks.count > 0 //process the location array (placemarks)
{
let pm = placemarks[0] as! CLPlacemark
self.toSetLocationStamped(pm)
print("got here")
} else
{
println("Problem receiving data from geocoder")
}
})
}
If I put a deinit the class Record, with a simple print log, there is no output.
deinit
{
print("deinit")
}
I'm initializing a dummyRecord: Record from a required init in MasterViewController class:
class MasterViewController: UITableViewController
{
var records = [Record]()
var subjectDescription: String?
// weak var delegate: MonsterSelectionDelegate? // property for object conforming to MSDelegate
required init(coder aDecoder: NSCoder) // // coder because class is loaded from Storyboard
{
super.init(coder: aDecoder)
var dummyRecord1 = Record()
dummyRecord1.details = "All was very good and strong with a little bit of lemon on the side of the hill."
dummyRecord1.dateTimeEntered = NSDate(dateString: "2015-07-22")
dummyRecord1.subject = "Clouds"
dummyRecord1.locationEntered = "Drittelsgasse 1, 69493 Großsachsen, Germany."
dummyRecord1.photos.append(UIImage(named: "zombies.jpg")!)
records.append(dummyRecord1)
}

After you call
self.locationManager.requestWhenInUseAuthorization()
you cannot immediately start updating location. That call is asynchronous.
Here is how you should properly do it:
1) Check the authorisation status:
switch CLLocationManager.authorizationStatus() {
case .AuthorizedWhenInUse, .AuthorisedAlways:
locationManager.startUpdatingLocation()
case .NotDetermined:
locationManager.requestWhenInUseAuthorization() // or request always if you need it
case .Restricted, .Denied:
// tell users that they need to enable access in settings
default:
break
}
2) If you have previously authorised your app this should update location. However, if you didn't a popup will appear. In order to respond to the change in the authorisation status you need to add another function:
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == .AuthorizedAlways) || (status == .AuthorizedWhenInUse) {
locationManager.startUpdatingLocation()
}
}

Related

swift user location - update if move

So I have been able to get the users location via the following code.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status != .authorizedWhenInUse {return}
print("test LOCATION BELOW")
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
let locValue: CLLocationCoordinate2D = manager.location!.coordinate
print("UUID: \(String(describing: uuid)) locations = \(locValue.latitude) \(locValue.longitude)")
}
However I want to watch the users location and if they move update their location.
I am wondering how do I get this code to keep checking for the users location?
My
override func viewDidLoad(){
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()}
I get it popping up the request and I approve it but it does not run the code
Here is how you can get the location data. The code is similar to yours but with a bit of change and additions.
First check if you already have user's permission to get their location data.
func isLocationServicesEnabled() -> Bool {
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
return false
case .authorizedAlways, .authorizedWhenInUse:
return true
#unknown default:
return false
}
}
return false
}
If this method returns false you can ask for authorization.
// Initialize manager in your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.delegate = self
// Do other stuff
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Check for auth
if isLocationServicesEnabled() {
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
// Do other stuff
}
Finally in your CLLocationManagerDelegate implementation get the coordinates.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last?.coordinate else { return }
// Use location.latitude and location.longitude here
// If you don't want to receive any more location data then call
locationManager.stopUpdatingLocation()
}
First, you have to check that you have user's permission to get their location data
func checkLocationServicesAuthorization() -> Bool {
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
return false
case .restricted, .denied:
//code for open settings screen in user’s phone
return false
case .authorizedWhenInUse, .authorizedAlways:
return true
default:
return false
}
} else {
//code for open settings screen in user’s phone
}
return false
}
And
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if checkLocationServicesAuthorization() {
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
You can use delegate method didUpdateLocations of CLLocationManagerDelegate and don't forgot to connect delegate yourLocationManager.delegate = self
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
// Call stopUpdatingLocation() to stop listening for location updates, other wise this function will be called every time when user location changes.
// yourLocationManager.stopUpdatingLocation()
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
let latitude = String.init(userLocation.coordinate.latitude)
let longitude = String.init(userLocation.coordinate.longitude)
print("\(latitude), \(longitude)")
}

Location Service as a Singleton in Swift, get stuck on "When In Use"

I'm programming an app that needs "Always location" and I decided to use a Singleton to keep tracking active since I need most of the time the location services even in the background.
When I run the application on my iPhone, the console says that the location service is in "When In Use" mode and my protocol don't get the location updates from the LocationManager.
What's wrong with my Singleton (I'm a Swift newbie please be clear in your answers.
Is it a good idea to use a Singleton for Location Services ?
LocationService.swift (UPDATED)
import Foundation
import CoreLocation
protocol LocationServiceDelegate {
func onLocationUpdate(location: CLLocation)
func onLocationDidFailWithError(error: Error)
}
class LocationService: NSObject, CLLocationManagerDelegate {
public static let shared = LocationService()
var delegate: LocationServiceDelegate?
var locationManager: CLLocationManager!
var currentLocation: CLLocation!
private override init() {
super.init()
self.initializeLocationServices()
}
func initializeLocationServices() {
self.locationManager = CLLocationManager()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestAlwaysAuthorization()
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
case .notDetermined:
self.locationManager.requestAlwaysAuthorization()
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("User choosed locatiom when app is in use.")
default:
print("Unhandled error occured.")
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.currentLocation = locations.last!
locationChanged(location: currentLocation)
}
private func locationChanged(location: CLLocation) {
guard let delegate = self.delegate else {
return
}
delegate.onLocationUpdate(location: location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.locationManager.stopUpdatingLocation()
locationFailed(error: error)
}
private func locationFailed(error: Error) {
guard let delegate = self.delegate else {
return
}
delegate.onLocationDidFailWithError(error: error)
}
}
Then I initialize the singleton :
AppDelegate.swift
let locationService = LocationService.shared
Then my View conforms to my protocol :
ViewController.swift
extension ViewController: LocationServiceDelegate {
func onLocationUpdate(location: CLLocation) {
print("Current Location : \(location)")
}
func onLocationDidFailWithError(error: Error) {
print("Error while trying to update device location : \(error)")
}
}
Yes, You can use singleton for your purpose. Few things you can check with your implementation:
locationManager.pausesLocationUpdatesAutomatically = false.
enable background modes for location updates.
Switch to significant location updates when the app moves to background.
Is it a better way to send notifications to all viewControllers to pass the CLLocation object or its better to conform to my protocol in every controllers ?

Location services permission alert disappears

I am asking for requestWhenInUseAuthorization from CLLocationManager and the user prompt displays but then quickly disappears.
According to other posts on SO it says that you have to declare your CLLocationManager var at the class level to fix this.
Problem is that I am already doing this and this still happens.
Any help would be appreciated.
class myLocation: CLLocationManagerDelegate {
var locationManager:CLLocationManager?
// Have also tried it as: let locationManager = CLLocationManager()
func start() {
if CLLocationManager.locationServicesEnabled() {
self.locationManager = CLLocationManager()
guard let manager = self.locationManager else {
print("Error creating Location Manager")
return
}
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
manager.requestWhenInUseAuthorization()
}
else {
print("Location Services Disabled")
}
}
//Delegate Methods
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways, .authorizedWhenInUse:
manager.startUpdatingLocation()
default:
manager.requestWhenInUseAuthorization()
}
}
...
}
The key to fixing this was that the var I instantiated this class too was a local var. I moved that to a class var and it no longer disappears.
So for example:
class ViewController: UIViewController {
let location = myLocation.init()
override func viewDidLoad() {
super.viewDidLoad()
location.start()
}
}

swift - didUpdateLocations Not Being Called

I'm working on an app that requires getting the user's current coordinates. I was planning on doing this through CLLocationManager's didUpdateLocations method. For some reason, didUpdateLocations is not being executed. However, it appears that locationManager.startUpdatingLocation() is being called successfully. None of the other possible solutions I've seen on this site have worked for me. I already added NSLocationAlwaysUsage to info.plist. Here is the entirety of my code:
import UIKit
import MapKit
import CoreLocation
var region: MKCoordinateRegion!
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse, .authorizedAlways:
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
print("Updating location now")
}
case .notDetermined:
locationManager.requestAlwaysAuthorization()
case .restricted, .denied:
print("User must enable access in settings")
break
}
if (region == nil){
}
else {
map.setRegion(region!, animated: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Got location")
let userLocation:CLLocation = locations[0]
let lat:CLLocationDegrees = userLocation.coordinate.latitude
let long:CLLocationDegrees = userLocation.coordinate.longitude
let currentPos:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, long)
didUpdateRegion(position: currentPos)
print(lat)
print(long)
}
func didUpdateRegion(position: CLLocationCoordinate2D) {
let span = MKCoordinateSpanMake(0.075, 0.075)
region = MKCoordinateRegion(center: position, span: span)
}
func locationManager(_manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
// If status has not yet been determied, ask for authorization
manager.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse:
// If authorized when in use
manager.startUpdatingLocation()
break
case .authorizedAlways:
// If always authorized
manager.startUpdatingLocation()
break
case .restricted:
print("User must activate location services in settings")
break
case .denied:
print("User must activate location services in settings")
break
default:
break
}
}
When I run this code on both the simulator and an actual device, I get the notification to allow location tracking. After accepting that, the console displays "Updating location now," but never gets to printing "Got location." Thank you for any light you can shed on this issue, I'm new to app development in general.
EDIT: I added in the entirety of my code instead of just the parts I thought were relevant. Basically, I'm trying to get the region shown on the map to follow the user. I attempt to do this by updating the variable "region" every time the didUpdateLocations function fires.
Am I getting it right and you only added one key - NSLocationAlwaysUsage?
Try to add both keys to the Info.plist:
Privacy - Location When In Use Usage Description
Privacy - Location Always Usage Description
Also, what happens if you implement this method of protocol?
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(error.localizedDescription)")
}
Does it print anything? Sorry, I was going to leave a comment, but I don't have enough reputation.

Location Manager not updating location in swift. AlertView disappears

I want to get location inside a custom delegate in swift. Note that this worked perfectly 2 hours ago. The major problem is that the Location authorization alertView disappears by its own before I get to Allow it. So i tried to go within settings and allow it but it does not work. Why is the alertView disappearing by it self and why even though I allowed it through the settings I still cannot get an update? I added the correct key in the plist and also added the delegate and CoreLocation framework in the file. Also note that the didFail is not called at any point. Any advise would be appreciated
func getLocation(){
println("called")
let locationManager:CLLocationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
let status = CLLocationManager.authorizationStatus()
println(status.rawValue)
if(status != CLAuthorizationStatus.Authorized) {
locationManager.requestWhenInUseAuthorization()
println("called2")
}else{
locationManager.startUpdatingLocation()
println("allowed and updating")
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
println("updating")
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
println(locationObj)
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println(error)
}
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
println(status)
switch status {
case CLAuthorizationStatus.Restricted:
println("Restricted Access to location")
case CLAuthorizationStatus.Denied:
println("User denied access to location")
case CLAuthorizationStatus.NotDetermined:
println("Status not determined")
default:
println("Allowed to location Access")
shouldIAllow = true
}
if (shouldIAllow == true) {
manager.startUpdatingLocation()
} else {
println("Denied access: \(status)")
}
}
Create a property from locationManager , because this way it is destroyed after you run your method. And don't forget to setup its delegate for example in viewDidLoad.

Resources