Getting current location using CLLocationManager in Swift - ios

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

Related

how to prevent mock GPS location (spoofing) from fake GPS app in Swift? [duplicate]

This question already has answers here:
iOS detect mock locations
(5 answers)
Closed 4 years ago.
I am making an app that using CLLocationManager, this app will be used to record the attendee of the employee. to validate the attendee of the employees, we will get GPS Coordinate.
as far as I know, in there is an app that usually used to get fake GPS. I want to prevent this mock GPS to be active when the user using my app.
If I am using Android, I can download a fake GPS app. and when let say I use tinder I can fake my location. let say actually I am in Bangkok, but because I use fake GPS app, I can set my tinder location to be in London, not in Bangkok anymore.
So Basically I want to prevent fake location that comes from that fake GPS when the user using my App. To be honest I don't really know whether iOS allow fake location or not
can I get that function in Swift?
here is the class LocationManager I use to get the coordinate
import UIKit
import CoreLocation
class LocationManager: NSObject {
let manager = CLLocationManager()
var didGetLocation: ((Coordinate?) -> Void)?
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestLocation()
}
func getPermission() {
// to ask permission to the user by showing an alert (the alert message is available on info.plist)
if CLLocationManager.authorizationStatus() == .notDetermined {
manager.requestWhenInUseAuthorization()
}
}
}
extension LocationManager : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
manager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
didGetLocation?(nil)
return
}
let coordinate = Coordinate(location: location)
if let didGetLocation = didGetLocation {
didGetLocation(coordinate)
}
}
}
private extension Coordinate {
init(location: CLLocation) {
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
}
}
You can check if the app is jailbroken or not. If it already jailbroken, you can prevent the user to use the app with showing permanent dialog or something else.
If you wanna know how to detect the device is jailbroken or not, you can find it by yourself. There is so many literature that will tell you how.
Cheers :)
Only thing I can tell you and I've been in a similar situation is you need to have a backup method to get the user location. Use an IP location API/service (which is not 100% reliable) and create a logic in your app to compare the data.
PS: THIS IS NOT A SOLUTION TO YOUR PROBLEM it's just an idea you could try to work with. But this would only work best if spoofing the location is happening using different cities/states since IP location is not a high accuracy one. If GPS says you are in San Diego but your IP say you are in San Francisco, then you could block the UI/request until user confirms something.
PS2: in iOS the only way I know a user can spoof it's location is running an app through XCode, using the location feature and then opening your app. (used to do that a lot with pokemon go #notproud :))

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

CLLocationManagerDelegate Method Not Being Called

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!

CLLocationManager didUpdateLocations not being called iOS 8 with plist entries

I'm having some issues with CLLocationManager. This code used to work on iOS 8.2 but since upgrading to 8.3 it doesn't work. Here is the code for setting up the location manager which is called on startup.
let distanceThreshold:CLLocationDistance = 100.0
var currentLocation:CLLocationCoordinate2D?
override init() {
assert(locMan == nil)
super.init()
locMan = self
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
currentLocation = manager.location.coordinate
if UIApplication.sharedApplication().applicationState == .Background {
PlacesManager.fetchNearbyPlaces(LocationManager.getLocationManager().currentLocation!, radius: distanceThreshold, callback: placesCallback)
}
}
With this code didUpdateLocations is never called despite it being called before.
I have added the relevant entries to the Info.plist file:
I have tried it on both a device and the simulator and neither works. In fact it seems that it is no longer requesting location authorisation anymore if I delete the app and reinstall.
I know I'm missing something stupid but I can't workout what the hell it is.
I'd appreciate any help people can provide.
Cheers,
Gerard
Answer from Anna:
The CLLocationManager documentation says: "To configure and use a CLLocationManager object to deliver events...Create an instance of the CLLocationManager class and store a strong reference to it somewhere in your app. Keeping a strong reference to the location manager object is required until all tasks involving that object are complete. Because most location manager tasks run asynchronously, storing your location manager in a local variable is insufficient."
In addition to the accepted answer, if you are using the simulator. You have to select a location after the app is running (Debug->Location->apple for apple headquarters). I set the location and assumed that the next time I ran the app that didUpdateLocations would be called with what I had set previously, but that assumption was wrong.

Unable to get the permission prompt from CLLocationManager

Here is my code from a ViewController implementing CLLocationManagerDelegate:
func startLocationManager() {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
println("I'm called")
locationManager.requestAlwaysAuthorization()
// locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
let status = CLLocationManager.authorizationStatus()
println(status.rawValue) // This print 0 which stands for kCLAuthorizationStatusNotDetermined
println(CLLocationManager.locationServicesEnabled()) // true
}
func locationManager(manager: CLLocationManager!,
didUpdateLocations locations: [AnyObject]!) {
println("nobody call me ever, and I'm sad")
}
For some reason, I never get the prompt / alter to autorise location updates. I have tried on my device iOS 8.1 and the simulartor. I followed the advices found here: requestAlwaysAuthorization not showing permission alert :
"Add Core Location framework to Project Settings / Targets / Capabilities / Background Modes set "Location Updates" and "Uses Bluetooth LE Accessories" Add key at Info.plist NSLocationAlwaysUsageDescription".
I have also tried to clean up and rebuild, nothing change. I feel clueless.
EDIT: This question seems related: iOS: App is not asking user's permission while installing the app. getting kCLAuthorizationStatusNotDetermined every time - Objective-c & Swift but the selected answer and its article doesn't expose anything new
Your CLLocationManager object is local object and thus will be deallocated immediately after it falls out of scope. Make it a class property and then asynchronous processes like requesting authorization and determining the location will have a chance to run.

Resources