Geofencing stops working after 7-8 hours. It notifies only when I open the app - ios

I have implemented geofencing to detect the entry and exit from the region. It seems working fine when the app is in a foreground/background/terminated state initially. I am testing this functionality using GPX. When an app is terminated, I am getting entry exit notifications too. But I have observed that in many scenarios when an app is suspended or terminated for a longer period of time, Even though the user is entering and leaving the region, No notifications are triggered. When I open the app manually, I can see the entry,or exit notifications instantly.
Here is my code snippet.
class LocationService: NSObject, CLLocationManagerDelegate {
static let sharedInstance: LocationService = { LocationService()
}()
var locationManager: CLLocationManager?
var startLocation: CLLocation?
var lastLocation: CLLocation?
var delegate: LocationServiceDelegate?
var isAuthorized: ((Bool) -> Void)?
var boolSendUpdate = false
var locationTimer = Timer()
var isFirstTime:Bool!
override init() {
super.init()
self.locationManager = CLLocationManager()
guard let locationManager = self.locationManager else {
return
}
//locationManager.desiredAccuracy = kCLLocationAccuracyBest // The accuracy of the location data
locationManager.delegate = self
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.allowsBackgroundLocationUpdates = true// if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self){
// print("Available")
// }
//
NotificationCenter.default.addObserver(self, selector:#selector(startUpdatingLocation), name: UIApplication.didBecomeActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(stopUpdatingLocation), name: UIApplication.willTerminateNotification, object: nil)
}
func checkPermission(isAuthorized: ((Bool) -> Void)? = nil) {
guard let locationManager = self.locationManager else {
return
}
switch(CLLocationManager.authorizationStatus()) {
case .authorizedAlways,.authorizedWhenInUse:
self.startUpdatingLocation()
isAuthorized?(true)
// get the user location
case .restricted, .denied:
isAuthorized?(false)
// redirect the users to settings
popupAlert(title: NSLocalizedString("settings", comment: ""), message:go_to_settings, actionTitles: [NSLocalizedString("Cancel", comment: ""),NSLocalizedString("Settings", comment: "")], actions:[{action1 in
},{action2 in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in })
}
}, nil])
case .notDetermined:
isAuthorized?(false)
locationManager.requestWhenInUseAuthorization()
#unknown default:
isAuthorized?(false)
locationManager.requestWhenInUseAuthorization()
}
}
#objc func startUpdatingLocation() {
self.locationManager?.startUpdatingLocation()
self.locationManager?.requestAlwaysAuthorization()
}
#objc func stopUpdatingLocation() {
if !CLLocationManager.significantLocationChangeMonitoringAvailable() {
return
}
self.locationManager?.stopUpdatingLocation()
self.locationManager?.startMonitoringSignificantLocationChanges()
}
func setUpGeofenceing(location:CLLocation,identifier:String = "",radius:CLLocationDistance,status:enumShiftStatus) {
let geofenceRegionCenter = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let geofenceRegion = CLCircularRegion.init(center: geofenceRegionCenter, radius: radius, identifier: identifier)
geofenceRegion.notifyOnExit = true
geofenceRegion.notifyOnEntry = true
if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
print("Geofencing is not supported on this device!")
UIApplication.shared.windows.first?.rootViewController?.presentAlert(withTitle:"MetroOne Officer", message: "Geofencing is not supported on this device!")
return
}
if locationManager?.monitoredRegions.contains(geofenceRegion) == false {
locationManager?.startMonitoring(for: geofenceRegion)
}
}
func stopGeoFenceing(identifier: String = ""){
}
// CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard let delegate = self.delegate else {
return
}
delegate.didChangeAuthorization(status: status)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
// singleton for get last location
self.lastLocation = location
// use for real time update location
updateLocation(currentLocation: location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// do on error
updateLocationDidFailWithError(error: error as NSError)
}
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
debugPrint("Started Monitoring for Region:::",region.description)
guard #available(iOS 14, *) else {
self.locationManager?.requestState(for:region)
return
}
}
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
debugPrint("error::\(error.localizedDescription)")
}
// Private function
private func updateLocation(currentLocation: CLLocation){
guard let delegate = self.delegate else {
return
}
delegate.tracingLocation(currentLocation: currentLocation)
}
private func updateLocationDidFailWithError(error: NSError) {
guard let delegate = self.delegate else {
return
}
delegate.tracingLocationDidFailWithError(error: error)
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
switch state {
case .inside:
postApiGeoFenceEntries(type: RegionType.In.rawValue, shiftStatus: enumShiftPostStatus.CheckIn.rawValue)
case .outside:
postApiGeoFenceEntries(type: RegionType.Out.rawValue, shiftStatus: enumShiftPostStatus.CheckOut.rawValue)
case .unknown:
print("Unknown")
default:
print("Default")
}
}
}
App delegate code snippet.
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch
if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
_ = LocationService.sharedInstance
UNUserNotificationCenter.current().delegate = self
}
return true
}
}
I have also enabled location updates in capabilities. Please let me know if I am missing something.
Thank you.

