CLLocationManagerDelegate Method Not Being Called - ios

I am coding in Swift and I want to use the user's location in my app. I have already added NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription to Info.plist and have properly configured them. I have also added CoreLocation.framework and imported it in the ViewController. I am also using CLLocationManagerDelegate in my class definition. I have implemented the method didChangeAuthorizationStatus and this works when the user chooses to allow for their location to be given to the app. I have printed out text to console when this happens to make sure. However, when I call the method didUpdateLocations from this method right after printing to the console, the method does not run and nothing new is printed to console. I have made sure to include a print command in the didUpdateLocations method just to make sure of this. This is the code for one of the methods:
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
print("permission granted")
locationManager.startUpdatingLocation()
} else {
print(status)
}
}
As you can see, in the method, locationManager.startUpdatingLocation() is called and it is supposed to start doing that. Here is the code for this method:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
}
However, once I press Allow in my app when it asks me, it doesn't print anything to the console besides "permission granted", which was called in the first method. How do I fix this? Any help is greatly appreciated!
EDIT:
Well, actually I just fixed the problem by simulating movement in the simulator phone. You just have to click Debug, hover over Location, and pick a form of movement!

Related

Properly launching an iOS app into the background from a location event

(iOS 11, Swift 4.1)
I wrote a cordova plugin in swift to handle region monitoring, but it isn't working properly when my app is suspended or killed. Here is my class with the relevant functions:
class GeofenceManager : NSObject, CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
switch state {
case .inside:
log ("Did Enter Region: " + region.identifier)
self.postGeofenceTransition(region: region, transitionType: 1)
break
case .unknown:
log ("Unknown Transition for region: " + region.identifier)
// self.postGeofenceTransition(region: region, transitionType: 1)
break
default:
break
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
log ("Did Exit Region: " + region.identifier)
self.postGeofenceTransition(region: region, transitionType: 2)
}
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
log("Did start monitoring region: " + region.identifier)
self.locationManager.requestState(for: region)
}
}
(I do it this way to handle someone that's already in the region when we start monitoring)
I've also got "didChangeAuthorizationStatus" that fetches and sets all the geofences, that is working fine.
In my AppDelegate (objc) I'm attempting to start Location Services if the app is opened from a region event, with
if([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
_locationManager = [[CLLocationManager alloc] init];
[_locationManager setDelegate:self];
[_locationManager setDistanceFilter:kCLHeadingFilterNone];
//change the desired accuracy to kCLLocationAccuracyBest
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
//SOLUTION: set setPausesLocationUpdatesAutomatically to NO
[_locationManager setPausesLocationUpdatesAutomatically:NO];
[_locationManager startUpdatingLocation];
}
My question is, if my app is opened from an event, i.e. "didEnterRegion", I immediately start location services, but will I hit my "didEnterRegion" delegate again? And am I doing something wildly wrong here?
I think I'm missing some understanding of iOS application states, have dug into the docs but it hasn't clicked yet. Any insight is very appreciated.
My question is, if my app is opened from an event, i.e. "didEnterRegion", I immediately start location services, but will I hit my "didEnterRegion" delegate again?
If your app was suspended or terminated, and if it is awakened or launched just to receive didEnterRegion, it stays in the background. It is given time just to handle this one event and then it is suspended.
You cannot start location updates with startUpdatingLocation at that point. You are in the background! Even if you are authorized for background updates, you cannot start getting updates while you are in the background. And even if you could, your code would fail, because you never set allowsBackgroundLocationUpdates. And even if you could do that, you wouldn't get any events, because you have no implementation of didUpdateLocation.
The correct procedure is: do nothing. Just handle didEnterRegion and get out. If the user enters the region again, your didEnterRegion will be called again. Region monitoring just goes on forever until you stop it (and make sure you do stop it, or the user may be be forced to delete your app).
Addendum: How to respond to being launched from scratch in the background like this? You can detect the key in didFinishLaunchWithOptions and handle it there and return false, or you can ignore it and return true and receive didEnterRegion. But either way you must immediately create a location manager and appoint its delegate or you will get nothing, obviously. This is why you should always create the location manager and set its delegate in your app delegate or root view controller, something that always exists, and exists as early as possible in the life of the app, as soon as it launches.

What's the proper way to use startMonitoringSignificantLocationChanges in swift 4?

I try to make a running app, and I want to use the method startMonitoringSignificantLocationChanges, but I'm missing something because is not firing the didUpdateLocations that the documentation says it should.
I have successfully used the startUpdatingLocation method of the CLLocationManager class and I can totally can use it for my interests, but I'd like to understand why my implementation doesn't work.
So, I have this code:
let locationManager = CLLocationManager()
func determineMyCurrentLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
print("authorization Status: \(CLLocationManager.authorizationStatus().rawValue)")
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation() //(1)
//(2)locationManager.startMonitoringSignificantLocationChanges()
locationManager.distanceFilter = 20
//locationManager.start
//locationManager.startUpdatingHeading)(
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("yaaay!") }
If you comment (1) and uncomment 2 the func locationManager...etc is never fired.
Also, I have my info.plist with the proper keys set, "Privacy - Location When In Use Usage Description" and "Privacy - Location Always and When In Use Usage Description").
Any idea?
Thanks in advance.
EDITED: My question was imprecise. I put the locationManager property as a class member and I completed the properties in my info.plist
The startMonitoringSignificantLocationChanges requires always authorisation. That also means you should turn on "Background Modes" in Capabilities section and selection "Location Updates". You also require the Privacy - Location Always Usage Description Try these two and you should receive significant location change updates.

