My iOS application does not update the location in background - ios

I have an iOS application developed in Swift. My app is currently in Swift 2 but I am using Xcode 8 with Swift 3. The app is configured to use the legacy swift language version.
Until recently the app was working correctly.
The app asks for the correct rights for always use the location and the autorisation is correctly set to always.
I renewed the signing identity for the production app and the app stopped to be notified on a location update but was still working in development mode (launched from xcode).
Now I revoked and renew the production and development certificate and the app does not update the location while in background whereas the autorisation is set to always.
The app is correctly installed so I guess that the certificates are okay but I don't understand why the location is not updated in background.
I run the app on an iPhone 7 with IOS 10.2 and xcode automatically manage signing.
Here is my location manager configuration:
public class LocationManager : NSObject, ModuleManager, CLLocationManagerDelegate {
/// The core location manager
let coreLocationManager: CLLocationManager
public var datas: JSONable? {
get {
return LocationDatas(locations: self.locations)
}
set {
self.locations = newValue == nil ? [Location]() : newValue as? [Location]
}
}
/// The list of locations to send
private var locations: [Location]?
/// The last location
public var lastLocation: Location? {
return self.locations?.last
}
public override init() {
self.coreLocationManager = CLLocationManager()
if #available(iOS 9.0, *) {
self.coreLocationManager.allowsBackgroundLocationUpdates = true
}
// The accuracy of the location data.
self.coreLocationManager.desiredAccuracy = kCLLocationAccuracyBest;
// The minimum distance (measured in meters) a device must move horizontally before an update event is generated.
self.coreLocationManager.distanceFilter = 500; // meters
self.locations = [Location]()
super.init()
self.coreLocationManager.delegate = self
self.locationManager(self.coreLocationManager, didChangeAuthorizationStatus: CLLocationManager.authorizationStatus())
}
// MARK: - CLLocationManager Delegate
public func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
NSLog("location update")
guard locations.count > 0 else {
NSLog("Module Location -- no location available")
return
}
// Add all location waiting in the list to send
self.locations?.appendContentsOf(locations.map { Location(cllocation: $0) })
SDKManager.manager?.sendHeartbeat()
}
public func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch CLLocationManager.authorizationStatus() {
case .NotDetermined:
if #available(iOS 8.0, *) {
self.coreLocationManager.requestAlwaysAuthorization()
} else {
self.coreLocationManager.startUpdatingLocation()
}
case .Denied, .Restricted:
NSLog("Module Location -- access denied to use the location")
case .AuthorizedAlways:
NSLog("AuthorizedAlways")
self.coreLocationManager.startUpdatingLocation()
//self.coreLocationManager.startMonitoringSignificantLocationChanges()
default:
break
}
}
public func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
NSLog("Module Location -- error : \(error)")
}
}
The locationManager function is not called in background.
Here is my info.plist:
Here is the authorization on the phone:
The little location arrow is always there but no location update is logged.

I checked your code and it seems to be fine, revise if you have done these required settings
Enable location updates in Background mode
Add NSLocationAlwaysUsageDescription in your info.plist
If you did not do 1st point you app would have crashed but if did not do 2nd point your code will go through but you will never get updates.
Update:
It seems your LocationManager object is released in ARC. Can you try changing your LocationManager class to Singleton by added
static let sharedInstance = LocationManager()
And accessing LocationManager in your code like this
LocationManager.sharedInstance

You don't need to use App background Refresh just for Location update in Background. (It can be used for other maintenance work like DB cleaning, uploading, downloading, etc. while charging)
While initializing coreLocationManager, set the following properties as well
// It will allow app running location updates in background state
coreLocationManager.allowsBackgroundLocationUpdates = true
// It will not pause location automatically, you can set it true if you require it.
coreLocationManager.pausesLocationUpdatesAutomatically = false

Related

iOS - Check if Developer Mode is ON (Swift)

My App relies on checking user location upon start.
On iOS 16, when the user enables the Developer Mode on Privacy Settings they can simulate any location on the device using a GPX file.
The device actually changes the location of the device and most apps that I use actually think my location is the one on the GPX file.
Is there a way to make sure the location is the actual user location or a simulated one ?
Is there a public API to check if Developer Mode is enabled ?
Is there a way to tell the location comes from the GPX file ?
Even if the Developer mode is turned off it takes restarting the device to pick up the actual location again , so not sure if there is a different and better solution to this.
Thank you.
Apple provides sourceInformation on each CLLocation object returned. Checking the isSimulatedBySoftware parameter will give a boolean response.
Core Location sets isSimulatedBySoftware to true if the system generated the location using on-device software simulation. You can simulate locations by loading GPX files using the Xcode debugger. The default value is false.
The code below is a full functioning example that will turn the screen red if a simulated location is detected. I have confirmed that toggling simulated locations on/off in Xcode will make the screen turn from green to red and back again.
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// setup location monitoring
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// list of locations that are simulated
let simulatedLocations = locations.filter { $0.sourceInformation?.isSimulatedBySoftware == true }
view.backgroundColor = simulatedLocations.count > 0 ? .red : .green
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
guard manager.authorizationStatus == .authorizedAlways || manager.authorizationStatus == .authorizedWhenInUse else { return }
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}