Related

Sending location to server in background every x minutes

I would like to send the device's location to a server every x minutes, even if the location does not change, while the app is in the background. (most optimal timeframe would be like 1.5 - 2 hours)
Currently it gets terminated after 30 sec.
I have seen some articles about adding these to the AppDelegate's didFinishLaunchingWithOptions method:
application.setMinimumBackgroundFetchInterval(1800)
UIApplication.shared.setMinimumBackgroundFetchInterval(1800)
but it got deprecated in iOS 13.
The AppDelegate looks like this:
import UIKit
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
var backgroundTaskTimer: Timer! = Timer()
var locationManager: LocationManager!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
locationManager = LocationManager()
let coordinator = AppCoordinator(window: window)
coordinator.start()
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
doBackgroundTask()
}
func applicationWillEnterForeground(_ application: UIApplication) {
if backgroundTaskTimer != nil {
backgroundTaskTimer.invalidate()
backgroundTaskTimer = nil
}
}
#objc func startTracking() {
self.locationManager.sendLocation()
}
func doBackgroundTask() {
DispatchQueue.global(qos: .default).async {
self.beginBackgroundTask()
if self.backgroundTaskTimer != nil {
self.backgroundTaskTimer.invalidate()
self.backgroundTaskTimer = nil
}
//Making the app to run in background forever by calling the API
self.backgroundTaskTimer = Timer.scheduledTimer(timeInterval: AppEnvironment.backgroundTimeInterval,
target: self, selector: #selector(self.startTracking),
userInfo: nil, repeats: true)
RunLoop.current.add(self.backgroundTaskTimer, forMode: RunLoop.Mode.default)
RunLoop.current.run()
// End the background task.
self.endBackgroundTask()
}
}
func beginBackgroundTask() {
backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(withName: "Track trip", expirationHandler: {
self.endBackgroundTask()
})
}
func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundUpdateTask)
backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
}
}
The LocationManager looks like this:
import Foundation
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
private let locationService: LocationServiceProtocol
private let manager = CLLocationManager()
private var lastLocation: Coordinate?
private var offlineCoordinates: [Coordinate] = []
init(locationService: LocationServiceProtocol = LocationService()) {
self.locationService = locationService
}
func sendLocation() {
manager.delegate = self
manager.requestLocation()
}
private func handleLocation() {
guard let coordinate = lastLocation else { return }
guard Connectivity.isConnectedToTheInternet else {
offlineCoordinates.append(coordinate)
return
}
if !offlineCoordinates.isEmpty {
offlineCoordinates.forEach { postCoordinate($0) }
offlineCoordinates.removeAll()
}
postCoordinate(coordinate)
}
private func postCoordinate(_ coordinate: Coordinate) {
locationService.sendLocation(coordinate) { [weak self] result in
guard self != nil else { return }
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(_):
print("Location sent.")
}
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
manager.stopUpdatingLocation()
manager.delegate = nil
guard let location = locations.first else { return }
let latitude = location.coordinate.latitude
let longitude = location.coordinate.longitude
print("\tLatitude: \(latitude), Longitude: \(longitude)")
lastLocation = Coordinate(lat: latitude, lon: longitude,
supervisorNfc: GlobalAppStorage.shared.defaults.string(forKey: Constants.StorageKeys.supervisorNfc) ?? "")
handleLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
In Background Modes the following are enabled:
• Location updates
• Background fetch
• Background processing
In Info.plist I have set the following properties:
• Privacy - Location Usage Description
• Privacy - Location When In Use Usage Description
• Privacy - Location Always and When In Use Usage Description
• and the above mentioned under the Required background modes.
Thanks in advance for your help!

How to return the value of CLLocationManager didUpdateLocations in function?

The problem iam having is that when i call the getCityName() function in other classes it returns nil because didUpdateLocations needs more time to run, how to solve this Async problem? and How to return the value of CLLocationManager didUpdateLocations in function?
import Foundation
import CoreLocation
class userCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var cityName:String!
init(cityName:String) {
super.init()
setUp()
}
func getCityName() -> String?{
return cityName
}
func setUp() {
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""
manager.stopUpdatingLocation()
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
extension CLLocation {
func geocode(completion: #escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
class userCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var cityName:String!
//1. Add a closure callback
var didGetCity: ((String) -> Void)?
//2. Send City Name when received
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""
// Check for null
// Send city name in the completion block
if (self.didGetCity != nil){
self.didGetCity?(self.cityName)
}
manager.stopUpdatingLocation()
}
}
}
//3. Add code to your class where you need the city Name
var City : String?
let locationManager = userCurrentLocation (cityName: "")
locationManager.didGetCity = {
[weak self] city in
self!.City = city
}
You can use Delegate to tell the main class that everything is ready and use that value
import Foundation
import CoreLocation
protocol userCurrentLocationDelegate: class {
func userCurrentLocationDidGetLocation(_ userLocation: userCurrentLocation)
}
class userCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var cityName:String!
weak var delegate: userCurrentLocationDelegate?
init(cityName:String) {
super.init()
setUp()
}
func getCityName() -> String?{
return cityName
}
func setUp() {
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""
manager.stopUpdatingLocation()
delegate?.userCurrentLocationDidGetLocation(self)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
extension CLLocation {
func geocode(completion: #escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
and in Main class that calls this one use delegate from this callback to start calling the city name

Get User Location Every 5 minutes with background modes in Swift 3

I want to ;
Get User Location Every 5 minutes with background modes in Swift 3
But my codes don't any action. I need to get and send server ,longitude , latitude , and altitude values every 5 minutes
My codes under below.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
sleep(2)
BackgroundLocationManager.instance.start()
return true
}
BackgroundLocationManager - Class
import Foundation
import CoreLocation
import UIKit
class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {
static let instance = BackgroundLocationManager()
static let BACKGROUND_TIMER = 15.0 // restart location manager every 150 seconds
static let UPDATE_SERVER_INTERVAL = 60 * 5 // 5 minutes server send
let locationManager = CLLocationManager()
var timer:Timer?
var currentBgTaskId : UIBackgroundTaskIdentifier?
var lastLocationDate : NSDate = NSDate()
private override init(){
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
locationManager.activityType = .other;
locationManager.distanceFilter = kCLDistanceFilterNone;
if #available(iOS 9, *){
locationManager.allowsBackgroundLocationUpdates = true
}
NotificationCenter.default.addObserver(self, selector: #selector(self.applicationEnterBackground), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
}
func applicationEnterBackground(){
// FileLogger.log("applicationEnterBackground")
start()
}
func start(){
if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways){
if #available(iOS 9, *){
locationManager.requestLocation()
} else {
locationManager.startUpdatingLocation()
}
} else {
locationManager.requestAlwaysAuthorization()
}
}
func restart (){
timer?.invalidate()
timer = nil
start()
}
private func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case CLAuthorizationStatus.restricted: break
//log("Restricted Access to location")
case CLAuthorizationStatus.denied: break
//log("User denied access to location")
case CLAuthorizationStatus.notDetermined: break
//log("Status not determined")
default:
//log("startUpdatintLocation")
if #available(iOS 9, *){
locationManager.requestLocation()
} else {
locationManager.startUpdatingLocation()
}
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if(timer==nil){
// The locations array is sorted in chronologically ascending order, so the
// last element is the most recent
guard locations.last != nil else {return}
beginNewBackgroundTask()
locationManager.stopUpdatingLocation()
let now = NSDate()
if(isItTime(now: now)){
//TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
beginNewBackgroundTask()
locationManager.stopUpdatingLocation()
}
func isItTime(now:NSDate) -> Bool {
let timePast = now.timeIntervalSince(lastLocationDate as Date)
let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
return intervalExceeded;
}
func sendLocationToServer(location:CLLocation, now:NSDate){
//TODO
}
func beginNewBackgroundTask(){
var previousTaskId = currentBgTaskId;
currentBgTaskId = UIApplication.shared.beginBackgroundTask(expirationHandler: {
// FileLogger.log("task expired: ")
})
if let taskId = previousTaskId{
UIApplication.shared.endBackgroundTask(taskId)
previousTaskId = UIBackgroundTaskInvalid
}
timer = Timer.scheduledTimer(timeInterval: BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
}
}

ApplicationdidEnterBackground swift

here is my class OneShotLocationManager that gives the location for the user
when calling it from the ViewController it is working fine but when calling it from the ApplicationdidEnterBackground it is not working but but displaying each 0.4 UpdateLocation in the console !
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
println("entered background")
var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("updateLocation"), userInfo: nil, repeats: true)
}
func updateLocation(){
// start location services, here
println("updateLocation")
var manager: OneShotLocationManager?
manager = OneShotLocationManager()
manager!.fetchWithCompletion {location, error in
// fetch location or an error
if let loc = location {
} else if let err = error {
println(err.localizedDescription)
}
println("end up")
manager = nil
}
}
override func viewDidLoad() {
super.viewDidLoad()
ViewController.UpdateLocation()
}
class func UpdateLocation(){
var manager: OneShotLocationManager?
manager = OneShotLocationManager()
manager!.fetchWithCompletion {location, error in
// fetch location or an error
if let loc = location {
println(loc.description)
} else if let err = error {
println(err.localizedDescription)
}
manager = nil
}
}
class OneShotLocationManager: NSObject, CLLocationManagerDelegate {
//location manager
private var locationManager: CLLocationManager?
//destroy the manager
deinit {
locationManager?.delegate = nil
locationManager = nil
}
typealias LocationClosure = ((location: CLLocation?, error: NSError?)->())
private var didComplete: LocationClosure?
//location manager returned, call didcomplete closure
private func _didComplete(location: CLLocation?, error: NSError?) {
locationManager?.stopUpdatingLocation()
didComplete?(location: location, error: error)
locationManager?.delegate = nil
locationManager = nil
}
//location authorization status changed
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .AuthorizedWhenInUse:
self.locationManager!.startUpdatingLocation()
case .Denied:
_didComplete(nil, error: NSError(domain: self.classForCoder.description(),
code: OneShotLocationManagerErrors.AuthorizationDenied.rawValue,
userInfo: nil))
default:
break
}
}
internal func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
_didComplete(nil, error: error)
}
internal func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
if let location = locations[0] as? CLLocation {
_didComplete(location, error: nil)
} else {
_didComplete(nil, error: NSError(domain: self.classForCoder.description(),
code: OneShotLocationManagerErrors.InvalidLocation.rawValue,
userInfo: nil))
}
}
//ask for location permissions, fetch 1 location, and return
func fetchWithCompletion(completion: LocationClosure) {
//store the completion closure
didComplete = completion
//fire the location manager
locationManager = CLLocationManager()
locationManager!.delegate = self
//check for description key and ask permissions
if (NSBundle.mainBundle().objectForInfoDictionaryKey("NSLocationWhenInUseUsageDescription") != nil) {
locationManager!.requestWhenInUseAuthorization()
} else if (NSBundle.mainBundle().objectForInfoDictionaryKey("NSLocationAlwaysUsageDescription") != nil) {
locationManager!.requestAlwaysAuthorization()
} else {
fatalError("To use location in iOS8 you need to define either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in the app bundle's Info.plist file")
}
}
}

