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")
}
}
}
Related
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.
This code has always worked reliably but lately (at least on my Watch) it's always returning New York, New York no matter where I am? Did something change in Core Location? 🤔
import CoreLocation
class WorkoutLocationManager: NSObject, CLLocationManagerDelegate {
private var locationManager: CLLocationManager?
public var formattedWorkoutAddress: String?
public func getWorkoutLocation() {
guard CLLocationManager.locationServicesEnabled() else {
print("User does not have location services enabled")
return
}
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
let locationAuthorizationStatus = CLLocationManager.authorizationStatus()
switch locationAuthorizationStatus {
case .authorizedAlways:
print("location authorized Always")
locationManager?.requestLocation()
case .authorizedWhenInUse:
print("location authorized When in Use")
locationManager?.requestLocation()
case .denied:
print("location authorization denied")
case .notDetermined:
print("location authorization not determined")
case .restricted:
print("location authorization restricted")
default: ()
}
}
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.first else { return }
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(currentLocation) { (placemarksArray, error) in
if let unwrappedError = error {
print("Geocoder error: \(unwrappedError)")
}
guard let placemarksArrayUnwrapped = placemarksArray else { return }
if placemarksArrayUnwrapped.count > 0 {
if let placemark = placemarksArray?.first {
let locality = placemark.locality ?? ""
let state = placemark.administrativeArea ?? ""
let workoutLocationAsString = (locality + " " + state)
print("workoutLocationAsString = \(workoutLocationAsString)")
self.formattedWorkoutAddress = workoutLocationAsString
} else { print("no placemark")}
} else { print("placemark.count = 0")}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("location manager error = \(error)")
}
//I added this code below to prevent getting the error "Failure to deallocate CLLocationManager on the same runloop as its creation may result in a crash" code is from this answer: https://stackoverflow.com/questions/52304969/failure-to-deallocate-cllocationmanager-on-the-same-runloop-as-its-creation-may?noredirect=1#comment95470009_52304969
override init() {
super.init()
self.performSelector(onMainThread: #selector(initLocationManager), with: nil, waitUntilDone: true)
}
#objc private func initLocationManager() {
self.locationManager = CLLocationManager()
self.locationManager?.delegate = self
}
#objc private func deinitLocationManager() {
locationManager = nil
}
deinit {
self.performSelector(onMainThread: #selector(deinitLocationManager), with: nil, waitUntilDone: true)
}
}
I finally figured it out, I did have a default location of NY/NY set in my Scheme...I must have done it a long time ago and forgot 🤦♂️
I've built an application where you can press a start button. Once the button is pressed the application will get user location every 10 second all the way till the stop button is pressed. When I leave the application or if the screen gets black, it will NOT get any more locations till I re-enter the application.
So, I'm currently trying to update the locations when the application is minimized. (I guess it's called in the background?), and also when the screen turns black. But my questions is:
Should I write this code in the AppDelegate?, if so. How can I know
if the button was pressed or not?
Where exactly in the AppDelegate should I add the code? And how can
I pass the locations back to the correct ViewController? (Since I
cannot make any prepare for segue from AppDelegate)
If you know the answers of this questions, please do not hesitate to answer them. :) I would really appreciate it!
The best way to get user's location in background is to use the Significant-Change Location Service according to apple's documentation put this func in your class:
func startReceivingSignificantLocationChanges() {
let authorizationStatus = CLLocationManager.authorizationStatus()
if authorizationStatus != .authorizedAlways {
// User has not authorized access to location information.
return
}
if !CLLocationManager.significantLocationChangeMonitoringAvailable() {
// The service is not available.
return
}
locationManager.delegate = self
locationManager.startMonitoringSignificantLocationChanges()
}
and also this func:
func locationManager(_ manager: CLLocationManager, didUpdateLocations
locations: [CLLocation]) {
let lastLocation = locations.last!
// Do something with the location.
}
so you just need to call startReceivingSignificantLocationChanges() inside your button and it will call locationManager(_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]), so do what you want with the location there.
Remember to ask permission to use location and to stop tracking with locationManager.stopMonitoringSignificantLocationChanges()
Take location permission for Always Allow
Set location manager for allowsBackgroundLocationUpdates true
from the above way you can get location in every location changes store this information and it send to server. Below is the sample code
typealias LocateMeCallback = (_ location: CLLocation?) -> Void
/*
LocationTracker to track the user in while navigating from one place to other and store new locations in locations array.
**/
class LocationTracker: NSObject {
static let shared = LocationTracker()
var lastLocation: CLLocation?
var locations: [CLLocation] = []
var previousLocation: CLLocation?
var isPreviousIsSameAsCurrent: Bool {
if let previous = previousLocation, let last = lastLocation {
return previous == last
}
return false
}
var isAggressiveModeOn = false
var locationManager: CLLocationManager = {
let locationManager = CLLocationManager()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.activityType = .automotiveNavigation
return locationManager
}()
var locateMeCallback: LocateMeCallback?
var isCurrentLocationAvailable: Bool {
if lastLocation != nil {
return true
}
return false
}
func enableLocationServices() {
locationManager.delegate = self
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
case .restricted, .denied:
// Disable location features
print("Fail permission to get current location of user")
case .authorizedWhenInUse:
// Enable basic location features
enableMyWhenInUseFeatures()
case .authorizedAlways:
// Enable any of your app's location features
enableMyAlwaysFeatures()
}
}
func enableMyWhenInUseFeatures() {
locationManager.startUpdatingLocation()
locationManager.delegate = self
escalateLocationServiceAuthorization()
}
func escalateLocationServiceAuthorization() {
// Escalate only when the authorization is set to when-in-use
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.requestAlwaysAuthorization()
}
}
func enableMyAlwaysFeatures() {
enableCoarseLocationFetch()
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
// Enable Rough Location Fetch
func enableCoarseLocationFetch() {
isAggressiveModeOn = false
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
locationManager.distanceFilter = 100
}
// Enable Aggressive Location Fetch
func enableAggressiveLocationFetch() {
isAggressiveModeOn = true
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.distanceFilter = 10
}
func locateMe(callback: #escaping LocateMeCallback) {
self.locateMeCallback = callback
if lastLocation == nil {
enableLocationServices()
} else {
callback(lastLocation)
}
}
func startTracking() {
enableLocationServices()
}
func stopTracking() {
locationManager.stopUpdatingLocation()
}
func resetPreviousLocation() {
previousLocation = nil
}
private override init() {}
}
extension LocationTracker: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
guard let location = locations.first else { return }
guard -location.timestamp.timeIntervalSinceNow < 120, // Validate only location fetched recently
location.horizontalAccuracy > 0, // Validate Horizontal Accuracy - Ve means Invalid
location.horizontalAccuracy < 200 // Validate Horizontal Accuracy > 100 M
else {
print("invalid location received OR ignore old (cached) updates")
return
}
self.locations.append(location)
lastLocation = location
if let activeRide = RideManager.shared.activeRide,
let _ = AccessTokenHelper.shared.accessToken,
let activeRideId = activeRide.ride_id,
let type = activeRide.rideStatusTypeOptional,
type == .started {
//Store Location For A particular Ride after Start
LocationUpdater.shared.saveInDataBase(rideId: activeRideId, locations: [location])
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
enableLocationServices()
}
}
/*
This class having responsibility of Updating the location on server after n second and update path after n second.
**/
class LocationTimer {
static let time: Double = 30
}
/*
class to update locations to server after nth second
**/
class LocationUpdater: NSObject {
static let shared = LocationUpdater(n: Double(LocationTimer.time), tracker: LocationTracker.shared)
let n: Double
private let tracker: LocationTracker
var timer: Timer! = nil
init(n: Double, tracker: LocationTracker) {
self.n = n
self.tracker = tracker
super.init()
}
func startUpdater() {
self.timer?.invalidate()
self.timer = nil
self.timer = Timer.scheduledTimer(timeInterval: n, target: self, selector: #selector(updateLocationsToServer), userInfo: nil, repeats: true)
self.timer.fire()
}
func stopUpdater() {
self.timer?.invalidate()
self.timer = nil
}
#objc func updateLocationsToServer() {
// update to server
}
}
// usage
LocationTracker.shared.startTracking()
LocationUpdater.shared.startUpdater()
This is a really basic outlay of what I am using...
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
// Call the locationManager class
let LocationManager = CLLocationManager()
// CoreData Delegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// Conform to Delegate Method
self.LocationManager.delegate = self
// Set required accuracy
self.LocationManager.desiredAccuracy = kCLLocationAccuracyBest
// Blue dot
self.mapView.showsUserLocation = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// check location services active
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// check location services
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways:
self.LocationManager.startUpdatingLocation()
case .notDetermined:
self.LocationManager.requestAlwaysAuthorization()
case .authorizedWhenInUse, .restricted, .denied:
let alertController = UIAlertController(
title: "Background Location Access Disabled",
message: "In order to work your location settings need to be set to 'Always'.",
preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
let openAction = UIAlertAction(title: "Open Settings", style: .default) { (action) in
if let url = NSURL(string:UIApplicationOpenSettingsURLString) {
UIApplication.shared.open(url as URL)
}
}
alertController.addAction(openAction)
self.present(alertController, animated: true, completion: nil)
}
}
// Location delegate methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
// get last location
let location = locations.last
print(location!.coordinate.latitude)
// set region
let region = MKCoordinateRegion(center: location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
// deploy region to map
self.mapView.setRegion(region, animated: true)
// Map to follow the user
self.mapView.setUserTrackingMode(MKUserTrackingMode.follow, animated: true)
// Show compass on map
self.mapView.showsCompass = true
// save the location data to CoreData
//self.save(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
// end Location updating
self.LocationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Errors: " + error.localizedDescription)
}
}
My issue is that func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation] calls itself over and over again (around 3 times on initial load)...
I am using the .last which AFAIK is meant to pull out the last result in that object.. which it probably is, as with breakpoints inserted it after the first 2 prints it only returns 1 lot of results...
After searching high and low, I am hoping I can get a result by asking the question... Thanks!
Console output of my issue:
When you call startUpdatingLocation() the location manager immediately starts the delivering of location data. The first incoming locations may be way off your actual location, so check the horizontalAccuracy and verticalAccuracy attributes and dismiss locations which are too inaccurate.
It looks like you just want to get a one-shot location, if so try this code:
// Use:
// at class level:
// var manager: LocationOneShotManager?
// in viewDidLoad:
// manager = LocationOneShotManager()
// manager!.fetchWithCompletion {location, error in
// // fetch location or an error
// if let loc = location {
// println(location)
// } else if let err = error {
// println(err.localizedDescription)
// }
// self.manager = nil
// }
import UIKit
import CoreLocation
// possible errors
enum OneShotLocationManagerErrors: Int {
case AuthorizationDenied
case AuthorizationNotDetermined
case InvalidLocation
}
class LocationOneShotManager: 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: [CLLocation]) {
let location = locations[0]
_didComplete(location, error: 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")
}
}
}
I need stop locationUpdates from AppDelegate when applicationDidEnterBackground , and startUpdatingLocation when applicationDidBecomeActive from ViewController, here is my Code..
How can do it if my locationManager is in the ViewController.
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate
var seenError : Bool = false
var locationFixAchieved : Bool = false
var locationStatus : NSString = "Not Started"
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
}
func initLocationManager() {
seenError = false
locationFixAchieved = false
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.locationServicesEnabled
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
}
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)")
}
}
NSNotificationCenter is a simple way to send messages within your application. Each class which needs to respond to such messages registers an observer to listen for the message.
AppDelegate:
func applicationDidEnterBackground(application: UIApplication) {
// Send a message that informs all listening classes for entering background
NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "appEntersBackground", object: nil))
}
func applicationDidBecomeActive(application: UIApplication) {
// Send a message that informs all listening classes for becoming active again
NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "appBecomesActive", object: nil))
}
Swift 4.2
NotificationCenter.default.post(name: Notification.Name("appEntersBackground"), object: nil)
ViewController
In your viewController, register observers for the messages:
class ViewController: UIViewController, CLLocationManagerDelegate {
...
override func viewDidLoad() {
super.viewDidLoad()
// register observers
NSNotificationCenter.defaultCenter().addObserver(self, selector: "enterBackground", name: "appEntersBackground", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "becomeActive", name: "appBecomesActive", object: nil)
}
deinit {
// remove observers
NSNotificationCenter.defaultCenter().removeObserver(self)
}
// app enters background
func enterBackground() {
self.locationManager.stopUpdatingLocation()
}
// app becomes active
func becomeActive() {
self.locationManager.startUpdatingLocation()
}
}
Swift 4.2
NotificationCenter.default.addObserver(self, selector: #selector(enterBackground), name: Notification.Name("appEntersBackground"), object: nil)
#objc func enterBackground() {
self.locationManager.stopUpdatingLocation()
}
Note that the locationmanager stops updating locations in background unless you have registered your app for background tasks in Info.plist.
You can fire notifications startUpdatingLocation from applicationDidBecomeActive and stopUpdatingLocation from applicationDidEnterBackground. Then you can listen to these notifications in your view controller to manage locationManager.
You can use this code-
func applicationDidEnterBackground(application: UIApplication) {
self.locationManager.stopUpdatingLocation()
}
func applicationDidBecomeActive(application: UIApplication) {
self.locationManager.startUpdatingLocation()
}
One more suggestion, you can put a check in your initLocationManager method -
if (self.locationManager.respondsToSelector(Selector("requestAlwaysAuthorization"))) {
self.locationManager.requestAlwaysAuthorization()
}