Standard Location based iOS app not waking up after suspended IOS

I am developing a location based app which is supposed to fetch user location always.Im using standard location service. But the problem is that the app after keeping idle for some time in background will not fetch the coordinates even after we move to some other locations. As per apple documentation, when a new location arrives, app should wake up automatically, but that is not happening here. I'm sharing the code and using to fetch location and screenshot of my plist.
class SALocation: NSObject,CLLocationManagerDelegate
{
static let sharedInstance : SALocation = SALocation()
var locationManager : CLLocationManager!
var location : CLLocation!
var address : String!
var latitude : NSString?
var longitude : NSString?
var isAdderssLoaded : Bool = false
var locdictionary : NSMutableDictionary = NSMutableDictionary()
func startLocationManager()
{
if self.locationManager == nil
{
self.locationManager = CLLocationManager()
if CLLocationManager.locationServicesEnabled(){
print("location service enabled")
}
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
if ( Float (UIDevice.currentDevice().systemVersion) >= 9) {
if #available(iOS 9.0, *) {
self.locationManager.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
};
}
self.locationManager.startUpdatingLocation()
//self.locationManager.stopMonitoringSignificantLocationChanges()
}
else
{
self.locationManager.startUpdatingLocation()
}
}
// MARK: CLLocationManagerDelegate
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
UIAlertView(title:"Alert", message:error.description, delegate: nil, cancelButtonTitle:nil, otherButtonTitles:"Ok").show()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if locations.count > 0
{
self.location = locations[0]
/* storing date and location to plist
*/
let datenow = NSDate()
let dateformatternow = NSDateFormatter ()
dateformatternow.dateFormat = "yyyyMMdd HH:mm:ss"
let timenow:NSString = dateformatternow.stringFromDate(datenow)
let documetsdirectorypath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last
latitude = NSString(format: "%f",self.location.coordinate.latitude)
longitude = NSString (format: "%f",self.location.coordinate.longitude)
let latlong : NSString = NSString(format:"%#~%#",latitude!,longitude!)
NSUserDefaults.standardUserDefaults().setObject(latlong, forKey: "latlong")
let aFilePath = NSString(format: "%#/location.plist",documetsdirectorypath!)
locdictionary.setObject(latlong, forKey: timenow as String)
locdictionary.writeToFile(aFilePath as String, atomically: true)
///////////// ||storing date and location to plist code ends here||\\\\\\
// self.getAddressFromLocation(locations[0] )
// if (NSUserDefaults.standardUserDefaults().objectForKey(SettingAppRefresh) != nil)
// {
// if (NSUserDefaults.standardUserDefaults().objectForKey(SettingAppRefresh) as! NSString).isEqualToString(FalseString)
// {
// // self.locationManager.stopUpdatingLocation()
// }
// }
}
}
}
What i'm doing here is just get location and write it to a plist file. This works in foreground, background etc fine. But when i keep the app idle for 20 minutes, location is not fetched even if i move to some other locations as the app is suspended
Capabilities tab looks like this
To start location in background you must start background service from the following path
Click on your name -> Click on your app name (target) -> goto capabilities -> find the background mode -> enable the location update mode
I am not sure you started that or not because you not put any screenshot about this.
And also check that your user started background refresh in settings.refer below link for this.
Background App Refresh checking, enabling and disabling programatically for whole device and for each particular application in iOS 7
Update::
For location update in background used below link(objective c)
http://www.creativeworkline.com/2014/12/core-location-manager-ios-8-fetching-location-background/
Well, I don't know how you're getting location updates - significant-location change as example and how you exit from background.
I suggest checking if your app is truly in background mode - UIApplication.sharedApplication().applicationState as it can be terminated.
And I also suggest checking out Apple's Execution States for Apps. - especially for your possible use case Implementing Long-Running Tasks part. There is also a good tutorial at rayywenderlich.com called Background modes.
Please use
self.locationManager.requestAlwaysAuthorization()
and don't forget to update your Info.plist to define the NSLocationAlwaysUsageDescription key.

Xcode 7.1 iOS 9.1 CLLocationManager not always working?