How to get Location user with CLLocationManager in swift?

I have this code on my view controller but this not working:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var location: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
location=CLLocationManager()
location.delegate = self
location.desiredAccuracy=kCLLocationAccuracyBest
location.startUpdatingLocation()
}
func locationManager(location:CLLocationManager, didUpdateLocations locations:AnyObject[]) {
println("locations = \(locations)")
label1.text = "success"
}
I have the permissions how I read in other post. but I don't obtain never, no println..
Thanks!!
first add this two line in plist file
1) NSLocationWhenInUseUsageDescription
2) NSLocationAlwaysUsageDescription
Then this is class working complete implement this
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var locationManager: CLLocationManager!
var seenError : Bool = false
var locationFixAchieved : Bool = false
var locationStatus : NSString = "Not Started"
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
initLocationManager();
return true
}
// Location Manager helper stuff
func initLocationManager() {
seenError = false
locationFixAchieved = false
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.locationServicesEnabled
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
}
// Location Manager Delegate stuff
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
if (error) {
if (seenError == false) {
seenError = true
print(error)
}
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: AnyObject[]!) {
if (locationFixAchieved == false) {
locationFixAchieved = true
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
println(coord.latitude)
println(coord.longitude)
}
}
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.Denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldIAllow = true
}
NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
if (shouldIAllow == true) {
NSLog("Location to Allowed")
// Start location services
locationManager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
}
Following are the simple steps for getting user location in Swift 3
1) First add this line in plist file with description
NSLocationWhenInUseUsageDescription
2) Add CoreLocation.framework in your project(Under section Build Phases-> Link Binary With Library)
3) In AppDelegate class
import CoreLocation
4) Create locationManager Object as follows
var locationManager:CLLocationManager!
5) Write following code in didFinishLaunchingWithOptions
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 200
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
6) Confirm CLLocationManagerDelegate delegate like as follows
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate
7) Write CLLocationManagerDelegate delegate method for getting user location
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("location error is = \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = (manager.location?.coordinate)!
print("Current Locations = \(locValue.latitude) \(locValue.longitude)")
}
Since you're declaring location as an explicitly unwrapped optional (CLLocationManager!) it requires an initializer, either in an init method as suggested by jhurray, or just inline, as:
var location: CLLocationManager! = nil
Note that you've got other possible problems as well, including that iOS 8 has new requirements for querying the user for permission to use CoreLocation. See this question for more information.
This is the same code as above but cleaned up to work with Swift as of the date of this posting. This worked for me.
Kudos to the original poster.
(note, stick this into whatever class you will use to handle your location stuff.)
var lastLocation = CLLocation()
var locationAuthorizationStatus:CLAuthorizationStatus!
var window: UIWindow?
var locationManager: CLLocationManager!
var seenError : Bool = false
var locationFixAchieved : Bool = false
var locationStatus : NSString = "Not Started"
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.initLocationManager()
}
// Location Manager helper stuff
func initLocationManager() {
seenError = false
locationFixAchieved = false
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
}
// Location Manager Delegate stuff
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
if ((error) != nil) {
if (seenError == false) {
seenError = true
print(error)
}
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
if (locationFixAchieved == false) {
locationFixAchieved = true
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
println(coord.latitude)
println(coord.longitude)
}
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.Denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldIAllow = true
}
NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
if (shouldIAllow == true) {
NSLog("Location to Allowed")
// Start location services
locationManager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
Do following stuff in viewcontroller [Using swift] -
class ViewController:
UIViewController,MKMapViewDelegate,CLLocationManagerDelegate {
var locationManager: CLLocationManager?
var usersCurrentLocation:CLLocationCoordinate2D?
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager = CLLocationManager()
if CLLocationManager.authorizationStatus() == .NotDetermined{
locationManager?.requestAlwaysAuthorization()
}
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
locationManager?.distanceFilter = 200
locationManager?.delegate = self
startUpdatingLocation()
usersCurrentLocation = CLLocationCoordinate2DMake(LATTITUDE, LONGITUDE)
let span = MKCoordinateSpanMake(0.005, 0.005)
let region = MKCoordinateRegionMake(usersCurrentLocation!, span)
mapview.setRegion(region, animated: true)
mapview.delegate = self
mapview.showsUserLocation = true
}
//MARK: CLLocationManagerDelegate methods
func startUpdatingLocation() {
self.locationManager?.startUpdatingLocation()
}
func stopUpdatingLocation() {
self.locationManager?.stopUpdatingLocation()
}
// MARK: MKMapViewDelegate
func mapView(mapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation){
mapview.centerCoordinate = userLocation.location!.coordinate
mapview.showsUserLocation = true
regionWithGeofencing()
}
You need to have init functions.
Override init(coder:) and init(nibName: bundle:) and add any custom init you want.
Because you have said that location is not optional, you must initialize it before your super init calls in ALL of your init functions.
func init() {
...
location = CLLocationManager()
// either set delegate and other stuff here or in viewDidLoad
super.init(nibName:nil, bundle:nil)
// other initialization below
}
It should be written as
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

Resources