I am trying to send location update to server 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 libswiftCore.dylib
0x10294b1ac specialized fatalErrorMessage(:_:file:line:flags:) + 120 1 Jemiyet 0x100e20824 AppDelegate.updatebackloc(Double, long : Double) -> () (AppDelegate.swift:436) 2 Jemiyet 0x100e1fa78 AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift:396) 3 Jemiyet
0x100e1fd7c @objc AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift) 4 CoreLocation 0x1891ad7bc (null) + 77412 5 CoreLocation 0x1891ad01c (null) + 75460 6 CoreLocation 0x1891956b4 (null) + 1004 7 CoreFoundation 0x182b57590 CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK + 20 8 CoreFoundation 0x182b56e60 __CFRunLoopDoBlocks + 288 9 CoreFoundation
0x182b550c8 __CFRunLoopRun + 2436 10 CoreFoundation
0x182a74c58 CFRunLoopRunSpecific + 436 11 GraphicsServices
0x184920f84 GSEventRunModal + 100 12 UIKit
0x18c1cd5c4 UIApplicationMain + 236 13 Jemiyet
0x100e21598 main (AppDelegate.swift:30) 14 libdyld.dylib
0x18259456c start + 4
3 Answers
Answers 1
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.
Answers 2
I guess you need to configure an Alamofire Session Manager with the background configuration in order to perform a request in the background. Then in order to make the request use the properly configured session manager.
e.g.
let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background") let sessionManager = Alamofire.SessionManager(configuration: configuration)