I am trying to send the location update to the server in the background
App sometimes crashes in the background
Here is my locationmanager delegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lat = manager.location?.coordinate.latitude,
let long = manager.location?.coordinate.longitude {
glat = String(lat)
glong = String(long)
if UIApplication.shared.applicationState == .background {
self.updatebackloc(lat, long: long)
}
if UIApplication.shared.applicationState == .active {
self.updateloc(lat, long: long)
locationManager.stopUpdatingLocation()
let status = CLLocationManager.authorizationStatus()
if (status == CLAuthorizationStatus.authorizedAlways) {
locationManager.startMonitoringSignificantLocationChanges()
}
}
}
}
This is the updatebacklog function
func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
let userID = TegKeychain.get("userID")!
let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
if let json = response.result.value {
var success = 0
if let dictJSON = json as? [String: AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
if success == 1
{
}
}
}
}
case .failure(_):
return
}
}
}
didFinishLaunchingWithOptions
part
if let _ = launchOptions?[UIApplicationLaunchOptionsKey.location] {
startSignificationLocation()
}
startSignificationLocation
function triggered on didFinishLaunchingWithOptions
func startSignificationLocation() {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startMonitoringSignificantLocationChanges()
}
Here is the crash log
Crashed: com.apple.main-thread
0 0x10285b798 specialized AppDelegate.updatebackloc(Double, long : Double) -> () + 4339644312
1 0x10285b8e4 specialized AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift:396)
2 0x1028540c0 @objc AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift)
3 CoreLocation 0x1874f97bc (null) + 77412
4 CoreLocation 0x1874f901c (null) + 75460
5 CoreLocation 0x1874e16b4 (null) + 1004
6 CoreFoundation 0x180ea3590 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
7 CoreFoundation 0x180ea2e60 __CFRunLoopDoBlocks + 288
8 CoreFoundation 0x180ea10c8 __CFRunLoopRun + 2436
9 CoreFoundation 0x180dc0c58 CFRunLoopRunSpecific + 436
10 GraphicsServices 0x182c6cf84 GSEventRunModal + 100
11 UIKit 0x18a5195c4 UIApplicationMain + 236
12 0x1027fe524 main (InboxInterests.swift:30)
13 libdyld.dylib 0x1808e056c start + 4
Here is the code
code as text
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lat = manager.location?.coordinate.latitude,
let long = manager.location?.coordinate.longitude {
glat = String(lat)
glong = String(long)
if UIApplication.shared.applicationState == .background {
self.updatebackloc(lat, long: long)
}
if UIApplication.shared.applicationState == .active {
self.updateloc(lat, long: long)
locationManager.stopUpdatingLocation()
let status = CLLocationManager.authorizationStatus()
if (status == CLAuthorizationStatus.authorizedAlways) {
locationManager.startMonitoringSignificantLocationChanges()
}
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func updateloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
let userID = TegKeychain.get("userID")!
let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
Alamofire.request("https://xxxxx.com/ios/updateloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
if let json = response.result.value {
var success = 0
if let dictJSON = json as? [String: AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
if success == 1
{
}
}
}
}
case .failure(_):
return
}
}
}
func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
guard let userID = TegKeychain.get("userID") else {
return
}
let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
if let json = response.result.value {
var success = 0
if let dictJSON = json as? [String: AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
if success == 1
{
}
}
}
}
case .failure(_):
return
}
}
}
All apps that access location in the background will need to be approved or app updates may be blocked and your app may be removed from Google Play. About the process Beginning April 1, 2022, all apps must also post a privacy policy in the Google Play Console and within the app itself.
If you are still facing issues after examining your code paths and restricting usage to foreground purposes only, please review any third-party SDKs used in the app that may be accessing location in the background. Related content Learn more about declaring permissionsin Play Console
On Android 10 (API level 29) and higher, you must declare the ACCESS_BACKGROUND_LOCATION permission in your app's manifest in order to request background location access at runtime. On earlier versions of Android, when your app receives foreground location access, it automatically receives background location access as well.
If your app requests access to location in the background but it’s not critical to the app’s core functionality, you must remove it from your app and/or implement location access in the foreground, such as when your app’s activity is visible to users. You can find details on policy compliant implementation below.
I highly suspect you are trying to force-unwrap a nil
value in this line:
let userID = TegKeychain.get("userID")!
To figure out if I am correct, try if this fixes the crash (but still does not do what you want obviously):
guard let userID = TegKeychain.get("userID") else {
return
}
Assuming TegKeychain.get("userID")
is trying to get a value for a key from the system KeyChain, this value has most probably been written to the KeyChain with a non-suitable accessibility. Thus you are not allowed to access it in background and nil
is returned.
To fix this, set a value that fits your needs for key kSecAttrAccessible
when saving the credential to the KeyChain.
kSecAttrAccessibleAfterFirstUnlock is recommended by Apple and probably fits your needs.
In code:
let userIdToSave = "secretString"
guard let secretAsData = userIdToSave.data(using: String.Encoding.utf8) else {
return
}
let keyChainKey = "userID"
let query = [
kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock as String,
kSecAttrService as String: yourService,
kSecAttrAccount as String: keyChainKey,
kSecValueData as String: secretAsData] as [String : Any]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
For details check Apples Documentation.
Edit:
According to your statements in the comments my guess was wrong.
My next guess is that the OS is killing you in the middle of the network process. You can avoid that by asking the OS for more time.
To do so, start a backgroundTask.
In code that would look something like this:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var bgTask = UIBackgroundTaskInvalid
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let _ = launchOptions?[UIApplicationLaunchOptionsKey.location] {
// app is launched in backgrond due to location change
bgTask = UIApplication.shared.beginBackgroundTask(expirationHandler: { // Start background task
UIApplication.shared.endBackgroundTask(bgTask)
bgTask = UIBackgroundTaskInvalid
})
startSignificationLocation()
}
return true
}
// ... your other methods
func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
guard let userID = TegKeychain.get("userID") else {
return
}
let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
defer {
// Tell the OS that you are done
UIApplication.shared.endBackgroundTask(bgTask)
bgTask = UIBackgroundTaskInvalid
}
switch response.result {
case .success:
if let json = response.result.value {
var success = 0
if let dictJSON = json as? [String: AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
if success == 1
{
}
}
}
}
case .failure(_):
return
}
}
}
}
Also do not forget to setup required Capatibilities as mentions in other answers:
You also should do more debugging. Read this about how to simulate movement in Xcode.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With