When implementing a CLLocationManager I came across a problem. Sometimes, when launching the app in the iPhone Simulator, it just doesn't fetch the current locations. But when I restart the app or it then suddenly works after 1-3 restarts. Restarting Xcode works too... Here's my code:
private var lat = 0.0;
private var long = 0.0;
private var didFindLocation = false
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == .AuthorizedAlways) || (status == .AuthorizedWhenInUse) {
didFindLocation = false
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last! as CLLocation
self.lat = location.coordinate.latitude
self.long = location.coordinate.longitude
if !didFindLocation {
print("\(self.lat) - \(self.long)")
didFindLocation = true
locationManager.stopUpdatingLocation()
}
}
As you can see, I've created a bool didFindLocation that allows me to fetch the location only once. I've put some breakpoints to see what's going on, and when the location doesn't get fetched, func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) doesn't even get called.
Thanks
iOS Simulator do not contains sensors like accelerometer, gyrometer etc, so we should not expect to work them on simulators. Besides this, for fetching a location, perhaps, it is using your system's internet.
So for a fair result it is advisable to use a real device for such cases.
From coding perspective, you can check for locationServicesEnabled property of CCLocationManager to see if this service is enabled or not.
This can happen on a simulator, but do add this method so that you get an error message of why it´s not working even though it´s on a simulator
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error.localizedDescription)
}
When you check location in simulator then first you need to point your simulator at some location.
Why?
Because simulator is not real time device first you need to point it at some position or location.
How you can do it?
There are two ways to do it.
1) select the simulator -> Goto debug -> select location -> select apple location.
Your location method called
Noto : if you select custom location from menu and enter your custom location then you need to restart your simulator (some time multiple times)
2) Goto xcode -> run your project -> in debug panel you find one location icon -> click on it -> it display location name -> select any place name
Note : Not necessary it worked all the times.

Buttons in allow access to your location disabled

my viewController looks like following
import CoreLocation
class MyViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidAppear(animated: Bool) {
if #available(iOS 8.0, *) {
self.locationManager.requestWhenInUseAuthorization()
} else {
// Fallback on earlier versions
}
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
let lon = locationManager.location!.coordinate.longitude
let lat = locationManager.location!.coordinate.latitude
print("lat = \(lat) and long = \(lon)")
}
}
}
// MARK: - CLLocationManagerDelegate
extension MyViewController : CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("\(error.localizedDescription)")
}
}
When I execute my program, it prompts a message like
Allow application to access your location while you use the app?
But the Don't Allow and Allow buttons are disabled.
Can someone guide me on where I am going wrong and what I should be doing.
I can manually allow my application to access location services by going to settings. But I would like to know why the buttons are disabled and what should I do to enable it.
note: I have added NSLocationWhenInUseUsageDescription to my info.plist file.
Thanks.
Dt: 29Oct2015
EDIT:
Uninstalled the app and installed again and tried. Now I am able to access the buttons.
Also, I noticed that sometimes the screen goes unresponsive i.e., screen cannot take any input from user. I noticed it today with a textbox. I am not able to get the cursor to the text box.
Is it something to do with IOS update? is anyone else experiencing this type of weird behaviour. Is there any workaround for the same?
Any help is highly appreciated.
Thanks.

Request User's Current Location Watch OS2

I'm trying to get a fix on the user's current location tied into displaying some data on a Map. My code is as follows for my watch kit extension.
import Foundation
import CoreLocation
class InterfaceController: WKInterfaceController, CLLocationManagerDelegate {
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
dLog("Did get a location.")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
dLog("Did fail to retrieve a location.")
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print(status)
}
#IBAction func btnRequestLocation() {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
I've also made sure to include the CoreLocation framework under the WatchKit extension Target Linked Frameworks and Libraries. However, running this code on both the watch simulator and a real Apple watch paired with an iOS app, these delegate callbacks never get fired. I'm not sure if I'm missing a step somewhere that would be preventing me from getting the location. I've tried resetting the location and privacy and I even get the request that my app needs location permissions. I hit "Allow" but no location is ever sent to the Apple Watch extension. Any help would be appreciated.
EDIT:
I've also taken some additional steps for iOS 9.
Project -> iOS Target -> Background Modes
- Location Updates is checked
- iOS Target also includes the "Required Background Modes" key in the plist file.
Here is the property list for the iOS App.
UPDATE
Seems this might be a common problem isolated in watch OS2.
https://forums.developer.apple.com/thread/14828
You are requesting requestAlwaysAuthorization so you need to make sure you have enabled Locations background mode and also set allowsBackgroundLocationUpdates=TRUE (for iOS9).
However, I would recommend requesting requestWhenInUseAuthorization instead since you are just calling for a onetime location update via requestLocation().

Resources