iOS app crashes on launch screen, location is requested after app crashes (Xcode 8.3.3)

When I run my iOS app from Xcode to the iOS simulator or to my physical device, the app crashes within a couple of seconds. As the app then enters the background and I am returned to the iPhone homescreen, the Alert View asking for permission to use my location pops up but then quickly disappears before I can select an answer.
After declaring and initializing a CLLocationManager called "locationManager", I believe that the errors are triggered from these statements:
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
The main error that appears in the console logs is:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Delegate must respond to locationManager:didFailWithError:'
I have set my usage description with the "NSLocationWhenInUseUsageDescription", so that my app can present to the user what their location will be used for, thus giving the app permission to access their location. Is there anything else I am missing that may contribute to this error?
In order to get the location of the user, do I only need it request it through adding the "NSLocationWhenInUseUsageDescription" key to the Info.plist file, or are there more measures I need to approach?
To solve your problem:
Create a class like, and override the methods as below:
class LocationDelegate : NSObject, CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// here you will receive your location updates on `locations` parameter
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// if something goes wrong comes to here
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// here you can monitor the authorization status changes
}
}
Create a global instance of this class wherever you want.
let locationDelegate: LocationDelegate = LocationDelegate()
Then before you ask permission set the delegate:
locationManager.delegate = locationDelegate

Getting current location using CLLocationManager in Swift

How do I get the current location of my iOS device?
It seems that Apple has made changes that to how get location and I could not find an up-to-date post. I answered this question myself below:
I couldn't find an up-to-date solution to getting the current location so here's how I did it.
My source is apple's sample code on location tracking and smart watch called PotLocCoreLocationwithiPhoneandAppleWatch: https://github.com/robovm/apple-ios-samples/tree/master/PotLocCoreLocationwithiPhoneandAppleWatch
Here's what you have to do in your app.
Note that everything from steps 2-6 are in this gist: https://gist.github.com/JonMercer/75b3f45cda16ee5d1e0955b2492f66dd
In the project settings in xcode, click on the capabilities tab (it's the tab beside the general tab where you put in your bundle identifier). Then turn on Background Mode and enable Location updates. EDIT thanks to #Rob: And also add NSLocationWhenInUseUsageDescription to your plist. The value can be anything you like
Inside your view controller, extend the CLLocationManagerDelegate
class YourClass: UIViewController, CLLocationManagerDelegate {
Implement these two functions
/**
Increases that location count by the number of locations received by the
manager. Updates the batch count with the added locations.
*/
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//use the "locations" variable here
}
/// Log any errors to the console.
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error occured: \(error.localizedDescription).")
}
Create a let manager = CLLocationManager() variable
On viewDidLoad() set the following: manager.requestWhenInUseAuthorization(), manager.allowsBackgroundLocationUpdates = true, and manager.startUpdatingLocation()
In order to stop tracking do the following: manager.stopUpdatingLocation() and manager.allowsBackgroundLocationUpdates = false

How does one call a function from ViewController in AppDelegate?

I am making an app that uses iBeacon. When the iBeacon is detected (inside AppDelegate), the app detects the phone's coordinates and forwards them to an API.
I have a function called runGPS() set up in ViewController which does the above and am trying to get it to execute when the iBeacon is detected.
When I do ViewController().runGPS(), I get an error: Missing parameter for 'coder' in call
I've looked up this issue and keep going around in circles on resolving it. One correction leads to another error etc. etc. etc.
How does one correct this or is my overall strategy for this wrong?
func locationManager(manager: CLLocationManager,
didEnterRegion region: CLRegion) {
manager.startRangingBeaconsInRegion(region as! CLBeaconRegion)
manager.startUpdatingLocation()
NSLog("You entered the region")
ViewController(NSCoder)!.runGPS()
}
I have also implemented iBeacon in my application. You have to add this location manager delegate in that viewcontroller and set delegate there like
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
and you can check in viewController if any user enter to your beacon region then this delegate method will run
func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
}
you can check your beacon in beacons array received in parameter.
You should separate out the runGPS() code from the ViewController class if its not meant to be there. Is it using any of the ViewController's functionality? If not then you should move it to a separate class meant for passing data to API. If you cannot do that then you can declare runGPS as a static method and called ViewController.runGPS() provided you are not using any of the properties of the ViewController in runGPS() method

Resources