收到背景位置时应用程序崩溃

时间:2018-03-02 09:50:44

标签: ios swift crash cllocationmanager

我正在尝试在后台将位置更新发送到服务器

App 有时在后台崩溃

这是我的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()
                    }
                }
            }
            }

这是updatebacklog函数

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部分

if let _ = launchOptions?[UIApplicationLaunchOptionsKey.location] {
                    startSignificationLocation()
                }
startSignificationLocation

上触发了

didFinishLaunchingWithOptions个功能

func startSignificationLocation() {
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startMonitoringSignificantLocationChanges()
    }

这是崩溃日志

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

enter image description here

这是代码

enter image description here

代码为文本

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
            }
        }
    }

4 个答案:

答案 0 :(得分:10)

我非常怀疑你是在试图强行打开这行中的nil值:

let userID = TegKeychain.get("userID")!

要弄清楚我是否正确,请尝试修复崩溃(但仍然没有做你想要的事情):

guard let userID = TegKeychain.get("userID") else {
    return
}

假设TegKeychain.get("userID")正在尝试从系统KeyChain获取密钥的值,则该值很可能已被写入具有不合适可访问性的KeyChain。因此,您不能在后台访问它并返回nil

要解决此问题,请在将凭据保存到KeyChain时设置一个符合您对密钥kSecAttrAccessible的需求的值。

Apple推荐

kSecAttrAccessibleAfterFirstUnlock,可能符合您的需求。

在代码中:

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)

有关详情,请查看Apples Documentation

修改

根据您在评论中的陈述,我猜错了。

我的下一个猜测是,操作系统正在网络进程中杀死你。您可以通过向操作系统询问更多时间来避免这种情况。

为此,请启动backgroundTask。

在代码中看起来像这样:

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
            }
        }
    }
}

另外,请不要忘记在其他答案中提及所需的Capatibilities:

enter image description here

您还应该进行更多调试。阅读this,了解如何在Xcode中模拟移动。

答案 1 :(得分:2)

我猜您需要使用后台配置配置Alamofire会话管理器,以便在后台执行请求。然后,为了使请求使用正确配置的会话管理器。

e.g。

let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")
let sessionManager = Alamofire.SessionManager(configuration: configuration)

答案 2 :(得分:1)

您是否为背景模式启用了位置服务?如果是,那么当你输入它实际上为我工作的背景时尝试重新初始化位置管理器,但最好的做法是在用户成为活动同步未同步记录到服务器时保存数据库中的坐标。 enter image description here

答案 3 :(得分:1)

您需要将背景位置调用包装在UIBackgroundTask中。

var bgTask: UIBackgroundTaskIdentifier?
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.

    bgTask = application.beginBackgroundTask(withName: "bgLocation", expirationHandler: {
        if let task = self.bgTask {
            application.endBackgroundTask(task)
            self.bgTask = UIBackgroundTaskInvalid
        }
    })
}

然后在调用updatebackloc函数时使用正确的后台队列并结束后台任务。

类似的东西:

func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
    let userID = TegKeychain.get("userID")!
    let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
    DispatchQueue.global().async {
        Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in

            //response handling here...
            //....
            if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
                if let task = appDelegate.bgTask {
                    UIApplication.shared.endBackgroundTask(task)
                    appDelegate.bgTask = UIBackgroundTaskInvalid
                }
            }
        }
    }
}